@blade-hq/agent-react 2608.0.2 → 2608.0.3
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/README.md +131 -1
- package/dist/components/AgentChat.d.ts +18 -0
- package/dist/components/ChatSurface.d.ts +59 -0
- package/dist/components/ChatView.d.ts +19 -39
- package/dist/components/LlmAdvancedSettings.d.ts +40 -0
- package/dist/components/LlmChat.d.ts +29 -0
- package/dist/components/ReplayBar.d.ts +13 -0
- package/dist/components/ReplayMismatchPrompt.d.ts +8 -0
- package/dist/embed/entry.d.ts +2 -0
- package/dist/hooks/use-llm-chat.d.ts +57 -0
- package/dist/hooks/use-replay.d.ts +50 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1134 -279
- package/dist/index.js.map +1 -1
- package/dist/style.full.css +1 -1
- package/package.json +2 -2
- package/public-api.md +270 -4
package/dist/index.js
CHANGED
|
@@ -99,7 +99,452 @@ function useAgentSession(sessionId, options = {}) {
|
|
|
99
99
|
return { session, state, error };
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
// src/
|
|
102
|
+
// src/hooks/use-replay.ts
|
|
103
|
+
import { DEFAULT_REPLAY_SPEED } from "@blade-hq/agent-client";
|
|
104
|
+
import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
|
|
105
|
+
var replayCreating = /* @__PURE__ */ new Set();
|
|
106
|
+
function useReplay(session) {
|
|
107
|
+
const client = useBladeClient();
|
|
108
|
+
const sessionId = session?.sessionId ?? null;
|
|
109
|
+
const [replay, setReplay] = useState2(null);
|
|
110
|
+
const [viewerRole, setViewerRole] = useState2(null);
|
|
111
|
+
const [sourceRunning, setSourceRunning] = useState2(false);
|
|
112
|
+
const [mismatch, setMismatch] = useState2(null);
|
|
113
|
+
const [preview, setPreview] = useState2({ status: "loading" });
|
|
114
|
+
const [isStarting, setIsStarting] = useState2(false);
|
|
115
|
+
const [error, setError] = useState2(null);
|
|
116
|
+
const [draftSpeed, setDraftSpeed] = useState2(DEFAULT_REPLAY_SPEED);
|
|
117
|
+
useEffect2(() => {
|
|
118
|
+
if (!session) {
|
|
119
|
+
setReplay(null);
|
|
120
|
+
setViewerRole(null);
|
|
121
|
+
setSourceRunning(false);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const pull = () => {
|
|
125
|
+
const state = session.getState();
|
|
126
|
+
setReplay(state.replay);
|
|
127
|
+
setViewerRole(state.viewerRole);
|
|
128
|
+
setSourceRunning(state.isStreaming || state.status === "running");
|
|
129
|
+
};
|
|
130
|
+
pull();
|
|
131
|
+
return session.subscribe(pull);
|
|
132
|
+
}, [session]);
|
|
133
|
+
const [lastSessionId, setLastSessionId] = useState2(sessionId);
|
|
134
|
+
if (sessionId !== lastSessionId) {
|
|
135
|
+
setLastSessionId(sessionId);
|
|
136
|
+
setMismatch(null);
|
|
137
|
+
setError(null);
|
|
138
|
+
setDraftSpeed(DEFAULT_REPLAY_SPEED);
|
|
139
|
+
}
|
|
140
|
+
useEffect2(() => {
|
|
141
|
+
setPreview({ status: "loading" });
|
|
142
|
+
if (!sessionId) return;
|
|
143
|
+
let alive = true;
|
|
144
|
+
client.sessions.getReplayPreview(sessionId).then((result) => {
|
|
145
|
+
if (!alive) return;
|
|
146
|
+
setPreview({
|
|
147
|
+
status: "ready",
|
|
148
|
+
supported: result.supported,
|
|
149
|
+
reason: result.reason ?? null
|
|
150
|
+
});
|
|
151
|
+
}).catch(() => {
|
|
152
|
+
if (alive) setPreview({ status: "ready", supported: true, reason: null });
|
|
153
|
+
});
|
|
154
|
+
return () => {
|
|
155
|
+
alive = false;
|
|
156
|
+
};
|
|
157
|
+
}, [client, sessionId, sourceRunning]);
|
|
158
|
+
useEffect2(() => {
|
|
159
|
+
if (!session) return;
|
|
160
|
+
return session.on("replayMismatch", ({ actualMessage, expectedMessage, respond }) => {
|
|
161
|
+
setMismatch({
|
|
162
|
+
actualMessage: actualMessage ?? "",
|
|
163
|
+
expectedMessage: expectedMessage ?? "",
|
|
164
|
+
resolve: (decision) => {
|
|
165
|
+
setMismatch(null);
|
|
166
|
+
respond(decision);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}, [session]);
|
|
171
|
+
const isReplay = replay?.isReplay ?? false;
|
|
172
|
+
const setSpeed = useCallback(
|
|
173
|
+
async (next) => {
|
|
174
|
+
setDraftSpeed(next);
|
|
175
|
+
if (!session) return;
|
|
176
|
+
setError(null);
|
|
177
|
+
try {
|
|
178
|
+
await session.setReplaySpeed(next);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
[session]
|
|
184
|
+
);
|
|
185
|
+
const exitToAutonomous = useCallback(async () => {
|
|
186
|
+
if (!session) return;
|
|
187
|
+
setError(null);
|
|
188
|
+
const pending = mismatch;
|
|
189
|
+
if (pending) {
|
|
190
|
+
pending.resolve("continue_replay");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
await session.exitReplay();
|
|
195
|
+
} catch (err) {
|
|
196
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
197
|
+
}
|
|
198
|
+
}, [session, mismatch]);
|
|
199
|
+
const startReplay = useCallback(
|
|
200
|
+
async (speed) => {
|
|
201
|
+
if (!sessionId) throw new Error("\u5F53\u524D\u6CA1\u6709\u53EF\u56DE\u653E\u7684\u4F1A\u8BDD");
|
|
202
|
+
if (replayCreating.has(sessionId)) throw new Error("\u6B63\u5728\u521B\u5EFA\u56DE\u653E\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u5019");
|
|
203
|
+
replayCreating.add(sessionId);
|
|
204
|
+
setIsStarting(true);
|
|
205
|
+
setError(null);
|
|
206
|
+
try {
|
|
207
|
+
const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed);
|
|
208
|
+
return result.session_id;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const failure = err instanceof Error ? err : new Error(String(err));
|
|
211
|
+
setError(failure);
|
|
212
|
+
throw failure;
|
|
213
|
+
} finally {
|
|
214
|
+
replayCreating.delete(sessionId);
|
|
215
|
+
setIsStarting(false);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
[client, sessionId, draftSpeed]
|
|
219
|
+
);
|
|
220
|
+
return {
|
|
221
|
+
isReplay,
|
|
222
|
+
speed: isReplay ? replay?.speed ?? 1 : draftSpeed,
|
|
223
|
+
setSpeed,
|
|
224
|
+
exitToAutonomous,
|
|
225
|
+
mismatch,
|
|
226
|
+
canControl: viewerRole !== "viewer",
|
|
227
|
+
canReplay: Boolean(sessionId) && preview.status === "ready" && preview.supported && viewerRole !== "viewer",
|
|
228
|
+
unsupportedReason: preview.status === "ready" && !preview.supported ? preview.reason : null,
|
|
229
|
+
startReplay,
|
|
230
|
+
isStarting,
|
|
231
|
+
error
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/hooks/use-llm-chat.ts
|
|
236
|
+
import { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
|
|
237
|
+
var DEFAULT_HISTORY_TURNS = 12;
|
|
238
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 4;
|
|
239
|
+
var syntheticIdSeq = 0;
|
|
240
|
+
function useLlmChat(options) {
|
|
241
|
+
const [history, setHistory] = useState3([]);
|
|
242
|
+
const [streamingText, setStreamingText] = useState3(null);
|
|
243
|
+
const [streamingCalls, setStreamingCalls] = useState3([]);
|
|
244
|
+
const [failedToolIds, setFailedToolIds] = useState3([]);
|
|
245
|
+
const [error, setError] = useState3(null);
|
|
246
|
+
const [isStreaming, setIsStreaming] = useState3(false);
|
|
247
|
+
const abortRef = useRef2(null);
|
|
248
|
+
const generationRef = useRef2(0);
|
|
249
|
+
const historyRef = useRef2([]);
|
|
250
|
+
const optionsRef = useRef2(options);
|
|
251
|
+
optionsRef.current = options;
|
|
252
|
+
historyRef.current = history;
|
|
253
|
+
const stop = useCallback2(() => {
|
|
254
|
+
abortRef.current?.abort();
|
|
255
|
+
abortRef.current = null;
|
|
256
|
+
}, []);
|
|
257
|
+
const reset = useCallback2(() => {
|
|
258
|
+
generationRef.current += 1;
|
|
259
|
+
stop();
|
|
260
|
+
setHistory([]);
|
|
261
|
+
setStreamingText(null);
|
|
262
|
+
setStreamingCalls([]);
|
|
263
|
+
setFailedToolIds([]);
|
|
264
|
+
setError(null);
|
|
265
|
+
setIsStreaming(false);
|
|
266
|
+
}, [stop]);
|
|
267
|
+
const send = useCallback2(async (text) => {
|
|
268
|
+
const content = text.trim();
|
|
269
|
+
if (!content || abortRef.current) return false;
|
|
270
|
+
const opts = optionsRef.current;
|
|
271
|
+
const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
272
|
+
const commit = (message) => {
|
|
273
|
+
historyRef.current = [...historyRef.current, message];
|
|
274
|
+
setHistory(historyRef.current);
|
|
275
|
+
};
|
|
276
|
+
setError(null);
|
|
277
|
+
setIsStreaming(true);
|
|
278
|
+
setStreamingText("");
|
|
279
|
+
setStreamingCalls([]);
|
|
280
|
+
commit({ role: "user", content });
|
|
281
|
+
let partial = "";
|
|
282
|
+
const controller = new AbortController();
|
|
283
|
+
abortRef.current = controller;
|
|
284
|
+
const generation = generationRef.current;
|
|
285
|
+
try {
|
|
286
|
+
for (let round = 0; ; round += 1) {
|
|
287
|
+
const result = await streamOnce(opts, historyRef.current, controller.signal, {
|
|
288
|
+
onText: (chunk) => {
|
|
289
|
+
partial += chunk;
|
|
290
|
+
setStreamingText((prev) => `${prev ?? ""}${chunk}`);
|
|
291
|
+
},
|
|
292
|
+
onToolCalls: (calls) => setStreamingCalls(calls)
|
|
293
|
+
});
|
|
294
|
+
const assistant = {
|
|
295
|
+
role: "assistant",
|
|
296
|
+
content: result.content,
|
|
297
|
+
...result.toolCalls.length ? { tool_calls: result.toolCalls } : {}
|
|
298
|
+
};
|
|
299
|
+
setStreamingText(null);
|
|
300
|
+
setStreamingCalls([]);
|
|
301
|
+
commit(assistant);
|
|
302
|
+
if (!result.toolCalls.length) return true;
|
|
303
|
+
const bail = (reason) => {
|
|
304
|
+
for (const call of result.toolCalls) {
|
|
305
|
+
commit({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ error: reason }) });
|
|
306
|
+
}
|
|
307
|
+
setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)]);
|
|
308
|
+
};
|
|
309
|
+
if (!opts.onToolCall) {
|
|
310
|
+
bail("\u8FD9\u4E2A\u5E94\u7528\u6CA1\u6709\u63D0\u4F9B\u5DE5\u5177\u6267\u884C\u5165\u53E3");
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
if (round >= maxRounds) {
|
|
314
|
+
bail(`\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE\u4E0A\u9650 ${maxRounds} \u8F6E\uFF0C\u6CA1\u6709\u6267\u884C`);
|
|
315
|
+
setError(`\u5DE5\u5177\u8C03\u7528\u8D85\u8FC7 ${maxRounds} \u8F6E\u4ECD\u672A\u7ED9\u51FA\u7ED3\u8BBA\uFF0C\u5DF2\u505C\u4E0B\u3002`);
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
for (const call of result.toolCalls) {
|
|
319
|
+
let output;
|
|
320
|
+
try {
|
|
321
|
+
const value = await opts.onToolCall({
|
|
322
|
+
id: call.id,
|
|
323
|
+
name: call.function.name,
|
|
324
|
+
arguments: call.function.arguments
|
|
325
|
+
});
|
|
326
|
+
output = typeof value === "string" ? value : JSON.stringify(value ?? null);
|
|
327
|
+
} catch (err) {
|
|
328
|
+
output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
|
|
329
|
+
setFailedToolIds((prev) => [...prev, call.id]);
|
|
330
|
+
}
|
|
331
|
+
commit({ role: "tool", tool_call_id: call.id, content: output });
|
|
332
|
+
}
|
|
333
|
+
setStreamingText("");
|
|
334
|
+
}
|
|
335
|
+
} catch (err) {
|
|
336
|
+
setStreamingText(null);
|
|
337
|
+
setStreamingCalls([]);
|
|
338
|
+
if (controller.signal.aborted) {
|
|
339
|
+
if (generation !== generationRef.current) return false;
|
|
340
|
+
commit({ role: "assistant", content: partial ? `${partial}\uFF08\u5DF2\u505C\u6B62\uFF09` : "\uFF08\u5DF2\u505C\u6B62\uFF09" });
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
if (partial && generation === generationRef.current) {
|
|
344
|
+
commit({ role: "assistant", content: partial });
|
|
345
|
+
}
|
|
346
|
+
setError(err instanceof Error && err.message ? err.message : "\u5BF9\u8BDD\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
347
|
+
return false;
|
|
348
|
+
} finally {
|
|
349
|
+
if (abortRef.current === controller) abortRef.current = null;
|
|
350
|
+
setIsStreaming(false);
|
|
351
|
+
}
|
|
352
|
+
}, []);
|
|
353
|
+
const messages = useMemo2(
|
|
354
|
+
() => toChatMessages(history, streamingText, streamingCalls, failedToolIds),
|
|
355
|
+
[history, streamingText, streamingCalls, failedToolIds]
|
|
356
|
+
);
|
|
357
|
+
return { messages, isStreaming, error, send, stop, reset };
|
|
358
|
+
}
|
|
359
|
+
async function streamOnce(options, history, signal, handlers) {
|
|
360
|
+
const {
|
|
361
|
+
baseURL,
|
|
362
|
+
model,
|
|
363
|
+
apiKey,
|
|
364
|
+
headers,
|
|
365
|
+
system,
|
|
366
|
+
historyTurns = DEFAULT_HISTORY_TURNS,
|
|
367
|
+
temperature,
|
|
368
|
+
extraBody,
|
|
369
|
+
tools,
|
|
370
|
+
fetchImpl
|
|
371
|
+
} = options;
|
|
372
|
+
const trimmed = trimHistory(history, historyTurns);
|
|
373
|
+
const payload = {
|
|
374
|
+
model,
|
|
375
|
+
stream: true,
|
|
376
|
+
messages: system ? [{ role: "system", content: system }, ...trimmed] : trimmed,
|
|
377
|
+
...tools?.length ? { tools } : {},
|
|
378
|
+
...temperature === void 0 ? {} : { temperature },
|
|
379
|
+
...extraBody
|
|
380
|
+
};
|
|
381
|
+
const doFetch = fetchImpl ?? globalThis.fetch;
|
|
382
|
+
const url = `${baseURL.replace(/\/$/, "")}/chat/completions`;
|
|
383
|
+
let response;
|
|
384
|
+
try {
|
|
385
|
+
response = await doFetch(url, {
|
|
386
|
+
method: "POST",
|
|
387
|
+
headers: {
|
|
388
|
+
"Content-Type": "application/json",
|
|
389
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
|
|
390
|
+
...headers
|
|
391
|
+
},
|
|
392
|
+
body: JSON.stringify(payload),
|
|
393
|
+
signal
|
|
394
|
+
});
|
|
395
|
+
} catch (err) {
|
|
396
|
+
if (signal.aborted) throw err;
|
|
397
|
+
throw new Error(`\u8FDE\u4E0D\u4E0A\u6A21\u578B\u670D\u52A1 ${url}\uFF1A${err instanceof Error ? err.message : String(err)}`);
|
|
398
|
+
}
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
throw new Error(await describeHttpError(response));
|
|
401
|
+
}
|
|
402
|
+
if (!response.body) {
|
|
403
|
+
throw new Error("\u6A21\u578B\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6D41\u5F0F\u54CD\u5E94");
|
|
404
|
+
}
|
|
405
|
+
let content = "";
|
|
406
|
+
const calls = [];
|
|
407
|
+
for await (const chunk of readSse(response.body, signal)) {
|
|
408
|
+
if (chunk.error) {
|
|
409
|
+
throw new Error(chunk.error);
|
|
410
|
+
}
|
|
411
|
+
if (chunk.text) {
|
|
412
|
+
content += chunk.text;
|
|
413
|
+
handlers.onText(chunk.text);
|
|
414
|
+
}
|
|
415
|
+
if (chunk.toolCallDeltas) {
|
|
416
|
+
for (const delta of chunk.toolCallDeltas) {
|
|
417
|
+
const index = delta.index ?? 0;
|
|
418
|
+
const existing = calls[index] ?? { id: "", type: "function", function: { name: "", arguments: "" } };
|
|
419
|
+
calls[index] = {
|
|
420
|
+
id: delta.id ?? existing.id,
|
|
421
|
+
type: "function",
|
|
422
|
+
function: {
|
|
423
|
+
name: delta.function?.name ?? existing.function.name,
|
|
424
|
+
arguments: `${existing.function.arguments}${delta.function?.arguments ?? ""}`
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const normalized = calls.filter(Boolean).filter((call) => call.function.name).map((call) => {
|
|
432
|
+
if (call.id) return call;
|
|
433
|
+
syntheticIdSeq += 1;
|
|
434
|
+
return { ...call, id: `call_auto_${syntheticIdSeq}` };
|
|
435
|
+
});
|
|
436
|
+
return { content, toolCalls: normalized };
|
|
437
|
+
}
|
|
438
|
+
function trimHistory(history, historyTurns) {
|
|
439
|
+
const userIndexes = history.reduce((acc, msg, i) => {
|
|
440
|
+
if (msg.role === "user") acc.push(i);
|
|
441
|
+
return acc;
|
|
442
|
+
}, []);
|
|
443
|
+
if (userIndexes.length <= historyTurns) return history;
|
|
444
|
+
return history.slice(userIndexes[userIndexes.length - historyTurns]);
|
|
445
|
+
}
|
|
446
|
+
async function describeHttpError(response) {
|
|
447
|
+
const raw = await response.text().catch(() => "");
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(raw);
|
|
450
|
+
const message = typeof parsed.error === "string" ? parsed.error : parsed.error?.message ?? (typeof parsed.detail === "string" ? parsed.detail : void 0);
|
|
451
|
+
if (message) return `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}\uFF1A${message}`;
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
const snippet = raw.trim().slice(0, 120);
|
|
455
|
+
return snippet ? `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}\uFF1A${snippet}` : `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}`;
|
|
456
|
+
}
|
|
457
|
+
async function* readSse(stream, signal) {
|
|
458
|
+
const reader = stream.getReader();
|
|
459
|
+
const decoder = new TextDecoder();
|
|
460
|
+
let buffer = "";
|
|
461
|
+
try {
|
|
462
|
+
while (!signal.aborted) {
|
|
463
|
+
const { done, value } = await reader.read();
|
|
464
|
+
if (done) {
|
|
465
|
+
const tail = parseSse(buffer);
|
|
466
|
+
if (tail && tail !== "done") yield tail;
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\r\n/g, "\n");
|
|
470
|
+
let boundary = buffer.indexOf("\n\n");
|
|
471
|
+
while (boundary !== -1) {
|
|
472
|
+
const chunk = parseSse(buffer.slice(0, boundary));
|
|
473
|
+
buffer = buffer.slice(boundary + 2);
|
|
474
|
+
if (chunk === "done") return;
|
|
475
|
+
if (chunk) yield chunk;
|
|
476
|
+
boundary = buffer.indexOf("\n\n");
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} finally {
|
|
480
|
+
reader.cancel().catch(() => {
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function parseSse(raw) {
|
|
485
|
+
const dataLines = [];
|
|
486
|
+
for (const line of raw.split("\n")) {
|
|
487
|
+
if (line.startsWith("data:")) dataLines.push(line.slice(5).trim());
|
|
488
|
+
}
|
|
489
|
+
if (!dataLines.length) return null;
|
|
490
|
+
const data = dataLines.join("\n");
|
|
491
|
+
if (data === "[DONE]") return "done";
|
|
492
|
+
let parsed;
|
|
493
|
+
try {
|
|
494
|
+
parsed = JSON.parse(data);
|
|
495
|
+
} catch {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
if (parsed.error) {
|
|
499
|
+
const message = typeof parsed.error === "string" ? parsed.error : parsed.error.message;
|
|
500
|
+
return { error: message || "\u6A21\u578B\u670D\u52A1\u8FD4\u56DE\u4E86\u9519\u8BEF" };
|
|
501
|
+
}
|
|
502
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
503
|
+
if (!delta) return null;
|
|
504
|
+
return { text: delta.content, toolCallDeltas: delta.tool_calls };
|
|
505
|
+
}
|
|
506
|
+
function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
507
|
+
const results = /* @__PURE__ */ new Map();
|
|
508
|
+
for (const msg of history) {
|
|
509
|
+
if (msg.role === "tool") results.set(msg.tool_call_id, msg.content);
|
|
510
|
+
}
|
|
511
|
+
const messages = [];
|
|
512
|
+
for (const msg of history) {
|
|
513
|
+
if (msg.role === "tool") continue;
|
|
514
|
+
if (msg.role === "user") {
|
|
515
|
+
messages.push({ role: "user", content: msg.content, status: "completed" });
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
messages.push({
|
|
519
|
+
role: "assistant",
|
|
520
|
+
content: msg.content,
|
|
521
|
+
status: "completed",
|
|
522
|
+
...msg.tool_calls?.length ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
if (streamingText !== null) {
|
|
526
|
+
messages.push({
|
|
527
|
+
role: "assistant",
|
|
528
|
+
content: streamingText,
|
|
529
|
+
status: "streaming",
|
|
530
|
+
...streamingCalls.length ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return messages;
|
|
534
|
+
}
|
|
535
|
+
function toToolCallInfo(call, results, failedToolIds) {
|
|
536
|
+
const result = results.get(call.id);
|
|
537
|
+
const failed = failedToolIds.includes(call.id);
|
|
538
|
+
return {
|
|
539
|
+
id: call.id,
|
|
540
|
+
name: call.function.name,
|
|
541
|
+
arguments: call.function.arguments,
|
|
542
|
+
...result === void 0 ? {} : { result },
|
|
543
|
+
status: result === void 0 ? "pending" : failed ? "error" : "done"
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/components/AgentChat.tsx
|
|
103
548
|
import { BladeApiError } from "@blade-hq/agent-client";
|
|
104
549
|
|
|
105
550
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
@@ -376,6 +821,19 @@ var Music = createLucideIcon("Music", [
|
|
|
376
821
|
["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
|
|
377
822
|
]);
|
|
378
823
|
|
|
824
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
|
|
825
|
+
var Play = createLucideIcon("Play", [
|
|
826
|
+
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
827
|
+
]);
|
|
828
|
+
|
|
829
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
|
|
830
|
+
var Settings2 = createLucideIcon("Settings2", [
|
|
831
|
+
["path", { d: "M20 7h-9", key: "3s1dr2" }],
|
|
832
|
+
["path", { d: "M14 17H5", key: "gfn3mx" }],
|
|
833
|
+
["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
|
|
834
|
+
["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
|
|
835
|
+
]);
|
|
836
|
+
|
|
379
837
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
|
|
380
838
|
var Square = createLucideIcon("Square", [
|
|
381
839
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
@@ -400,8 +858,8 @@ var X = createLucideIcon("X", [
|
|
|
400
858
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
401
859
|
]);
|
|
402
860
|
|
|
403
|
-
// src/components/
|
|
404
|
-
import { useEffect as
|
|
861
|
+
// src/components/AgentChat.tsx
|
|
862
|
+
import { useEffect as useEffect7, useState as useState12 } from "react";
|
|
405
863
|
|
|
406
864
|
// src/lib/utils.ts
|
|
407
865
|
function cn(...inputs) {
|
|
@@ -416,8 +874,120 @@ async function copyToClipboard(text) {
|
|
|
416
874
|
}
|
|
417
875
|
}
|
|
418
876
|
|
|
419
|
-
// src/components/
|
|
877
|
+
// src/components/ReplayBar.tsx
|
|
420
878
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
879
|
+
var SPEED_OPTIONS = [1, 2, 5];
|
|
880
|
+
function ReplayBar({
|
|
881
|
+
isReplay,
|
|
882
|
+
speed,
|
|
883
|
+
onSpeedChange,
|
|
884
|
+
onExit,
|
|
885
|
+
canControl = true,
|
|
886
|
+
className
|
|
887
|
+
}) {
|
|
888
|
+
if (!isReplay) return null;
|
|
889
|
+
return /* @__PURE__ */ jsxs(
|
|
890
|
+
"div",
|
|
891
|
+
{
|
|
892
|
+
className: cn(
|
|
893
|
+
"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs",
|
|
894
|
+
className
|
|
895
|
+
),
|
|
896
|
+
children: [
|
|
897
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]", children: [
|
|
898
|
+
/* @__PURE__ */ jsx2(Play, { size: 13 }),
|
|
899
|
+
"\u56DE\u653E\u6A21\u5F0F"
|
|
900
|
+
] }),
|
|
901
|
+
/* @__PURE__ */ jsx2("span", { className: "text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u91CD\u73B0\u4E4B\u524D\u7684\u5BF9\u8BDD" }),
|
|
902
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
|
|
903
|
+
/* @__PURE__ */ jsx2("div", { className: "flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5", children: SPEED_OPTIONS.map((option) => /* @__PURE__ */ jsxs(
|
|
904
|
+
"button",
|
|
905
|
+
{
|
|
906
|
+
type: "button",
|
|
907
|
+
onClick: () => onSpeedChange(option),
|
|
908
|
+
"aria-pressed": speed === option,
|
|
909
|
+
disabled: !canControl,
|
|
910
|
+
className: cn(
|
|
911
|
+
"h-6 rounded px-2 font-medium transition-colors",
|
|
912
|
+
speed === option ? "bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
|
|
913
|
+
!canControl && "cursor-not-allowed opacity-50"
|
|
914
|
+
),
|
|
915
|
+
children: [
|
|
916
|
+
option,
|
|
917
|
+
"x"
|
|
918
|
+
]
|
|
919
|
+
},
|
|
920
|
+
option
|
|
921
|
+
)) }),
|
|
922
|
+
/* @__PURE__ */ jsx2(
|
|
923
|
+
"button",
|
|
924
|
+
{
|
|
925
|
+
type: "button",
|
|
926
|
+
onClick: onExit,
|
|
927
|
+
disabled: !canControl,
|
|
928
|
+
className: cn(
|
|
929
|
+
"h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
|
|
930
|
+
!canControl && "cursor-not-allowed opacity-50 hover:bg-transparent"
|
|
931
|
+
),
|
|
932
|
+
children: "\u9000\u51FA\u56DE\u653E"
|
|
933
|
+
}
|
|
934
|
+
)
|
|
935
|
+
] })
|
|
936
|
+
]
|
|
937
|
+
}
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// src/components/ReplayMismatchPrompt.tsx
|
|
942
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
943
|
+
function ReplayMismatchPrompt({ mismatch, className }) {
|
|
944
|
+
if (!mismatch) return null;
|
|
945
|
+
return /* @__PURE__ */ jsxs2(
|
|
946
|
+
"div",
|
|
947
|
+
{
|
|
948
|
+
className: cn(
|
|
949
|
+
"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm",
|
|
950
|
+
className
|
|
951
|
+
),
|
|
952
|
+
children: [
|
|
953
|
+
/* @__PURE__ */ jsx3("div", { className: "font-medium text-[hsl(var(--foreground))]", children: "\u8FD9\u53E5\u8BDD\u548C\u4E4B\u524D\u5F55\u5236\u7684\u4E0D\u4E00\u6837" }),
|
|
954
|
+
/* @__PURE__ */ jsxs2("dl", { className: "mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
955
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
956
|
+
/* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u5F55\u5236\u7684\u662F" }),
|
|
957
|
+
/* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.expectedMessage || "\uFF08\u7A7A\uFF09" })
|
|
958
|
+
] }),
|
|
959
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
960
|
+
/* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u4F60\u8F93\u5165\u7684" }),
|
|
961
|
+
/* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.actualMessage || "\uFF08\u7A7A\uFF09" })
|
|
962
|
+
] })
|
|
963
|
+
] }),
|
|
964
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
965
|
+
/* @__PURE__ */ jsx3(
|
|
966
|
+
"button",
|
|
967
|
+
{
|
|
968
|
+
type: "button",
|
|
969
|
+
onClick: () => mismatch.resolve("keep_replay"),
|
|
970
|
+
className: "h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90",
|
|
971
|
+
children: "\u6309\u5F55\u5236\u5185\u5BB9\u7EE7\u7EED"
|
|
972
|
+
}
|
|
973
|
+
),
|
|
974
|
+
/* @__PURE__ */ jsx3(
|
|
975
|
+
"button",
|
|
976
|
+
{
|
|
977
|
+
type: "button",
|
|
978
|
+
onClick: () => mismatch.resolve("continue_replay"),
|
|
979
|
+
className: "h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
|
|
980
|
+
children: "\u4ECE\u8FD9\u91CC\u5F00\u59CB\u771F\u7684\u8FD0\u884C"
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
] })
|
|
984
|
+
]
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/components/ChatInput.tsx
|
|
990
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
421
991
|
function ChatInput({
|
|
422
992
|
value,
|
|
423
993
|
onValueChange,
|
|
@@ -442,8 +1012,8 @@ function ChatInput({
|
|
|
442
1012
|
void handleSend();
|
|
443
1013
|
}
|
|
444
1014
|
};
|
|
445
|
-
return /* @__PURE__ */
|
|
446
|
-
/* @__PURE__ */
|
|
1015
|
+
return /* @__PURE__ */ jsx4("div", { className: cn("blade-chat-input border-t border-[hsl(var(--border))] py-3", className), children: /* @__PURE__ */ jsxs3("div", { className: "blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2", children: [
|
|
1016
|
+
/* @__PURE__ */ jsx4(
|
|
447
1017
|
"textarea",
|
|
448
1018
|
{
|
|
449
1019
|
value,
|
|
@@ -460,7 +1030,7 @@ function ChatInput({
|
|
|
460
1030
|
className: "blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]"
|
|
461
1031
|
}
|
|
462
1032
|
),
|
|
463
|
-
isStreaming ? /* @__PURE__ */
|
|
1033
|
+
isStreaming ? /* @__PURE__ */ jsx4(
|
|
464
1034
|
"button",
|
|
465
1035
|
{
|
|
466
1036
|
type: "button",
|
|
@@ -469,9 +1039,9 @@ function ChatInput({
|
|
|
469
1039
|
"aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
470
1040
|
title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
471
1041
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60",
|
|
472
|
-
children: isStopping ? /* @__PURE__ */
|
|
1042
|
+
children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
|
|
473
1043
|
}
|
|
474
|
-
) : /* @__PURE__ */
|
|
1044
|
+
) : /* @__PURE__ */ jsx4(
|
|
475
1045
|
"button",
|
|
476
1046
|
{
|
|
477
1047
|
type: "button",
|
|
@@ -480,20 +1050,20 @@ function ChatInput({
|
|
|
480
1050
|
"aria-label": "\u53D1\u9001\u6D88\u606F",
|
|
481
1051
|
title: "\u53D1\u9001\u6D88\u606F",
|
|
482
1052
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40",
|
|
483
|
-
children: /* @__PURE__ */
|
|
1053
|
+
children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
|
|
484
1054
|
}
|
|
485
1055
|
)
|
|
486
1056
|
] }) });
|
|
487
1057
|
}
|
|
488
1058
|
|
|
489
1059
|
// src/components/ConnectionBanner.tsx
|
|
490
|
-
import { jsx as
|
|
1060
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
491
1061
|
function ConnectionBanner({ connection, className }) {
|
|
492
1062
|
if (connection === "connected" || connection === "connecting") {
|
|
493
1063
|
return null;
|
|
494
1064
|
}
|
|
495
1065
|
const reconnecting = connection === "reconnecting";
|
|
496
|
-
return /* @__PURE__ */
|
|
1066
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
497
1067
|
"div",
|
|
498
1068
|
{
|
|
499
1069
|
className: cn(
|
|
@@ -501,10 +1071,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
501
1071
|
reconnecting ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
502
1072
|
),
|
|
503
1073
|
children: [
|
|
504
|
-
/* @__PURE__ */
|
|
505
|
-
/* @__PURE__ */
|
|
506
|
-
/* @__PURE__ */
|
|
507
|
-
/* @__PURE__ */
|
|
1074
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: reconnecting ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
|
|
1075
|
+
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
1076
|
+
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children: reconnecting ? "\u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C\u6B63\u5728\u91CD\u8FDE\u2026" : "\u8FDE\u63A5\u5DF2\u65AD\u5F00" }),
|
|
1077
|
+
/* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: "\u6D88\u606F\u540C\u6B65\u53EF\u80FD\u4F1A\u5EF6\u8FDF\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
|
|
508
1078
|
] })
|
|
509
1079
|
]
|
|
510
1080
|
}
|
|
@@ -513,10 +1083,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
513
1083
|
|
|
514
1084
|
// src/components/MessageList.tsx
|
|
515
1085
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
516
|
-
import { useCallback as
|
|
1086
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef6, useState as useState11 } from "react";
|
|
517
1087
|
|
|
518
1088
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
519
|
-
import { useCallback, useMemo as
|
|
1089
|
+
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
|
|
520
1090
|
var DEFAULT_SPRING_ANIMATION = {
|
|
521
1091
|
/**
|
|
522
1092
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -553,12 +1123,12 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
553
1123
|
mouseDown = false;
|
|
554
1124
|
});
|
|
555
1125
|
var useStickToBottom = (options = {}) => {
|
|
556
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
557
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
558
|
-
const [isNearBottom, setIsNearBottom] =
|
|
559
|
-
const optionsRef =
|
|
1126
|
+
const [escapedFromLock, updateEscapedFromLock] = useState4(false);
|
|
1127
|
+
const [isAtBottom, updateIsAtBottom] = useState4(options.initial !== false);
|
|
1128
|
+
const [isNearBottom, setIsNearBottom] = useState4(false);
|
|
1129
|
+
const optionsRef = useRef3(null);
|
|
560
1130
|
optionsRef.current = options;
|
|
561
|
-
const isSelecting =
|
|
1131
|
+
const isSelecting = useCallback3(() => {
|
|
562
1132
|
if (!mouseDown) {
|
|
563
1133
|
return false;
|
|
564
1134
|
}
|
|
@@ -569,15 +1139,15 @@ var useStickToBottom = (options = {}) => {
|
|
|
569
1139
|
const range = selection.getRangeAt(0);
|
|
570
1140
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
571
1141
|
}, []);
|
|
572
|
-
const setIsAtBottom =
|
|
1142
|
+
const setIsAtBottom = useCallback3((isAtBottom2) => {
|
|
573
1143
|
state.isAtBottom = isAtBottom2;
|
|
574
1144
|
updateIsAtBottom(isAtBottom2);
|
|
575
1145
|
}, []);
|
|
576
|
-
const setEscapedFromLock =
|
|
1146
|
+
const setEscapedFromLock = useCallback3((escapedFromLock2) => {
|
|
577
1147
|
state.escapedFromLock = escapedFromLock2;
|
|
578
1148
|
updateEscapedFromLock(escapedFromLock2);
|
|
579
1149
|
}, []);
|
|
580
|
-
const state =
|
|
1150
|
+
const state = useMemo3(() => {
|
|
581
1151
|
let lastCalculation;
|
|
582
1152
|
return {
|
|
583
1153
|
escapedFromLock,
|
|
@@ -630,7 +1200,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
630
1200
|
}
|
|
631
1201
|
};
|
|
632
1202
|
}, []);
|
|
633
|
-
const scrollToBottom =
|
|
1203
|
+
const scrollToBottom = useCallback3((scrollOptions = {}) => {
|
|
634
1204
|
if (typeof scrollOptions === "string") {
|
|
635
1205
|
scrollOptions = { animation: scrollOptions };
|
|
636
1206
|
}
|
|
@@ -715,11 +1285,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
715
1285
|
}
|
|
716
1286
|
return next();
|
|
717
1287
|
}, [setIsAtBottom, isSelecting, state]);
|
|
718
|
-
const stopScroll =
|
|
1288
|
+
const stopScroll = useCallback3(() => {
|
|
719
1289
|
setEscapedFromLock(true);
|
|
720
1290
|
setIsAtBottom(false);
|
|
721
1291
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
722
|
-
const handleScroll =
|
|
1292
|
+
const handleScroll = useCallback3(({ target }) => {
|
|
723
1293
|
if (target !== scrollRef.current) {
|
|
724
1294
|
return;
|
|
725
1295
|
}
|
|
@@ -758,7 +1328,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
758
1328
|
}
|
|
759
1329
|
}, 1);
|
|
760
1330
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
761
|
-
const handleWheel =
|
|
1331
|
+
const handleWheel = useCallback3(({ target, deltaY }) => {
|
|
762
1332
|
let element = target;
|
|
763
1333
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
764
1334
|
if (!element.parentElement) {
|
|
@@ -828,7 +1398,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
828
1398
|
};
|
|
829
1399
|
};
|
|
830
1400
|
function useRefCallback(callback, deps) {
|
|
831
|
-
const result =
|
|
1401
|
+
const result = useCallback3((ref) => {
|
|
832
1402
|
result.current = ref;
|
|
833
1403
|
return callback(ref);
|
|
834
1404
|
}, deps);
|
|
@@ -860,11 +1430,11 @@ function mergeAnimations(...animations) {
|
|
|
860
1430
|
|
|
861
1431
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
862
1432
|
import * as React from "react";
|
|
863
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
1433
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef4 } from "react";
|
|
864
1434
|
var StickToBottomContext = createContext2(null);
|
|
865
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1435
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
|
|
866
1436
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
867
|
-
const customTargetScrollTop =
|
|
1437
|
+
const customTargetScrollTop = useRef4(null);
|
|
868
1438
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
869
1439
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
870
1440
|
return get?.(target, elements) ?? target;
|
|
@@ -878,7 +1448,7 @@ function StickToBottom({ instance, children, resize, initial, mass, damping, sti
|
|
|
878
1448
|
targetScrollTop
|
|
879
1449
|
});
|
|
880
1450
|
const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state } = instance ?? defaultInstance;
|
|
881
|
-
const context =
|
|
1451
|
+
const context = useMemo4(() => ({
|
|
882
1452
|
scrollToBottom,
|
|
883
1453
|
stopScroll,
|
|
884
1454
|
scrollRef,
|
|
@@ -941,10 +1511,10 @@ function useStickToBottomContext() {
|
|
|
941
1511
|
|
|
942
1512
|
// src/components/AssistantTurnBlock.tsx
|
|
943
1513
|
import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
|
|
944
|
-
import { useState as
|
|
1514
|
+
import { useState as useState10 } from "react";
|
|
945
1515
|
|
|
946
1516
|
// src/components/AgentLoopBlock.tsx
|
|
947
|
-
import { useState as
|
|
1517
|
+
import { useState as useState5 } from "react";
|
|
948
1518
|
|
|
949
1519
|
// src/components/display-utils.ts
|
|
950
1520
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1066,7 +1636,7 @@ function formatToolResult(result) {
|
|
|
1066
1636
|
}
|
|
1067
1637
|
|
|
1068
1638
|
// src/components/AgentLoopBlock.tsx
|
|
1069
|
-
import { jsx as
|
|
1639
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1070
1640
|
function parseAgentDescription(argumentsJson) {
|
|
1071
1641
|
try {
|
|
1072
1642
|
const parsed = JSON.parse(argumentsJson);
|
|
@@ -1076,12 +1646,12 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1076
1646
|
}
|
|
1077
1647
|
}
|
|
1078
1648
|
function AgentLoopBlock({ toolCall }) {
|
|
1079
|
-
const [expanded, setExpanded] =
|
|
1649
|
+
const [expanded, setExpanded] = useState5(false);
|
|
1080
1650
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1081
1651
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1082
1652
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1083
|
-
return /* @__PURE__ */
|
|
1084
|
-
/* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
|
|
1654
|
+
/* @__PURE__ */ jsxs5(
|
|
1085
1655
|
"div",
|
|
1086
1656
|
{
|
|
1087
1657
|
className: cn(
|
|
@@ -1089,7 +1659,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1089
1659
|
failed ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : running ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]"
|
|
1090
1660
|
),
|
|
1091
1661
|
children: [
|
|
1092
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsxs5(
|
|
1093
1663
|
"button",
|
|
1094
1664
|
{
|
|
1095
1665
|
type: "button",
|
|
@@ -1097,7 +1667,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1097
1667
|
className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
|
|
1098
1668
|
"aria-expanded": expanded,
|
|
1099
1669
|
children: [
|
|
1100
|
-
/* @__PURE__ */
|
|
1670
|
+
/* @__PURE__ */ jsx6(
|
|
1101
1671
|
ChevronRight,
|
|
1102
1672
|
{
|
|
1103
1673
|
size: 11,
|
|
@@ -1107,8 +1677,8 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1107
1677
|
)
|
|
1108
1678
|
}
|
|
1109
1679
|
),
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1680
|
+
/* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1681
|
+
/* @__PURE__ */ jsxs5(
|
|
1112
1682
|
"span",
|
|
1113
1683
|
{
|
|
1114
1684
|
className: cn(
|
|
@@ -1116,35 +1686,35 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1116
1686
|
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1117
1687
|
),
|
|
1118
1688
|
children: [
|
|
1119
|
-
running ? /* @__PURE__ */
|
|
1120
|
-
/* @__PURE__ */
|
|
1689
|
+
running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
|
|
1690
|
+
/* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
|
|
1121
1691
|
]
|
|
1122
1692
|
}
|
|
1123
1693
|
),
|
|
1124
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1125
1695
|
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1126
1696
|
description
|
|
1127
1697
|
] })
|
|
1128
1698
|
]
|
|
1129
1699
|
}
|
|
1130
1700
|
),
|
|
1131
|
-
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */
|
|
1701
|
+
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx6("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
|
|
1132
1702
|
]
|
|
1133
1703
|
}
|
|
1134
1704
|
),
|
|
1135
|
-
expanded && toolCall.result != null && /* @__PURE__ */
|
|
1136
|
-
/* @__PURE__ */
|
|
1137
|
-
/* @__PURE__ */
|
|
1705
|
+
expanded && toolCall.result != null && /* @__PURE__ */ jsxs5("div", { className: "ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
|
|
1706
|
+
/* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
1707
|
+
/* @__PURE__ */ jsx6("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
|
|
1138
1708
|
] })
|
|
1139
1709
|
] });
|
|
1140
1710
|
}
|
|
1141
1711
|
|
|
1142
1712
|
// src/components/MarkdownContent.tsx
|
|
1143
1713
|
import {
|
|
1144
|
-
useEffect as
|
|
1145
|
-
useMemo as
|
|
1146
|
-
useRef as
|
|
1147
|
-
useState as
|
|
1714
|
+
useEffect as useEffect4,
|
|
1715
|
+
useMemo as useMemo5,
|
|
1716
|
+
useRef as useRef5,
|
|
1717
|
+
useState as useState7
|
|
1148
1718
|
} from "react";
|
|
1149
1719
|
|
|
1150
1720
|
// src/lib/media-tags.ts
|
|
@@ -1191,8 +1761,8 @@ function collapseMediaTags(text, options) {
|
|
|
1191
1761
|
}
|
|
1192
1762
|
|
|
1193
1763
|
// src/components/FileCard.tsx
|
|
1194
|
-
import { useState as
|
|
1195
|
-
import { jsx as
|
|
1764
|
+
import { useState as useState6 } from "react";
|
|
1765
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1196
1766
|
var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
|
|
1197
1767
|
var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
|
|
1198
1768
|
var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
|
|
@@ -1234,8 +1804,8 @@ function FileCard({
|
|
|
1234
1804
|
...props
|
|
1235
1805
|
}) {
|
|
1236
1806
|
const client = useBladeClient();
|
|
1237
|
-
const [downloading, setDownloading] =
|
|
1238
|
-
const [failed, setFailed] =
|
|
1807
|
+
const [downloading, setDownloading] = useState6(false);
|
|
1808
|
+
const [failed, setFailed] = useState6(false);
|
|
1239
1809
|
const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
|
|
1240
1810
|
const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
|
|
1241
1811
|
const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
|
|
@@ -1253,8 +1823,8 @@ function FileCard({
|
|
|
1253
1823
|
setDownloading(false);
|
|
1254
1824
|
}
|
|
1255
1825
|
};
|
|
1256
|
-
return /* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1826
|
+
return /* @__PURE__ */ jsxs6("span", { ...props, className: cn("blade-chat-file-card", className), children: [
|
|
1827
|
+
/* @__PURE__ */ jsxs6(
|
|
1258
1828
|
"button",
|
|
1259
1829
|
{
|
|
1260
1830
|
type: "button",
|
|
@@ -1263,24 +1833,24 @@ function FileCard({
|
|
|
1263
1833
|
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
|
|
1264
1834
|
title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : `\u4E0B\u8F7D ${name}`,
|
|
1265
1835
|
children: [
|
|
1266
|
-
/* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */
|
|
1268
|
-
/* @__PURE__ */
|
|
1836
|
+
/* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-icon", children: downloading ? /* @__PURE__ */ jsx7(LoaderCircle, { size: 15, className: "animate-spin" }) : /* @__PURE__ */ jsx7(Icon2, { size: 15 }) }),
|
|
1837
|
+
/* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-name", children: name }),
|
|
1838
|
+
/* @__PURE__ */ jsx7(Download, { size: 13, className: "blade-chat-file-card-action" })
|
|
1269
1839
|
]
|
|
1270
1840
|
}
|
|
1271
1841
|
),
|
|
1272
|
-
failed && /* @__PURE__ */
|
|
1842
|
+
failed && /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
|
|
1273
1843
|
] });
|
|
1274
1844
|
}
|
|
1275
1845
|
|
|
1276
1846
|
// src/components/MarkdownContent.tsx
|
|
1277
|
-
import { jsx as
|
|
1847
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1278
1848
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1279
1849
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1280
|
-
const preRef =
|
|
1281
|
-
const [copied, setCopied] =
|
|
1282
|
-
const [language, setLanguage] =
|
|
1283
|
-
|
|
1850
|
+
const preRef = useRef5(null);
|
|
1851
|
+
const [copied, setCopied] = useState7(false);
|
|
1852
|
+
const [language, setLanguage] = useState7("");
|
|
1853
|
+
useEffect4(() => {
|
|
1284
1854
|
const codeEl = preRef.current?.querySelector("code");
|
|
1285
1855
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1286
1856
|
}, []);
|
|
@@ -1291,10 +1861,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1291
1861
|
setTimeout(() => setCopied(false), 2e3);
|
|
1292
1862
|
}
|
|
1293
1863
|
};
|
|
1294
|
-
return /* @__PURE__ */
|
|
1295
|
-
/* @__PURE__ */
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1864
|
+
return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
|
|
1865
|
+
/* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
|
|
1866
|
+
/* @__PURE__ */ jsx8("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
|
|
1867
|
+
/* @__PURE__ */ jsxs7(
|
|
1298
1868
|
"button",
|
|
1299
1869
|
{
|
|
1300
1870
|
type: "button",
|
|
@@ -1304,13 +1874,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1304
1874
|
copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
|
|
1305
1875
|
),
|
|
1306
1876
|
children: [
|
|
1307
|
-
copied ? /* @__PURE__ */
|
|
1308
|
-
/* @__PURE__ */
|
|
1877
|
+
copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
|
|
1878
|
+
/* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
|
|
1309
1879
|
]
|
|
1310
1880
|
}
|
|
1311
1881
|
)
|
|
1312
1882
|
] }),
|
|
1313
|
-
/* @__PURE__ */
|
|
1883
|
+
/* @__PURE__ */ jsx8(
|
|
1314
1884
|
"pre",
|
|
1315
1885
|
{
|
|
1316
1886
|
ref: preRef,
|
|
@@ -1322,7 +1892,7 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1322
1892
|
] });
|
|
1323
1893
|
}
|
|
1324
1894
|
function ExternalAnchor({ node: _node, children, ...props }) {
|
|
1325
|
-
return /* @__PURE__ */
|
|
1895
|
+
return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
|
|
1326
1896
|
}
|
|
1327
1897
|
var MARKDOWN_COMPONENTS = {
|
|
1328
1898
|
pre: CodeBlockPre,
|
|
@@ -1333,19 +1903,19 @@ var CUSTOM_ALLOWED_TAGS = {
|
|
|
1333
1903
|
};
|
|
1334
1904
|
function MarkdownContent({ children, className, mode, sessionId }) {
|
|
1335
1905
|
const streaming = mode === "streaming";
|
|
1336
|
-
const resolvedChildren =
|
|
1906
|
+
const resolvedChildren = useMemo5(() => {
|
|
1337
1907
|
const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
|
|
1338
1908
|
if (!sessionId) return withoutReminders;
|
|
1339
1909
|
return collapseMediaTags(withoutReminders, { streaming });
|
|
1340
1910
|
}, [children, sessionId, streaming]);
|
|
1341
|
-
const components =
|
|
1911
|
+
const components = useMemo5(
|
|
1342
1912
|
() => ({
|
|
1343
1913
|
...MARKDOWN_COMPONENTS,
|
|
1344
|
-
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */
|
|
1914
|
+
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx8(FileCard, { ...fileCardProps, sessionId })
|
|
1345
1915
|
}),
|
|
1346
1916
|
[sessionId]
|
|
1347
1917
|
);
|
|
1348
|
-
return /* @__PURE__ */
|
|
1918
|
+
return /* @__PURE__ */ jsx8(
|
|
1349
1919
|
_r,
|
|
1350
1920
|
{
|
|
1351
1921
|
className: cn("blade-chat-markdown break-words", className),
|
|
@@ -1359,17 +1929,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
|
|
|
1359
1929
|
}
|
|
1360
1930
|
|
|
1361
1931
|
// src/components/Shimmer.tsx
|
|
1362
|
-
import { jsx as
|
|
1932
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1363
1933
|
function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
1364
|
-
return /* @__PURE__ */
|
|
1934
|
+
return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
|
|
1365
1935
|
}
|
|
1366
1936
|
|
|
1367
1937
|
// src/components/ToolCallBlock.tsx
|
|
1368
|
-
import { useState as
|
|
1938
|
+
import { useState as useState9 } from "react";
|
|
1369
1939
|
|
|
1370
1940
|
// src/components/AskUserQuestionBlock.tsx
|
|
1371
|
-
import { useEffect as
|
|
1372
|
-
import { jsx as
|
|
1941
|
+
import { useEffect as useEffect5, useMemo as useMemo6, useState as useState8 } from "react";
|
|
1942
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1373
1943
|
function AskUserQuestionBlock({
|
|
1374
1944
|
data,
|
|
1375
1945
|
answered,
|
|
@@ -1378,16 +1948,16 @@ function AskUserQuestionBlock({
|
|
|
1378
1948
|
answerData,
|
|
1379
1949
|
onAnswer
|
|
1380
1950
|
}) {
|
|
1381
|
-
const [selections, setSelections] =
|
|
1382
|
-
const [customTexts, setCustomTexts] =
|
|
1383
|
-
const [usingCustom, setUsingCustom] =
|
|
1384
|
-
const [submitted, setSubmitted] =
|
|
1385
|
-
|
|
1951
|
+
const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
|
|
1952
|
+
const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
|
|
1953
|
+
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
1954
|
+
const [submitted, setSubmitted] = useState8(false);
|
|
1955
|
+
useEffect5(() => {
|
|
1386
1956
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1387
1957
|
setSubmitted(false);
|
|
1388
1958
|
}
|
|
1389
1959
|
}, [sessionStatus]);
|
|
1390
|
-
const displayAnswerState =
|
|
1960
|
+
const displayAnswerState = useMemo6(() => {
|
|
1391
1961
|
if (!(answered && answerData)) {
|
|
1392
1962
|
return { selections, customTexts, usingCustom };
|
|
1393
1963
|
}
|
|
@@ -1472,7 +2042,7 @@ ${parts.join("\n")}`;
|
|
|
1472
2042
|
setSubmitted(true);
|
|
1473
2043
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
1474
2044
|
};
|
|
1475
|
-
return /* @__PURE__ */
|
|
2045
|
+
return /* @__PURE__ */ jsxs8(
|
|
1476
2046
|
"div",
|
|
1477
2047
|
{
|
|
1478
2048
|
className: cn(
|
|
@@ -1480,12 +2050,12 @@ ${parts.join("\n")}`;
|
|
|
1480
2050
|
answered ? "max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80" : "max-w-lg space-y-5 p-4 text-sm"
|
|
1481
2051
|
),
|
|
1482
2052
|
children: [
|
|
1483
|
-
data.source_loop?.description && /* @__PURE__ */
|
|
2053
|
+
data.source_loop?.description && /* @__PURE__ */ jsxs8("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
1484
2054
|
"\u5B50\u667A\u80FD\u4F53\u300C",
|
|
1485
2055
|
data.source_loop.description,
|
|
1486
2056
|
"\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
|
|
1487
2057
|
] }),
|
|
1488
|
-
data.questions.map((q, qIdx) => /* @__PURE__ */
|
|
2058
|
+
data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
|
|
1489
2059
|
QuestionCard,
|
|
1490
2060
|
{
|
|
1491
2061
|
question: q,
|
|
@@ -1500,7 +2070,7 @@ ${parts.join("\n")}`;
|
|
|
1500
2070
|
},
|
|
1501
2071
|
q.question
|
|
1502
2072
|
)),
|
|
1503
|
-
!answered && !submitted && onAnswer && /* @__PURE__ */
|
|
2073
|
+
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
|
|
1504
2074
|
"button",
|
|
1505
2075
|
{
|
|
1506
2076
|
type: "button",
|
|
@@ -1510,14 +2080,14 @@ ${parts.join("\n")}`;
|
|
|
1510
2080
|
children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
|
|
1511
2081
|
}
|
|
1512
2082
|
),
|
|
1513
|
-
submitted && !answered && /* @__PURE__ */
|
|
2083
|
+
submitted && !answered && /* @__PURE__ */ jsxs8(
|
|
1514
2084
|
"button",
|
|
1515
2085
|
{
|
|
1516
2086
|
type: "button",
|
|
1517
2087
|
disabled: true,
|
|
1518
2088
|
className: "flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80",
|
|
1519
2089
|
children: [
|
|
1520
|
-
/* @__PURE__ */
|
|
2090
|
+
/* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
1521
2091
|
"\u786E\u8BA4\u4E2D"
|
|
1522
2092
|
]
|
|
1523
2093
|
}
|
|
@@ -1538,30 +2108,30 @@ function QuestionCard({
|
|
|
1538
2108
|
onCustomChange
|
|
1539
2109
|
}) {
|
|
1540
2110
|
const multi = question.multiSelect ?? false;
|
|
1541
|
-
return /* @__PURE__ */
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
2111
|
+
return /* @__PURE__ */ jsxs8("div", { children: [
|
|
2112
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
2113
|
+
/* @__PURE__ */ jsx10(
|
|
1544
2114
|
MessageSquareMore,
|
|
1545
2115
|
{
|
|
1546
2116
|
size: answered ? 12 : 13,
|
|
1547
2117
|
className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
|
|
1548
2118
|
}
|
|
1549
2119
|
),
|
|
1550
|
-
/* @__PURE__ */
|
|
2120
|
+
/* @__PURE__ */ jsx10(
|
|
1551
2121
|
"div",
|
|
1552
2122
|
{
|
|
1553
2123
|
className: cn(
|
|
1554
2124
|
"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
|
|
1555
2125
|
answered ? "text-xs" : "text-sm"
|
|
1556
2126
|
),
|
|
1557
|
-
children: /* @__PURE__ */
|
|
2127
|
+
children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
|
|
1558
2128
|
}
|
|
1559
2129
|
)
|
|
1560
2130
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
2131
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
|
|
1562
2132
|
question.options.map((opt, optIdx) => {
|
|
1563
2133
|
const isSel = selected.has(optIdx);
|
|
1564
|
-
return /* @__PURE__ */
|
|
2134
|
+
return /* @__PURE__ */ jsxs8(
|
|
1565
2135
|
"button",
|
|
1566
2136
|
{
|
|
1567
2137
|
type: "button",
|
|
@@ -1575,14 +2145,14 @@ function QuestionCard({
|
|
|
1575
2145
|
answered && "cursor-default opacity-70"
|
|
1576
2146
|
),
|
|
1577
2147
|
children: [
|
|
1578
|
-
multi && /* @__PURE__ */
|
|
2148
|
+
multi && /* @__PURE__ */ jsx10(
|
|
1579
2149
|
"div",
|
|
1580
2150
|
{
|
|
1581
2151
|
className: cn(
|
|
1582
2152
|
"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
|
|
1583
2153
|
isSel && !answered ? "border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]" : isSel ? "border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]" : "border-[hsl(var(--border))]"
|
|
1584
2154
|
),
|
|
1585
|
-
children: isSel && /* @__PURE__ */
|
|
2155
|
+
children: isSel && /* @__PURE__ */ jsx10(
|
|
1586
2156
|
Check,
|
|
1587
2157
|
{
|
|
1588
2158
|
size: 9,
|
|
@@ -1591,9 +2161,9 @@ function QuestionCard({
|
|
|
1591
2161
|
)
|
|
1592
2162
|
}
|
|
1593
2163
|
),
|
|
1594
|
-
/* @__PURE__ */
|
|
1595
|
-
/* @__PURE__ */
|
|
1596
|
-
opt.description && /* @__PURE__ */
|
|
2164
|
+
/* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
|
|
2165
|
+
/* @__PURE__ */ jsx10("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
|
|
2166
|
+
opt.description && /* @__PURE__ */ jsx10(
|
|
1597
2167
|
"div",
|
|
1598
2168
|
{
|
|
1599
2169
|
className: cn(
|
|
@@ -1610,7 +2180,7 @@ function QuestionCard({
|
|
|
1610
2180
|
opt.label
|
|
1611
2181
|
);
|
|
1612
2182
|
}),
|
|
1613
|
-
answered && !isCustom ? null : /* @__PURE__ */
|
|
2183
|
+
answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
|
|
1614
2184
|
"div",
|
|
1615
2185
|
{
|
|
1616
2186
|
className: cn(
|
|
@@ -1620,8 +2190,8 @@ function QuestionCard({
|
|
|
1620
2190
|
answered && "cursor-default opacity-70"
|
|
1621
2191
|
),
|
|
1622
2192
|
children: [
|
|
1623
|
-
/* @__PURE__ */
|
|
1624
|
-
/* @__PURE__ */
|
|
2193
|
+
/* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2194
|
+
/* @__PURE__ */ jsx10(
|
|
1625
2195
|
"input",
|
|
1626
2196
|
{
|
|
1627
2197
|
type: "text",
|
|
@@ -1689,7 +2259,7 @@ function normalizeOptionItem(value) {
|
|
|
1689
2259
|
}
|
|
1690
2260
|
|
|
1691
2261
|
// src/components/ToolCallBlock.tsx
|
|
1692
|
-
import { Fragment, jsx as
|
|
2262
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1693
2263
|
function resolveAskQuestionState({
|
|
1694
2264
|
toolStatus,
|
|
1695
2265
|
hasAnswerData,
|
|
@@ -1709,12 +2279,12 @@ function ToolCallBlock({
|
|
|
1709
2279
|
sessionStatus,
|
|
1710
2280
|
renderer
|
|
1711
2281
|
}) {
|
|
1712
|
-
const [expanded, setExpanded] =
|
|
2282
|
+
const [expanded, setExpanded] = useState9(false);
|
|
1713
2283
|
const normalizedName = formatToolName(toolCall.name);
|
|
1714
2284
|
if (renderer) {
|
|
1715
2285
|
const custom = renderer(toolCall);
|
|
1716
2286
|
if (custom !== null && custom !== void 0) {
|
|
1717
|
-
return /* @__PURE__ */
|
|
2287
|
+
return /* @__PURE__ */ jsx11(Fragment, { children: custom });
|
|
1718
2288
|
}
|
|
1719
2289
|
}
|
|
1720
2290
|
if (normalizedName === "AskUserQuestion") {
|
|
@@ -1726,7 +2296,7 @@ function ToolCallBlock({
|
|
|
1726
2296
|
});
|
|
1727
2297
|
const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
|
|
1728
2298
|
if (askData) {
|
|
1729
|
-
return /* @__PURE__ */
|
|
2299
|
+
return /* @__PURE__ */ jsx11(
|
|
1730
2300
|
AskUserQuestionBlock,
|
|
1731
2301
|
{
|
|
1732
2302
|
data: askData,
|
|
@@ -1739,24 +2309,24 @@ function ToolCallBlock({
|
|
|
1739
2309
|
);
|
|
1740
2310
|
}
|
|
1741
2311
|
if (toolCall.status === "pending") {
|
|
1742
|
-
return /* @__PURE__ */
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
/* @__PURE__ */
|
|
2312
|
+
return /* @__PURE__ */ jsxs9("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
|
|
2313
|
+
/* @__PURE__ */ jsx11(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
2314
|
+
/* @__PURE__ */ jsx11("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
|
|
1745
2315
|
] });
|
|
1746
2316
|
}
|
|
1747
|
-
return /* @__PURE__ */
|
|
1748
|
-
/* @__PURE__ */
|
|
1749
|
-
/* @__PURE__ */
|
|
2317
|
+
return /* @__PURE__ */ jsxs9("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
|
|
2318
|
+
/* @__PURE__ */ jsx11("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
|
|
2319
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
|
|
1750
2320
|
] });
|
|
1751
2321
|
}
|
|
1752
2322
|
const tone = getToolTone(toolCall.status);
|
|
1753
2323
|
const displayName = getToolDisplayLabel(toolCall);
|
|
1754
2324
|
const toneClass = tone === "red" ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : tone === "amber" ? "border-l-amber-400" : tone === "blue" ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]";
|
|
1755
|
-
const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */
|
|
2325
|
+
const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx11(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx11(X, { size: 11 }) : /* @__PURE__ */ jsx11(Check, { size: 11 });
|
|
1756
2326
|
const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
|
|
1757
|
-
return /* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1759
|
-
/* @__PURE__ */
|
|
2327
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool ml-4 text-xs", children: [
|
|
2328
|
+
/* @__PURE__ */ jsxs9("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
|
|
2329
|
+
/* @__PURE__ */ jsxs9(
|
|
1760
2330
|
"button",
|
|
1761
2331
|
{
|
|
1762
2332
|
type: "button",
|
|
@@ -1764,7 +2334,7 @@ function ToolCallBlock({
|
|
|
1764
2334
|
className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
|
|
1765
2335
|
"aria-expanded": expanded,
|
|
1766
2336
|
children: [
|
|
1767
|
-
/* @__PURE__ */
|
|
2337
|
+
/* @__PURE__ */ jsx11(
|
|
1768
2338
|
ChevronRight,
|
|
1769
2339
|
{
|
|
1770
2340
|
size: 11,
|
|
@@ -1774,24 +2344,24 @@ function ToolCallBlock({
|
|
|
1774
2344
|
)
|
|
1775
2345
|
}
|
|
1776
2346
|
),
|
|
1777
|
-
/* @__PURE__ */
|
|
2347
|
+
/* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
|
|
1778
2348
|
statusIcon,
|
|
1779
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
|
|
1780
2350
|
] }),
|
|
1781
|
-
/* @__PURE__ */
|
|
2351
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
|
|
1782
2352
|
]
|
|
1783
2353
|
}
|
|
1784
2354
|
),
|
|
1785
|
-
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */
|
|
2355
|
+
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx11("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
|
|
1786
2356
|
] }),
|
|
1787
|
-
expanded && /* @__PURE__ */
|
|
1788
|
-
/* @__PURE__ */
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
1792
|
-
toolCall.result != null && /* @__PURE__ */
|
|
1793
|
-
/* @__PURE__ */
|
|
1794
|
-
/* @__PURE__ */
|
|
2357
|
+
expanded && /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
|
|
2358
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
|
|
2359
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
|
|
2360
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
|
|
2361
|
+
/* @__PURE__ */ jsx11("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolArgs(toolCall.arguments) }),
|
|
2362
|
+
toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
2363
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
2364
|
+
/* @__PURE__ */ jsx11("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
|
|
1795
2365
|
] })
|
|
1796
2366
|
] })
|
|
1797
2367
|
] });
|
|
@@ -1809,11 +2379,11 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
1809
2379
|
}
|
|
1810
2380
|
|
|
1811
2381
|
// src/components/AssistantTurnBlock.tsx
|
|
1812
|
-
import { jsx as
|
|
2382
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1813
2383
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
1814
|
-
const [open, setOpen] =
|
|
1815
|
-
return /* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
2384
|
+
const [open, setOpen] = useState10(false);
|
|
2385
|
+
return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
|
|
2386
|
+
/* @__PURE__ */ jsxs10(
|
|
1817
2387
|
"button",
|
|
1818
2388
|
{
|
|
1819
2389
|
type: "button",
|
|
@@ -1821,14 +2391,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
1821
2391
|
"aria-expanded": open,
|
|
1822
2392
|
className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
1823
2393
|
children: [
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
isStreaming ? /* @__PURE__ */
|
|
1826
|
-
/* @__PURE__ */
|
|
2394
|
+
/* @__PURE__ */ jsx12(Brain, { size: 12, className: "shrink-0" }),
|
|
2395
|
+
isStreaming ? /* @__PURE__ */ jsx12(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx12("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
|
|
2396
|
+
/* @__PURE__ */ jsxs10("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
|
|
1827
2397
|
"\xB7 ",
|
|
1828
2398
|
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
1829
2399
|
" \u5B57"
|
|
1830
2400
|
] }),
|
|
1831
|
-
/* @__PURE__ */
|
|
2401
|
+
/* @__PURE__ */ jsx12(
|
|
1832
2402
|
ChevronDown,
|
|
1833
2403
|
{
|
|
1834
2404
|
size: 12,
|
|
@@ -1838,7 +2408,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
1838
2408
|
]
|
|
1839
2409
|
}
|
|
1840
2410
|
),
|
|
1841
|
-
open && /* @__PURE__ */
|
|
2411
|
+
open && /* @__PURE__ */ jsx12("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
|
|
1842
2412
|
] });
|
|
1843
2413
|
}
|
|
1844
2414
|
function getMessageText(message) {
|
|
@@ -1864,21 +2434,21 @@ function AssistantTurnBlock({
|
|
|
1864
2434
|
(message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
1865
2435
|
);
|
|
1866
2436
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
1867
|
-
return /* @__PURE__ */
|
|
1868
|
-
hasInterrupted && /* @__PURE__ */
|
|
2437
|
+
return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2438
|
+
hasInterrupted && /* @__PURE__ */ jsx12("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
|
|
1869
2439
|
messages.map((message, index) => {
|
|
1870
2440
|
const isLast = index === messages.length - 1;
|
|
1871
2441
|
const streamingThis = isStreaming && isLast;
|
|
1872
2442
|
const text = getMessageText(message);
|
|
1873
2443
|
const toolCalls = message.tool_calls ?? [];
|
|
1874
2444
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
1875
|
-
return /* @__PURE__ */
|
|
2445
|
+
return /* @__PURE__ */ jsxs10(
|
|
1876
2446
|
"div",
|
|
1877
2447
|
{
|
|
1878
2448
|
className: "flex flex-col gap-3",
|
|
1879
2449
|
children: [
|
|
1880
|
-
showReasoning && message.reasoning && /* @__PURE__ */
|
|
1881
|
-
text && /* @__PURE__ */
|
|
2450
|
+
showReasoning && message.reasoning && /* @__PURE__ */ jsx12(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
|
|
2451
|
+
text && /* @__PURE__ */ jsx12("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx12(
|
|
1882
2452
|
MarkdownContent,
|
|
1883
2453
|
{
|
|
1884
2454
|
mode: streamingThis ? "streaming" : "static",
|
|
@@ -1887,8 +2457,8 @@ function AssistantTurnBlock({
|
|
|
1887
2457
|
children: text
|
|
1888
2458
|
}
|
|
1889
2459
|
) }),
|
|
1890
|
-
toolCalls.length > 0 && /* @__PURE__ */
|
|
1891
|
-
(toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */
|
|
2460
|
+
toolCalls.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: toolCalls.map(
|
|
2461
|
+
(toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx12(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx12(
|
|
1892
2462
|
ToolCallBlock,
|
|
1893
2463
|
{
|
|
1894
2464
|
toolCall,
|
|
@@ -1906,13 +2476,13 @@ function AssistantTurnBlock({
|
|
|
1906
2476
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
1907
2477
|
);
|
|
1908
2478
|
}),
|
|
1909
|
-
isStreaming && !hasAnyContent && /* @__PURE__ */
|
|
2479
|
+
isStreaming && !hasAnyContent && /* @__PURE__ */ jsx12(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
|
|
1910
2480
|
] });
|
|
1911
2481
|
}
|
|
1912
2482
|
|
|
1913
2483
|
// src/components/RenderErrorBoundary.tsx
|
|
1914
2484
|
import { Component } from "react";
|
|
1915
|
-
import { jsx as
|
|
2485
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1916
2486
|
function getFirstComponentName(componentStack) {
|
|
1917
2487
|
const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
|
|
1918
2488
|
return match?.[1] ?? null;
|
|
@@ -1945,18 +2515,18 @@ var RenderErrorBoundary = class extends Component {
|
|
|
1945
2515
|
return children;
|
|
1946
2516
|
}
|
|
1947
2517
|
const componentName = getFirstComponentName(componentStack);
|
|
1948
|
-
return /* @__PURE__ */
|
|
1949
|
-
/* @__PURE__ */
|
|
1950
|
-
/* @__PURE__ */
|
|
1951
|
-
/* @__PURE__ */
|
|
2518
|
+
return /* @__PURE__ */ jsx13("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-start gap-2", children: [
|
|
2519
|
+
/* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
|
|
2520
|
+
/* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
|
|
2521
|
+
/* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
|
|
1952
2522
|
label,
|
|
1953
2523
|
"\u6E32\u67D3\u5931\u8D25"
|
|
1954
2524
|
] }),
|
|
1955
|
-
/* @__PURE__ */
|
|
2525
|
+
/* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
|
|
1956
2526
|
componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
|
|
1957
2527
|
error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
|
|
1958
2528
|
] }),
|
|
1959
|
-
details ? /* @__PURE__ */
|
|
2529
|
+
details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
|
|
1960
2530
|
] })
|
|
1961
2531
|
] }) });
|
|
1962
2532
|
}
|
|
@@ -1964,7 +2534,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
1964
2534
|
|
|
1965
2535
|
// src/components/UserMessageBubble.tsx
|
|
1966
2536
|
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
1967
|
-
import { jsx as
|
|
2537
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1968
2538
|
function isUserMessage(message) {
|
|
1969
2539
|
return message.role === "user";
|
|
1970
2540
|
}
|
|
@@ -1976,8 +2546,8 @@ function UserMessageBubble({ message, className }) {
|
|
|
1976
2546
|
const text = getTextContent2(message.content).trim();
|
|
1977
2547
|
const fileParts = getFileParts(message.content);
|
|
1978
2548
|
const imageParts = getImageParts(message.content);
|
|
1979
|
-
return /* @__PURE__ */
|
|
1980
|
-
imageParts.length > 0 && /* @__PURE__ */
|
|
2549
|
+
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs12("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
|
|
2550
|
+
imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
|
|
1981
2551
|
"img",
|
|
1982
2552
|
{
|
|
1983
2553
|
src: part.image_url.url,
|
|
@@ -1986,21 +2556,21 @@ function UserMessageBubble({ message, className }) {
|
|
|
1986
2556
|
},
|
|
1987
2557
|
part.image_url.url
|
|
1988
2558
|
)) }),
|
|
1989
|
-
fileParts.length > 0 && /* @__PURE__ */
|
|
2559
|
+
fileParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs12(
|
|
1990
2560
|
"div",
|
|
1991
2561
|
{
|
|
1992
2562
|
className: "flex items-center gap-1.5 rounded-lg border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]",
|
|
1993
2563
|
children: [
|
|
1994
|
-
/* @__PURE__ */
|
|
1995
|
-
/* @__PURE__ */
|
|
2564
|
+
/* @__PURE__ */ jsx14(FileText, { size: 12, className: "shrink-0" }),
|
|
2565
|
+
/* @__PURE__ */ jsx14("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
|
|
1996
2566
|
]
|
|
1997
2567
|
},
|
|
1998
2568
|
`${part.name}-${part.data.length}`
|
|
1999
2569
|
)) }),
|
|
2000
|
-
text && /* @__PURE__ */
|
|
2001
|
-
text && isSending(message) && /* @__PURE__ */
|
|
2002
|
-
/* @__PURE__ */
|
|
2003
|
-
/* @__PURE__ */
|
|
2570
|
+
text && /* @__PURE__ */ jsx14("div", { className: "blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx14(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
|
|
2571
|
+
text && isSending(message) && /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
|
|
2572
|
+
/* @__PURE__ */ jsx14(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
|
|
2573
|
+
/* @__PURE__ */ jsx14("span", { children: "\u53D1\u9001\u4E2D" })
|
|
2004
2574
|
] })
|
|
2005
2575
|
] }) });
|
|
2006
2576
|
}
|
|
@@ -2009,11 +2579,11 @@ function ErrorMessageBlock({
|
|
|
2009
2579
|
className
|
|
2010
2580
|
}) {
|
|
2011
2581
|
const text = getTextContent2(message.content);
|
|
2012
|
-
return /* @__PURE__ */
|
|
2582
|
+
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-error-row flex justify-center", className), children: /* @__PURE__ */ jsx14("div", { className: "blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]", children: text }) });
|
|
2013
2583
|
}
|
|
2014
2584
|
|
|
2015
2585
|
// src/components/MessageList.tsx
|
|
2016
|
-
import { jsx as
|
|
2586
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2017
2587
|
function parseModeChange(message) {
|
|
2018
2588
|
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
2019
2589
|
return null;
|
|
@@ -2056,7 +2626,7 @@ function MessageList({
|
|
|
2056
2626
|
className,
|
|
2057
2627
|
sessionId
|
|
2058
2628
|
}) {
|
|
2059
|
-
const renderBlocks =
|
|
2629
|
+
const renderBlocks = useMemo7(() => {
|
|
2060
2630
|
const visible = messages.filter((message) => {
|
|
2061
2631
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
2062
2632
|
if (isHiddenInternalMessage(message)) return false;
|
|
@@ -2129,24 +2699,24 @@ function MessageList({
|
|
|
2129
2699
|
}
|
|
2130
2700
|
return blocks;
|
|
2131
2701
|
}, [messages, isStreaming]);
|
|
2132
|
-
return /* @__PURE__ */
|
|
2133
|
-
/* @__PURE__ */
|
|
2134
|
-
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */
|
|
2135
|
-
/* @__PURE__ */
|
|
2136
|
-
/* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2702
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs13(StickToBottom, { className: "h-full overflow-y-hidden", initial: "instant", resize: "instant", children: [
|
|
2703
|
+
/* @__PURE__ */ jsx15(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs13("div", { className: "flex min-w-0 flex-col", children: [
|
|
2704
|
+
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs13("div", { className: "blade-chat-empty", children: [
|
|
2705
|
+
/* @__PURE__ */ jsx15(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
2706
|
+
/* @__PURE__ */ jsx15("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
|
|
2707
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
2138
2708
|
] }) : renderBlocks.map((block) => {
|
|
2139
2709
|
if (block.type === "message") {
|
|
2140
|
-
return /* @__PURE__ */
|
|
2710
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
2141
2711
|
}
|
|
2142
2712
|
if (block.type === "assistant_turn") {
|
|
2143
|
-
return /* @__PURE__ */
|
|
2713
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx15(
|
|
2144
2714
|
RenderErrorBoundary,
|
|
2145
2715
|
{
|
|
2146
2716
|
label: "\u52A9\u624B\u6D88\u606F",
|
|
2147
2717
|
details: block.key,
|
|
2148
2718
|
resetKey: getMessageResetSignature(block.messages),
|
|
2149
|
-
children: /* @__PURE__ */
|
|
2719
|
+
children: /* @__PURE__ */ jsx15(
|
|
2150
2720
|
AssistantTurnBlock,
|
|
2151
2721
|
{
|
|
2152
2722
|
messages: block.messages,
|
|
@@ -2162,30 +2732,30 @@ function MessageList({
|
|
|
2162
2732
|
) }, block.key);
|
|
2163
2733
|
}
|
|
2164
2734
|
if (block.type === "compaction") {
|
|
2165
|
-
return /* @__PURE__ */
|
|
2735
|
+
return /* @__PURE__ */ jsxs13(
|
|
2166
2736
|
"div",
|
|
2167
2737
|
{
|
|
2168
2738
|
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
2169
2739
|
children: [
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2740
|
+
/* @__PURE__ */ jsx15(Layers, { size: 12 }),
|
|
2741
|
+
/* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
2172
2742
|
]
|
|
2173
2743
|
},
|
|
2174
2744
|
block.key
|
|
2175
2745
|
);
|
|
2176
2746
|
}
|
|
2177
|
-
return /* @__PURE__ */
|
|
2747
|
+
return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
|
|
2178
2748
|
}),
|
|
2179
|
-
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */
|
|
2749
|
+
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx15("div", { className: "flex", children: /* @__PURE__ */ jsx15("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
|
|
2180
2750
|
] }) }) }),
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx15(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
|
|
2752
|
+
/* @__PURE__ */ jsx15(ScrollToBottomButton, {})
|
|
2183
2753
|
] }) });
|
|
2184
2754
|
}
|
|
2185
2755
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
2186
2756
|
const { scrollToBottom } = useStickToBottomContext();
|
|
2187
|
-
const previousCountRef =
|
|
2188
|
-
|
|
2757
|
+
const previousCountRef = useRef6(userMessageCount);
|
|
2758
|
+
useEffect6(() => {
|
|
2189
2759
|
if (userMessageCount > previousCountRef.current) {
|
|
2190
2760
|
scrollToBottom("instant");
|
|
2191
2761
|
}
|
|
@@ -2195,9 +2765,9 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
2195
2765
|
}
|
|
2196
2766
|
function ScrollToBottomButton() {
|
|
2197
2767
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
2198
|
-
const [visible, setVisible] =
|
|
2199
|
-
const hideTimerRef =
|
|
2200
|
-
|
|
2768
|
+
const [visible, setVisible] = useState11(false);
|
|
2769
|
+
const hideTimerRef = useRef6(null);
|
|
2770
|
+
useEffect6(() => {
|
|
2201
2771
|
if (isAtBottom) {
|
|
2202
2772
|
if (!hideTimerRef.current) {
|
|
2203
2773
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -2219,7 +2789,7 @@ function ScrollToBottomButton() {
|
|
|
2219
2789
|
}
|
|
2220
2790
|
};
|
|
2221
2791
|
}, [isAtBottom]);
|
|
2222
|
-
const handleClick =
|
|
2792
|
+
const handleClick = useCallback5(() => {
|
|
2223
2793
|
if (hideTimerRef.current) {
|
|
2224
2794
|
clearTimeout(hideTimerRef.current);
|
|
2225
2795
|
hideTimerRef.current = null;
|
|
@@ -2228,7 +2798,7 @@ function ScrollToBottomButton() {
|
|
|
2228
2798
|
scrollToBottom();
|
|
2229
2799
|
}, [scrollToBottom]);
|
|
2230
2800
|
if (!visible) return null;
|
|
2231
|
-
return /* @__PURE__ */
|
|
2801
|
+
return /* @__PURE__ */ jsxs13(
|
|
2232
2802
|
"button",
|
|
2233
2803
|
{
|
|
2234
2804
|
type: "button",
|
|
@@ -2236,34 +2806,108 @@ function ScrollToBottomButton() {
|
|
|
2236
2806
|
"aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
|
|
2237
2807
|
className: "blade-chat-scroll-bottom absolute bottom-4 right-4 flex items-center gap-1 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] shadow-lg transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
|
|
2238
2808
|
children: [
|
|
2239
|
-
/* @__PURE__ */
|
|
2240
|
-
/* @__PURE__ */
|
|
2809
|
+
/* @__PURE__ */ jsx15(ChevronDown, { size: 14 }),
|
|
2810
|
+
/* @__PURE__ */ jsx15("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
|
|
2241
2811
|
]
|
|
2242
2812
|
}
|
|
2243
2813
|
);
|
|
2244
2814
|
}
|
|
2245
2815
|
function PlanningDivider({ kind }) {
|
|
2246
|
-
return /* @__PURE__ */
|
|
2247
|
-
/* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2816
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
|
|
2817
|
+
/* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
|
|
2818
|
+
/* @__PURE__ */ jsxs13("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300", children: [
|
|
2819
|
+
/* @__PURE__ */ jsx15(Lightbulb, { size: 12 }),
|
|
2820
|
+
/* @__PURE__ */ jsx15("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
|
|
2251
2821
|
] }),
|
|
2252
|
-
/* @__PURE__ */
|
|
2822
|
+
/* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
|
|
2253
2823
|
] });
|
|
2254
2824
|
}
|
|
2255
2825
|
|
|
2256
|
-
// src/components/
|
|
2257
|
-
import { jsx as
|
|
2826
|
+
// src/components/ChatSurface.tsx
|
|
2827
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2258
2828
|
function themeAttr(theme) {
|
|
2259
2829
|
return theme === "dark" ? "dark" : void 0;
|
|
2260
2830
|
}
|
|
2831
|
+
function ChatSurface({
|
|
2832
|
+
theme,
|
|
2833
|
+
classNames,
|
|
2834
|
+
renderers,
|
|
2835
|
+
slots,
|
|
2836
|
+
placeholder,
|
|
2837
|
+
connection,
|
|
2838
|
+
errorMessage,
|
|
2839
|
+
messages,
|
|
2840
|
+
isStreaming,
|
|
2841
|
+
isStopping,
|
|
2842
|
+
inputText,
|
|
2843
|
+
onInputChange,
|
|
2844
|
+
onSend,
|
|
2845
|
+
onStop,
|
|
2846
|
+
sessionStatus,
|
|
2847
|
+
askAnswers,
|
|
2848
|
+
onAnswer,
|
|
2849
|
+
sessionId,
|
|
2850
|
+
beforeInput,
|
|
2851
|
+
banner
|
|
2852
|
+
}) {
|
|
2853
|
+
return /* @__PURE__ */ jsxs14(
|
|
2854
|
+
"div",
|
|
2855
|
+
{
|
|
2856
|
+
"data-theme": themeAttr(theme),
|
|
2857
|
+
className: cn(
|
|
2858
|
+
"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden",
|
|
2859
|
+
classNames?.root
|
|
2860
|
+
),
|
|
2861
|
+
children: [
|
|
2862
|
+
/* @__PURE__ */ jsx16(ConnectionBanner, { connection, className: classNames?.banner }),
|
|
2863
|
+
banner,
|
|
2864
|
+
errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
|
|
2865
|
+
/* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
|
|
2866
|
+
/* @__PURE__ */ jsx16("span", { children: errorMessage })
|
|
2867
|
+
] }),
|
|
2868
|
+
slots?.header,
|
|
2869
|
+
/* @__PURE__ */ jsx16(
|
|
2870
|
+
MessageList,
|
|
2871
|
+
{
|
|
2872
|
+
messages,
|
|
2873
|
+
isStreaming,
|
|
2874
|
+
sessionStatus,
|
|
2875
|
+
askAnswers,
|
|
2876
|
+
onAnswer,
|
|
2877
|
+
toolCallRenderer: renderers?.toolCall,
|
|
2878
|
+
emptyState: slots?.emptyState,
|
|
2879
|
+
className: classNames?.messageList,
|
|
2880
|
+
sessionId
|
|
2881
|
+
}
|
|
2882
|
+
),
|
|
2883
|
+
beforeInput,
|
|
2884
|
+
/* @__PURE__ */ jsx16(
|
|
2885
|
+
ChatInput,
|
|
2886
|
+
{
|
|
2887
|
+
value: inputText,
|
|
2888
|
+
onValueChange: onInputChange,
|
|
2889
|
+
onSend,
|
|
2890
|
+
onStop,
|
|
2891
|
+
isStreaming,
|
|
2892
|
+
isStopping,
|
|
2893
|
+
placeholder,
|
|
2894
|
+
className: classNames?.chatInput
|
|
2895
|
+
}
|
|
2896
|
+
),
|
|
2897
|
+
slots?.footer
|
|
2898
|
+
]
|
|
2899
|
+
}
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
// src/components/AgentChat.tsx
|
|
2904
|
+
import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2261
2905
|
function isUnauthorizedError(error) {
|
|
2262
2906
|
return error instanceof BladeApiError && error.status === 401;
|
|
2263
2907
|
}
|
|
2264
2908
|
function LoginCard({ client, onLoggedIn }) {
|
|
2265
|
-
const [loggingIn, setLoggingIn] =
|
|
2266
|
-
const [loginError, setLoginError] =
|
|
2909
|
+
const [loggingIn, setLoggingIn] = useState12(false);
|
|
2910
|
+
const [loginError, setLoginError] = useState12(null);
|
|
2267
2911
|
const handleLogin = async () => {
|
|
2268
2912
|
setLoggingIn(true);
|
|
2269
2913
|
setLoginError(null);
|
|
@@ -2276,11 +2920,11 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
2276
2920
|
setLoggingIn(false);
|
|
2277
2921
|
}
|
|
2278
2922
|
};
|
|
2279
|
-
return /* @__PURE__ */
|
|
2280
|
-
/* @__PURE__ */
|
|
2281
|
-
/* @__PURE__ */
|
|
2282
|
-
/* @__PURE__ */
|
|
2283
|
-
/* @__PURE__ */
|
|
2923
|
+
return /* @__PURE__ */ jsx17("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs15("div", { className: "flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center", children: [
|
|
2924
|
+
/* @__PURE__ */ jsx17(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
|
|
2925
|
+
/* @__PURE__ */ jsx17("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
|
|
2926
|
+
/* @__PURE__ */ jsx17("div", { className: "text-sm text-[hsl(var(--muted-foreground))]", children: "\u767B\u5F55\u540E\u5373\u53EF\u4E0E\u667A\u80FD\u4F53\u5BF9\u8BDD\uFF0C\u4F60\u7684\u4F1A\u8BDD\u5185\u5BB9\u4EC5\u81EA\u5DF1\u53EF\u89C1\u3002" }),
|
|
2927
|
+
/* @__PURE__ */ jsx17(
|
|
2284
2928
|
"button",
|
|
2285
2929
|
{
|
|
2286
2930
|
type: "button",
|
|
@@ -2290,20 +2934,20 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
2290
2934
|
children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
2291
2935
|
}
|
|
2292
2936
|
),
|
|
2293
|
-
loginError && /* @__PURE__ */
|
|
2937
|
+
loginError && /* @__PURE__ */ jsx17("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
|
|
2294
2938
|
] }) });
|
|
2295
2939
|
}
|
|
2296
|
-
function
|
|
2940
|
+
function AgentChat(props) {
|
|
2297
2941
|
const client = useBladeClient();
|
|
2298
|
-
const [attempt, setAttempt] =
|
|
2299
|
-
const [needLogin, setNeedLogin] =
|
|
2942
|
+
const [attempt, setAttempt] = useState12(0);
|
|
2943
|
+
const [needLogin, setNeedLogin] = useState12(() => !client.hasToken());
|
|
2300
2944
|
if (needLogin) {
|
|
2301
|
-
return /* @__PURE__ */
|
|
2945
|
+
return /* @__PURE__ */ jsx17(
|
|
2302
2946
|
"div",
|
|
2303
2947
|
{
|
|
2304
2948
|
"data-theme": themeAttr(props.theme),
|
|
2305
2949
|
className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
|
|
2306
|
-
children: /* @__PURE__ */
|
|
2950
|
+
children: /* @__PURE__ */ jsx17(
|
|
2307
2951
|
LoginCard,
|
|
2308
2952
|
{
|
|
2309
2953
|
client,
|
|
@@ -2316,7 +2960,7 @@ function ChatView(props) {
|
|
|
2316
2960
|
}
|
|
2317
2961
|
);
|
|
2318
2962
|
}
|
|
2319
|
-
return /* @__PURE__ */
|
|
2963
|
+
return /* @__PURE__ */ jsx17(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
|
|
2320
2964
|
}
|
|
2321
2965
|
function ChatSessionView({
|
|
2322
2966
|
sessionId,
|
|
@@ -2335,14 +2979,15 @@ function ChatSessionView({
|
|
|
2335
2979
|
createOptions,
|
|
2336
2980
|
onSessionCreated
|
|
2337
2981
|
});
|
|
2338
|
-
const
|
|
2339
|
-
const [
|
|
2340
|
-
|
|
2982
|
+
const replay = useReplay(session);
|
|
2983
|
+
const [stopRequested, setStopRequested] = useState12(false);
|
|
2984
|
+
const [inputText, setInputText] = useState12("");
|
|
2985
|
+
useEffect7(() => {
|
|
2341
2986
|
if (session) {
|
|
2342
2987
|
onSessionReady?.(session);
|
|
2343
2988
|
}
|
|
2344
2989
|
}, [session, onSessionReady]);
|
|
2345
|
-
|
|
2990
|
+
useEffect7(() => {
|
|
2346
2991
|
if (!session) return;
|
|
2347
2992
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
2348
2993
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -2358,12 +3003,12 @@ ${content}`);
|
|
|
2358
3003
|
offInsert();
|
|
2359
3004
|
};
|
|
2360
3005
|
}, [session]);
|
|
2361
|
-
|
|
3006
|
+
useEffect7(() => {
|
|
2362
3007
|
if (isUnauthorizedError(error)) {
|
|
2363
3008
|
onUnauthorized();
|
|
2364
3009
|
}
|
|
2365
3010
|
}, [error, onUnauthorized]);
|
|
2366
|
-
|
|
3011
|
+
useEffect7(() => {
|
|
2367
3012
|
if (!session || !commands) return;
|
|
2368
3013
|
const unsubscribes = Object.entries(commands).map(
|
|
2369
3014
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -2375,7 +3020,7 @@ ${content}`);
|
|
|
2375
3020
|
const isStreaming = state?.isStreaming ?? false;
|
|
2376
3021
|
const isStopping = stopRequested && isStreaming;
|
|
2377
3022
|
const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
|
|
2378
|
-
const errorMessage = connectError ?? state?.errorMessage ?? null;
|
|
3023
|
+
const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
|
|
2379
3024
|
const handleSend = (text) => {
|
|
2380
3025
|
setStopRequested(false);
|
|
2381
3026
|
return session?.send(text, { mode: state?.mode ?? void 0 }) ?? Promise.resolve(false);
|
|
@@ -2384,73 +3029,281 @@ ${content}`);
|
|
|
2384
3029
|
setStopRequested(true);
|
|
2385
3030
|
void session?.stop();
|
|
2386
3031
|
};
|
|
2387
|
-
return /* @__PURE__ */
|
|
2388
|
-
|
|
3032
|
+
return /* @__PURE__ */ jsx17(
|
|
3033
|
+
ChatSurface,
|
|
2389
3034
|
{
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
3035
|
+
theme,
|
|
3036
|
+
classNames,
|
|
3037
|
+
renderers,
|
|
3038
|
+
slots,
|
|
3039
|
+
placeholder,
|
|
3040
|
+
connection: state?.connection ?? "connecting",
|
|
3041
|
+
banner: /* @__PURE__ */ jsxs15(Fragment2, { children: [
|
|
3042
|
+
/* @__PURE__ */ jsx17(
|
|
3043
|
+
ReplayBar,
|
|
2398
3044
|
{
|
|
2399
|
-
|
|
2400
|
-
|
|
3045
|
+
isReplay: replay.isReplay,
|
|
3046
|
+
speed: replay.speed,
|
|
3047
|
+
canControl: replay.canControl,
|
|
3048
|
+
onSpeedChange: (next) => void replay.setSpeed(next),
|
|
3049
|
+
onExit: () => void replay.exitToAutonomous()
|
|
2401
3050
|
}
|
|
2402
3051
|
),
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
3052
|
+
/* @__PURE__ */ jsx17(ReplayMismatchPrompt, { mismatch: replay.mismatch })
|
|
3053
|
+
] }),
|
|
3054
|
+
errorMessage,
|
|
3055
|
+
messages: state?.messages ?? [],
|
|
3056
|
+
isStreaming,
|
|
3057
|
+
isStopping,
|
|
3058
|
+
inputText,
|
|
3059
|
+
onInputChange: setInputText,
|
|
3060
|
+
onSend: handleSend,
|
|
3061
|
+
onStop: handleStop,
|
|
3062
|
+
sessionStatus: state?.status ?? void 0,
|
|
3063
|
+
askAnswers: state?.askAnswers,
|
|
3064
|
+
onAnswer: (answer, toolCallId, answerData) => {
|
|
3065
|
+
void session?.send(answer, {
|
|
3066
|
+
mode: state?.mode ?? void 0,
|
|
3067
|
+
askUserAnswer: { tool_call_id: toolCallId, ...answerData }
|
|
3068
|
+
});
|
|
3069
|
+
},
|
|
3070
|
+
sessionId: session?.sessionId
|
|
3071
|
+
}
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
// src/components/LlmChat.tsx
|
|
3076
|
+
import { useEffect as useEffect8, useMemo as useMemo8, useState as useState14 } from "react";
|
|
3077
|
+
|
|
3078
|
+
// src/components/LlmAdvancedSettings.tsx
|
|
3079
|
+
import { useState as useState13 } from "react";
|
|
3080
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3081
|
+
var FIELDS = [
|
|
3082
|
+
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
3083
|
+
{ id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
|
|
3084
|
+
{ id: "apiKey", label: "\u5BC6\u94A5", placeholder: "\u7559\u7A7A\u8868\u793A\u4E0D\u6539", secret: true }
|
|
3085
|
+
];
|
|
3086
|
+
function normalizeAdvanced(value) {
|
|
3087
|
+
if (!value) return null;
|
|
3088
|
+
const config = value === true ? {} : value;
|
|
3089
|
+
return {
|
|
3090
|
+
baseURL: config.baseURL ?? true,
|
|
3091
|
+
model: config.model ?? true,
|
|
3092
|
+
apiKey: config.apiKey ?? false,
|
|
3093
|
+
storage: config.storage ?? "local"
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
function storageKeyFor(settings, baseURL) {
|
|
3097
|
+
const explicit = typeof settings === "object" ? settings.storageKey : void 0;
|
|
3098
|
+
return `blade-llm-override:${explicit ?? baseURL}`;
|
|
3099
|
+
}
|
|
3100
|
+
function readOverride(settings, baseURL) {
|
|
3101
|
+
const normalized = normalizeAdvanced(settings);
|
|
3102
|
+
if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return {};
|
|
3103
|
+
try {
|
|
3104
|
+
const raw = localStorage.getItem(storageKeyFor(settings, baseURL));
|
|
3105
|
+
if (!raw) return {};
|
|
3106
|
+
const stored = JSON.parse(raw);
|
|
3107
|
+
return Object.fromEntries(
|
|
3108
|
+
Object.entries(stored).filter(([key]) => normalized[key])
|
|
3109
|
+
);
|
|
3110
|
+
} catch {
|
|
3111
|
+
return {};
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
function writeOverride(settings, baseURL, override) {
|
|
3115
|
+
const normalized = normalizeAdvanced(settings);
|
|
3116
|
+
if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return;
|
|
3117
|
+
try {
|
|
3118
|
+
const key = storageKeyFor(settings, baseURL);
|
|
3119
|
+
if (Object.keys(override).length === 0) localStorage.removeItem(key);
|
|
3120
|
+
else localStorage.setItem(key, JSON.stringify(override));
|
|
3121
|
+
} catch {
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3125
|
+
const normalized = normalizeAdvanced(settings);
|
|
3126
|
+
const [open, setOpen] = useState13(false);
|
|
3127
|
+
const [draft, setDraft] = useState13(override);
|
|
3128
|
+
if (!normalized) return null;
|
|
3129
|
+
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3130
|
+
const dirty = Object.keys(override).length > 0;
|
|
3131
|
+
return /* @__PURE__ */ jsxs16("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
|
|
3132
|
+
/* @__PURE__ */ jsxs16(
|
|
3133
|
+
"button",
|
|
3134
|
+
{
|
|
3135
|
+
type: "button",
|
|
3136
|
+
onClick: () => {
|
|
3137
|
+
setDraft(override);
|
|
3138
|
+
setOpen((value) => !value);
|
|
3139
|
+
},
|
|
3140
|
+
className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
3141
|
+
children: [
|
|
3142
|
+
/* @__PURE__ */ jsx18(Settings2, { size: 13 }),
|
|
3143
|
+
"\u9AD8\u7EA7\u8BBE\u7F6E",
|
|
3144
|
+
dirty && /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
|
|
3145
|
+
]
|
|
3146
|
+
}
|
|
3147
|
+
),
|
|
3148
|
+
open && /* @__PURE__ */ jsxs16("div", { className: "mt-2 flex flex-col gap-2", children: [
|
|
3149
|
+
fields.map((field) => /* @__PURE__ */ jsxs16("label", { className: "flex flex-col gap-1", children: [
|
|
3150
|
+
/* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
|
|
3151
|
+
/* @__PURE__ */ jsx18(
|
|
3152
|
+
"input",
|
|
2410
3153
|
{
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
3154
|
+
type: field.secret ? "password" : "text",
|
|
3155
|
+
value: draft[field.id] ?? "",
|
|
3156
|
+
placeholder: field.id === "apiKey" ? field.placeholder : defaults[field.id] || field.placeholder,
|
|
3157
|
+
onChange: (event) => setDraft({ ...draft, [field.id]: event.target.value }),
|
|
3158
|
+
className: "rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none"
|
|
3159
|
+
}
|
|
3160
|
+
)
|
|
3161
|
+
] }, field.id)),
|
|
3162
|
+
normalized.apiKey && /* @__PURE__ */ jsx18("p", { className: "text-[hsl(var(--muted-foreground))]", children: "\u5BC6\u94A5\u4F1A\u5B58\u5728\u8FD9\u53F0\u6D4F\u89C8\u5668\u91CC\u3002\u53EA\u5728\u4F60\u4FE1\u5F97\u8FC7\u8FD9\u53F0\u673A\u5668\u65F6\u586B\u3002" }),
|
|
3163
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
|
|
3164
|
+
/* @__PURE__ */ jsx18(
|
|
3165
|
+
"button",
|
|
3166
|
+
{
|
|
3167
|
+
type: "button",
|
|
3168
|
+
onClick: () => {
|
|
3169
|
+
const next = Object.fromEntries(
|
|
3170
|
+
Object.entries(draft).filter(([, value]) => String(value ?? "").trim() !== "")
|
|
3171
|
+
);
|
|
3172
|
+
onChange(next);
|
|
3173
|
+
setOpen(false);
|
|
2420
3174
|
},
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
className: classNames?.messageList,
|
|
2424
|
-
sessionId: session?.sessionId
|
|
3175
|
+
className: "rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]",
|
|
3176
|
+
children: "\u4FDD\u5B58"
|
|
2425
3177
|
}
|
|
2426
3178
|
),
|
|
2427
|
-
/* @__PURE__ */
|
|
2428
|
-
|
|
3179
|
+
/* @__PURE__ */ jsx18(
|
|
3180
|
+
"button",
|
|
2429
3181
|
{
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
3182
|
+
type: "button",
|
|
3183
|
+
onClick: () => {
|
|
3184
|
+
setDraft({});
|
|
3185
|
+
onChange({});
|
|
3186
|
+
setOpen(false);
|
|
3187
|
+
},
|
|
3188
|
+
className: "rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]",
|
|
3189
|
+
children: "\u6062\u590D\u9ED8\u8BA4"
|
|
2438
3190
|
}
|
|
2439
|
-
)
|
|
2440
|
-
|
|
2441
|
-
|
|
3191
|
+
)
|
|
3192
|
+
] })
|
|
3193
|
+
] })
|
|
3194
|
+
] });
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// src/components/LlmChat.tsx
|
|
3198
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
3199
|
+
function LlmChat({
|
|
3200
|
+
classNames,
|
|
3201
|
+
renderers,
|
|
3202
|
+
slots,
|
|
3203
|
+
placeholder,
|
|
3204
|
+
theme,
|
|
3205
|
+
onReady,
|
|
3206
|
+
advanced,
|
|
3207
|
+
onOverrideChange,
|
|
3208
|
+
...options
|
|
3209
|
+
}) {
|
|
3210
|
+
const [override, setOverride] = useState14(() => readOverride(advanced, options.baseURL));
|
|
3211
|
+
const effective = { ...options, ...override };
|
|
3212
|
+
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3213
|
+
const [inputText, setInputText] = useState14("");
|
|
3214
|
+
const [stopRequested, setStopRequested] = useState14(false);
|
|
3215
|
+
const handle = useMemo8(
|
|
3216
|
+
() => ({
|
|
3217
|
+
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
3218
|
+
${text}` : text),
|
|
3219
|
+
send,
|
|
3220
|
+
reset
|
|
3221
|
+
}),
|
|
3222
|
+
[send, reset]
|
|
3223
|
+
);
|
|
3224
|
+
useEffect8(() => {
|
|
3225
|
+
onReady?.(handle);
|
|
3226
|
+
}, [handle, onReady]);
|
|
3227
|
+
return /* @__PURE__ */ jsx19(
|
|
3228
|
+
ChatSurface,
|
|
3229
|
+
{
|
|
3230
|
+
theme,
|
|
3231
|
+
classNames,
|
|
3232
|
+
renderers,
|
|
3233
|
+
slots,
|
|
3234
|
+
placeholder,
|
|
3235
|
+
connection: "connected",
|
|
3236
|
+
errorMessage: error,
|
|
3237
|
+
messages,
|
|
3238
|
+
isStreaming,
|
|
3239
|
+
isStopping: stopRequested && isStreaming,
|
|
3240
|
+
inputText,
|
|
3241
|
+
onInputChange: setInputText,
|
|
3242
|
+
onSend: async (text) => {
|
|
3243
|
+
setStopRequested(false);
|
|
3244
|
+
if (!text.trim() || isStreaming) return false;
|
|
3245
|
+
void send(text);
|
|
3246
|
+
return true;
|
|
3247
|
+
},
|
|
3248
|
+
onStop: () => {
|
|
3249
|
+
setStopRequested(true);
|
|
3250
|
+
stop();
|
|
3251
|
+
},
|
|
3252
|
+
beforeInput: advanced ? /* @__PURE__ */ jsx19(
|
|
3253
|
+
LlmAdvancedSettingsBar,
|
|
3254
|
+
{
|
|
3255
|
+
settings: advanced,
|
|
3256
|
+
defaults: { baseURL: options.baseURL, model: options.model },
|
|
3257
|
+
override,
|
|
3258
|
+
onChange: (next) => {
|
|
3259
|
+
setOverride(next);
|
|
3260
|
+
writeOverride(advanced, options.baseURL, next);
|
|
3261
|
+
onOverrideChange?.(next);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
) : void 0
|
|
2442
3265
|
}
|
|
2443
3266
|
);
|
|
2444
3267
|
}
|
|
2445
3268
|
|
|
3269
|
+
// src/components/ChatView.tsx
|
|
3270
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
3271
|
+
function ChatView(props) {
|
|
3272
|
+
const { mode, llm, onLlmReady, ...rest } = props;
|
|
3273
|
+
if (mode === "llm") {
|
|
3274
|
+
if (!llm) {
|
|
3275
|
+
throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
|
|
3276
|
+
}
|
|
3277
|
+
return /* @__PURE__ */ jsx20(
|
|
3278
|
+
LlmChat,
|
|
3279
|
+
{
|
|
3280
|
+
...llm,
|
|
3281
|
+
classNames: rest.classNames,
|
|
3282
|
+
renderers: rest.renderers,
|
|
3283
|
+
slots: rest.slots,
|
|
3284
|
+
placeholder: rest.placeholder,
|
|
3285
|
+
theme: rest.theme,
|
|
3286
|
+
onReady: onLlmReady
|
|
3287
|
+
}
|
|
3288
|
+
);
|
|
3289
|
+
}
|
|
3290
|
+
return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
|
|
3291
|
+
}
|
|
3292
|
+
|
|
2446
3293
|
// src/index.ts
|
|
2447
3294
|
export * from "@blade-hq/agent-client";
|
|
2448
3295
|
export {
|
|
3296
|
+
AgentChat,
|
|
2449
3297
|
BladeProvider,
|
|
2450
3298
|
ChatView,
|
|
3299
|
+
LlmChat,
|
|
2451
3300
|
MarkdownContent,
|
|
3301
|
+
ReplayBar,
|
|
3302
|
+
ReplayMismatchPrompt,
|
|
2452
3303
|
useAgentSession,
|
|
2453
|
-
useBladeClient
|
|
3304
|
+
useBladeClient,
|
|
3305
|
+
useLlmChat,
|
|
3306
|
+
useReplay
|
|
2454
3307
|
};
|
|
2455
3308
|
/*! Bundled license information:
|
|
2456
3309
|
|
|
@@ -2481,6 +3334,8 @@ lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
|
2481
3334
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
2482
3335
|
lucide-react/dist/esm/icons/message-square.js:
|
|
2483
3336
|
lucide-react/dist/esm/icons/music.js:
|
|
3337
|
+
lucide-react/dist/esm/icons/play.js:
|
|
3338
|
+
lucide-react/dist/esm/icons/settings-2.js:
|
|
2484
3339
|
lucide-react/dist/esm/icons/square.js:
|
|
2485
3340
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
2486
3341
|
lucide-react/dist/esm/icons/x.js:
|