@blade-hq/agent-react 2608.0.8 → 2608.0.9
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 +151 -2
- package/dist/components/AgentChat.d.ts +18 -0
- package/dist/components/AssistantTurnBlock.d.ts +1 -1
- package/dist/components/ChatSurface.d.ts +59 -0
- package/dist/components/ChatView.d.ts +19 -39
- package/dist/components/ConnectionBanner.d.ts +1 -1
- package/dist/components/FileCard.d.ts +3 -3
- package/dist/components/LlmAdvancedSettings.d.ts +40 -0
- package/dist/components/LlmChat.d.ts +29 -0
- package/dist/components/MarkdownContent.d.ts +2 -2
- package/dist/components/MessageList.d.ts +1 -1
- package/dist/components/ReplayBar.d.ts +13 -0
- package/dist/components/ReplayMismatchPrompt.d.ts +8 -0
- package/dist/embed/entry.d.ts +2 -3
- 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 +1221 -329
- package/dist/index.js.map +1 -1
- package/dist/lib/media-tags.d.ts +3 -3
- package/dist/style.css +38 -11
- package/dist/style.full.css +39 -12
- package/package.json +2 -2
- package/public-api.md +255 -25
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
|
|
@@ -173,6 +618,13 @@ var createLucideIcon = (iconName, iconNode) => {
|
|
|
173
618
|
return Component2;
|
|
174
619
|
};
|
|
175
620
|
|
|
621
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/archive.js
|
|
622
|
+
var Archive = createLucideIcon("Archive", [
|
|
623
|
+
["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
|
|
624
|
+
["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
|
|
625
|
+
["path", { d: "M10 12h4", key: "a56b0p" }]
|
|
626
|
+
]);
|
|
627
|
+
|
|
176
628
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
|
|
177
629
|
var ArrowUp = createLucideIcon("ArrowUp", [
|
|
178
630
|
["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
|
|
@@ -240,6 +692,31 @@ var Copy = createLucideIcon("Copy", [
|
|
|
240
692
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
241
693
|
]);
|
|
242
694
|
|
|
695
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/download.js
|
|
696
|
+
var Download = createLucideIcon("Download", [
|
|
697
|
+
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
|
|
698
|
+
["polyline", { points: "7 10 12 15 17 10", key: "2ggqvy" }],
|
|
699
|
+
["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
|
|
700
|
+
]);
|
|
701
|
+
|
|
702
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-code-2.js
|
|
703
|
+
var FileCode2 = createLucideIcon("FileCode2", [
|
|
704
|
+
["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }],
|
|
705
|
+
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
|
|
706
|
+
["path", { d: "m5 12-3 3 3 3", key: "oke12k" }],
|
|
707
|
+
["path", { d: "m9 18 3-3-3-3", key: "112psh" }]
|
|
708
|
+
]);
|
|
709
|
+
|
|
710
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js
|
|
711
|
+
var FileSpreadsheet = createLucideIcon("FileSpreadsheet", [
|
|
712
|
+
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
713
|
+
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
|
|
714
|
+
["path", { d: "M8 13h2", key: "yr2amv" }],
|
|
715
|
+
["path", { d: "M14 13h2", key: "un5t4a" }],
|
|
716
|
+
["path", { d: "M8 17h2", key: "2yhykz" }],
|
|
717
|
+
["path", { d: "M14 17h2", key: "10kma7" }]
|
|
718
|
+
]);
|
|
719
|
+
|
|
243
720
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
244
721
|
var FileText = createLucideIcon("FileText", [
|
|
245
722
|
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
@@ -249,6 +726,31 @@ var FileText = createLucideIcon("FileText", [
|
|
|
249
726
|
["path", { d: "M16 17H8", key: "z1uh3a" }]
|
|
250
727
|
]);
|
|
251
728
|
|
|
729
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file.js
|
|
730
|
+
var File = createLucideIcon("File", [
|
|
731
|
+
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
732
|
+
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
733
|
+
]);
|
|
734
|
+
|
|
735
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/film.js
|
|
736
|
+
var Film = createLucideIcon("Film", [
|
|
737
|
+
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
|
|
738
|
+
["path", { d: "M7 3v18", key: "bbkbws" }],
|
|
739
|
+
["path", { d: "M3 7.5h4", key: "zfgn84" }],
|
|
740
|
+
["path", { d: "M3 12h18", key: "1i2n21" }],
|
|
741
|
+
["path", { d: "M3 16.5h4", key: "1230mu" }],
|
|
742
|
+
["path", { d: "M17 3v18", key: "in4fa5" }],
|
|
743
|
+
["path", { d: "M17 7.5h4", key: "myr1c1" }],
|
|
744
|
+
["path", { d: "M17 16.5h4", key: "go4c1d" }]
|
|
745
|
+
]);
|
|
746
|
+
|
|
747
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/image.js
|
|
748
|
+
var Image = createLucideIcon("Image", [
|
|
749
|
+
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
|
|
750
|
+
["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
|
|
751
|
+
["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
|
|
752
|
+
]);
|
|
753
|
+
|
|
252
754
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
|
|
253
755
|
var Layers = createLucideIcon("Layers", [
|
|
254
756
|
[
|
|
@@ -312,6 +814,26 @@ var MessageSquare = createLucideIcon("MessageSquare", [
|
|
|
312
814
|
["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
|
|
313
815
|
]);
|
|
314
816
|
|
|
817
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/music.js
|
|
818
|
+
var Music = createLucideIcon("Music", [
|
|
819
|
+
["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
|
|
820
|
+
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
821
|
+
["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
|
|
822
|
+
]);
|
|
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
|
+
|
|
315
837
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
|
|
316
838
|
var Square = createLucideIcon("Square", [
|
|
317
839
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
@@ -336,8 +858,8 @@ var X = createLucideIcon("X", [
|
|
|
336
858
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
337
859
|
]);
|
|
338
860
|
|
|
339
|
-
// src/components/
|
|
340
|
-
import { useEffect as useEffect7, useState as
|
|
861
|
+
// src/components/AgentChat.tsx
|
|
862
|
+
import { useEffect as useEffect7, useState as useState12 } from "react";
|
|
341
863
|
|
|
342
864
|
// src/lib/utils.ts
|
|
343
865
|
function cn(...inputs) {
|
|
@@ -352,8 +874,120 @@ async function copyToClipboard(text) {
|
|
|
352
874
|
}
|
|
353
875
|
}
|
|
354
876
|
|
|
355
|
-
// src/components/
|
|
877
|
+
// src/components/ReplayBar.tsx
|
|
356
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";
|
|
357
991
|
function ChatInput({
|
|
358
992
|
value,
|
|
359
993
|
onValueChange,
|
|
@@ -378,8 +1012,8 @@ function ChatInput({
|
|
|
378
1012
|
void handleSend();
|
|
379
1013
|
}
|
|
380
1014
|
};
|
|
381
|
-
return /* @__PURE__ */
|
|
382
|
-
/* @__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(
|
|
383
1017
|
"textarea",
|
|
384
1018
|
{
|
|
385
1019
|
value,
|
|
@@ -396,7 +1030,7 @@ function ChatInput({
|
|
|
396
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)]"
|
|
397
1031
|
}
|
|
398
1032
|
),
|
|
399
|
-
isStreaming ? /* @__PURE__ */
|
|
1033
|
+
isStreaming ? /* @__PURE__ */ jsx4(
|
|
400
1034
|
"button",
|
|
401
1035
|
{
|
|
402
1036
|
type: "button",
|
|
@@ -405,9 +1039,9 @@ function ChatInput({
|
|
|
405
1039
|
"aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
406
1040
|
title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
407
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",
|
|
408
|
-
children: isStopping ? /* @__PURE__ */
|
|
1042
|
+
children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
|
|
409
1043
|
}
|
|
410
|
-
) : /* @__PURE__ */
|
|
1044
|
+
) : /* @__PURE__ */ jsx4(
|
|
411
1045
|
"button",
|
|
412
1046
|
{
|
|
413
1047
|
type: "button",
|
|
@@ -416,81 +1050,31 @@ function ChatInput({
|
|
|
416
1050
|
"aria-label": "\u53D1\u9001\u6D88\u606F",
|
|
417
1051
|
title: "\u53D1\u9001\u6D88\u606F",
|
|
418
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",
|
|
419
|
-
children: /* @__PURE__ */
|
|
1053
|
+
children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
|
|
420
1054
|
}
|
|
421
1055
|
)
|
|
422
1056
|
] }) });
|
|
423
1057
|
}
|
|
424
1058
|
|
|
425
1059
|
// src/components/ConnectionBanner.tsx
|
|
426
|
-
import {
|
|
427
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
428
|
-
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
429
|
-
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
430
|
-
function useConnectionNoticePhase(connected) {
|
|
431
|
-
const [phase, setPhase] = useState2("hidden");
|
|
432
|
-
const connectedRef = useRef2(connected);
|
|
433
|
-
const timersRef = useRef2([]);
|
|
434
|
-
connectedRef.current = connected;
|
|
435
|
-
useEffect2(() => {
|
|
436
|
-
const clearTimers = () => {
|
|
437
|
-
for (const timer of timersRef.current) clearTimeout(timer);
|
|
438
|
-
timersRef.current = [];
|
|
439
|
-
};
|
|
440
|
-
const startGracePeriod = () => {
|
|
441
|
-
clearTimers();
|
|
442
|
-
setPhase("hidden");
|
|
443
|
-
timersRef.current = [
|
|
444
|
-
setTimeout(() => setPhase("recovering"), CONNECTION_NOTICE_DELAY_MS),
|
|
445
|
-
setTimeout(() => setPhase("failed"), CONNECTION_ERROR_DELAY_MS)
|
|
446
|
-
];
|
|
447
|
-
};
|
|
448
|
-
if (connected) {
|
|
449
|
-
clearTimers();
|
|
450
|
-
setPhase("hidden");
|
|
451
|
-
} else {
|
|
452
|
-
startGracePeriod();
|
|
453
|
-
}
|
|
454
|
-
const handleForeground = () => {
|
|
455
|
-
if (!connectedRef.current) startGracePeriod();
|
|
456
|
-
};
|
|
457
|
-
const handleVisibilityChange = () => {
|
|
458
|
-
if (document.visibilityState === "visible") handleForeground();
|
|
459
|
-
};
|
|
460
|
-
window.addEventListener("blade:app-active", handleForeground);
|
|
461
|
-
window.addEventListener("focus", handleForeground);
|
|
462
|
-
window.addEventListener("pageshow", handleForeground);
|
|
463
|
-
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
464
|
-
return () => {
|
|
465
|
-
clearTimers();
|
|
466
|
-
window.removeEventListener("blade:app-active", handleForeground);
|
|
467
|
-
window.removeEventListener("focus", handleForeground);
|
|
468
|
-
window.removeEventListener("pageshow", handleForeground);
|
|
469
|
-
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
470
|
-
};
|
|
471
|
-
}, [connected]);
|
|
472
|
-
return phase;
|
|
473
|
-
}
|
|
1060
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
474
1061
|
function ConnectionBanner({ connection, className }) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
const recovering = phase === "recovering";
|
|
481
|
-
const firstConnection = !hasConnectedRef.current;
|
|
482
|
-
return /* @__PURE__ */ jsx3("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs2(
|
|
1062
|
+
if (connection === "connected" || connection === "connecting") {
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
const reconnecting = connection === "reconnecting";
|
|
1066
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
483
1067
|
"div",
|
|
484
1068
|
{
|
|
485
1069
|
className: cn(
|
|
486
1070
|
"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
|
|
487
|
-
|
|
1071
|
+
reconnecting ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
488
1072
|
),
|
|
489
1073
|
children: [
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
/* @__PURE__ */
|
|
492
|
-
/* @__PURE__ */
|
|
493
|
-
/* @__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" })
|
|
494
1078
|
] })
|
|
495
1079
|
]
|
|
496
1080
|
}
|
|
@@ -499,10 +1083,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
499
1083
|
|
|
500
1084
|
// src/components/MessageList.tsx
|
|
501
1085
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
502
|
-
import { useCallback as
|
|
1086
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef6, useState as useState11 } from "react";
|
|
503
1087
|
|
|
504
1088
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
505
|
-
import { useCallback, useMemo as
|
|
1089
|
+
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
|
|
506
1090
|
var DEFAULT_SPRING_ANIMATION = {
|
|
507
1091
|
/**
|
|
508
1092
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -539,12 +1123,12 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
539
1123
|
mouseDown = false;
|
|
540
1124
|
});
|
|
541
1125
|
var useStickToBottom = (options = {}) => {
|
|
542
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
543
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
544
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1126
|
+
const [escapedFromLock, updateEscapedFromLock] = useState4(false);
|
|
1127
|
+
const [isAtBottom, updateIsAtBottom] = useState4(options.initial !== false);
|
|
1128
|
+
const [isNearBottom, setIsNearBottom] = useState4(false);
|
|
545
1129
|
const optionsRef = useRef3(null);
|
|
546
1130
|
optionsRef.current = options;
|
|
547
|
-
const isSelecting =
|
|
1131
|
+
const isSelecting = useCallback3(() => {
|
|
548
1132
|
if (!mouseDown) {
|
|
549
1133
|
return false;
|
|
550
1134
|
}
|
|
@@ -555,15 +1139,15 @@ var useStickToBottom = (options = {}) => {
|
|
|
555
1139
|
const range = selection.getRangeAt(0);
|
|
556
1140
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
557
1141
|
}, []);
|
|
558
|
-
const setIsAtBottom =
|
|
1142
|
+
const setIsAtBottom = useCallback3((isAtBottom2) => {
|
|
559
1143
|
state.isAtBottom = isAtBottom2;
|
|
560
1144
|
updateIsAtBottom(isAtBottom2);
|
|
561
1145
|
}, []);
|
|
562
|
-
const setEscapedFromLock =
|
|
1146
|
+
const setEscapedFromLock = useCallback3((escapedFromLock2) => {
|
|
563
1147
|
state.escapedFromLock = escapedFromLock2;
|
|
564
1148
|
updateEscapedFromLock(escapedFromLock2);
|
|
565
1149
|
}, []);
|
|
566
|
-
const state =
|
|
1150
|
+
const state = useMemo3(() => {
|
|
567
1151
|
let lastCalculation;
|
|
568
1152
|
return {
|
|
569
1153
|
escapedFromLock,
|
|
@@ -616,7 +1200,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
616
1200
|
}
|
|
617
1201
|
};
|
|
618
1202
|
}, []);
|
|
619
|
-
const scrollToBottom =
|
|
1203
|
+
const scrollToBottom = useCallback3((scrollOptions = {}) => {
|
|
620
1204
|
if (typeof scrollOptions === "string") {
|
|
621
1205
|
scrollOptions = { animation: scrollOptions };
|
|
622
1206
|
}
|
|
@@ -701,11 +1285,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
701
1285
|
}
|
|
702
1286
|
return next();
|
|
703
1287
|
}, [setIsAtBottom, isSelecting, state]);
|
|
704
|
-
const stopScroll =
|
|
1288
|
+
const stopScroll = useCallback3(() => {
|
|
705
1289
|
setEscapedFromLock(true);
|
|
706
1290
|
setIsAtBottom(false);
|
|
707
1291
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
708
|
-
const handleScroll =
|
|
1292
|
+
const handleScroll = useCallback3(({ target }) => {
|
|
709
1293
|
if (target !== scrollRef.current) {
|
|
710
1294
|
return;
|
|
711
1295
|
}
|
|
@@ -744,7 +1328,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
744
1328
|
}
|
|
745
1329
|
}, 1);
|
|
746
1330
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
747
|
-
const handleWheel =
|
|
1331
|
+
const handleWheel = useCallback3(({ target, deltaY }) => {
|
|
748
1332
|
let element = target;
|
|
749
1333
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
750
1334
|
if (!element.parentElement) {
|
|
@@ -814,7 +1398,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
814
1398
|
};
|
|
815
1399
|
};
|
|
816
1400
|
function useRefCallback(callback, deps) {
|
|
817
|
-
const result =
|
|
1401
|
+
const result = useCallback3((ref) => {
|
|
818
1402
|
result.current = ref;
|
|
819
1403
|
return callback(ref);
|
|
820
1404
|
}, deps);
|
|
@@ -846,7 +1430,7 @@ function mergeAnimations(...animations) {
|
|
|
846
1430
|
|
|
847
1431
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
848
1432
|
import * as React from "react";
|
|
849
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as
|
|
1433
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef4 } from "react";
|
|
850
1434
|
var StickToBottomContext = createContext2(null);
|
|
851
1435
|
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
|
|
852
1436
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
@@ -864,7 +1448,7 @@ function StickToBottom({ instance, children, resize, initial, mass, damping, sti
|
|
|
864
1448
|
targetScrollTop
|
|
865
1449
|
});
|
|
866
1450
|
const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state } = instance ?? defaultInstance;
|
|
867
|
-
const context =
|
|
1451
|
+
const context = useMemo4(() => ({
|
|
868
1452
|
scrollToBottom,
|
|
869
1453
|
stopScroll,
|
|
870
1454
|
scrollRef,
|
|
@@ -927,10 +1511,10 @@ function useStickToBottomContext() {
|
|
|
927
1511
|
|
|
928
1512
|
// src/components/AssistantTurnBlock.tsx
|
|
929
1513
|
import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
|
|
930
|
-
import { useState as
|
|
1514
|
+
import { useState as useState10 } from "react";
|
|
931
1515
|
|
|
932
1516
|
// src/components/AgentLoopBlock.tsx
|
|
933
|
-
import { useState as
|
|
1517
|
+
import { useState as useState5 } from "react";
|
|
934
1518
|
|
|
935
1519
|
// src/components/display-utils.ts
|
|
936
1520
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1052,7 +1636,7 @@ function formatToolResult(result) {
|
|
|
1052
1636
|
}
|
|
1053
1637
|
|
|
1054
1638
|
// src/components/AgentLoopBlock.tsx
|
|
1055
|
-
import { jsx as
|
|
1639
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1056
1640
|
function parseAgentDescription(argumentsJson) {
|
|
1057
1641
|
try {
|
|
1058
1642
|
const parsed = JSON.parse(argumentsJson);
|
|
@@ -1062,12 +1646,12 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1062
1646
|
}
|
|
1063
1647
|
}
|
|
1064
1648
|
function AgentLoopBlock({ toolCall }) {
|
|
1065
|
-
const [expanded, setExpanded] =
|
|
1649
|
+
const [expanded, setExpanded] = useState5(false);
|
|
1066
1650
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1067
1651
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1068
1652
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1069
|
-
return /* @__PURE__ */
|
|
1070
|
-
/* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
|
|
1654
|
+
/* @__PURE__ */ jsxs5(
|
|
1071
1655
|
"div",
|
|
1072
1656
|
{
|
|
1073
1657
|
className: cn(
|
|
@@ -1075,7 +1659,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1075
1659
|
failed ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : running ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]"
|
|
1076
1660
|
),
|
|
1077
1661
|
children: [
|
|
1078
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsxs5(
|
|
1079
1663
|
"button",
|
|
1080
1664
|
{
|
|
1081
1665
|
type: "button",
|
|
@@ -1083,7 +1667,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1083
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",
|
|
1084
1668
|
"aria-expanded": expanded,
|
|
1085
1669
|
children: [
|
|
1086
|
-
/* @__PURE__ */
|
|
1670
|
+
/* @__PURE__ */ jsx6(
|
|
1087
1671
|
ChevronRight,
|
|
1088
1672
|
{
|
|
1089
1673
|
size: 11,
|
|
@@ -1093,8 +1677,8 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1093
1677
|
)
|
|
1094
1678
|
}
|
|
1095
1679
|
),
|
|
1096
|
-
/* @__PURE__ */
|
|
1097
|
-
/* @__PURE__ */
|
|
1680
|
+
/* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1681
|
+
/* @__PURE__ */ jsxs5(
|
|
1098
1682
|
"span",
|
|
1099
1683
|
{
|
|
1100
1684
|
className: cn(
|
|
@@ -1102,25 +1686,25 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1102
1686
|
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1103
1687
|
),
|
|
1104
1688
|
children: [
|
|
1105
|
-
running ? /* @__PURE__ */
|
|
1106
|
-
/* @__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" })
|
|
1107
1691
|
]
|
|
1108
1692
|
}
|
|
1109
1693
|
),
|
|
1110
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1111
1695
|
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1112
1696
|
description
|
|
1113
1697
|
] })
|
|
1114
1698
|
]
|
|
1115
1699
|
}
|
|
1116
1700
|
),
|
|
1117
|
-
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) })
|
|
1118
1702
|
]
|
|
1119
1703
|
}
|
|
1120
1704
|
),
|
|
1121
|
-
expanded && toolCall.result != null && /* @__PURE__ */
|
|
1122
|
-
/* @__PURE__ */
|
|
1123
|
-
/* @__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) })
|
|
1124
1708
|
] })
|
|
1125
1709
|
] });
|
|
1126
1710
|
}
|
|
@@ -1128,9 +1712,9 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1128
1712
|
// src/components/MarkdownContent.tsx
|
|
1129
1713
|
import {
|
|
1130
1714
|
useEffect as useEffect4,
|
|
1131
|
-
useMemo as
|
|
1715
|
+
useMemo as useMemo5,
|
|
1132
1716
|
useRef as useRef5,
|
|
1133
|
-
useState as
|
|
1717
|
+
useState as useState7
|
|
1134
1718
|
} from "react";
|
|
1135
1719
|
|
|
1136
1720
|
// src/lib/media-tags.ts
|
|
@@ -1177,8 +1761,26 @@ function collapseMediaTags(text, options) {
|
|
|
1177
1761
|
}
|
|
1178
1762
|
|
|
1179
1763
|
// src/components/FileCard.tsx
|
|
1180
|
-
import { useState as
|
|
1181
|
-
import { jsx as
|
|
1764
|
+
import { useState as useState6 } from "react";
|
|
1765
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1766
|
+
var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
|
|
1767
|
+
var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
|
|
1768
|
+
var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
|
|
1769
|
+
var CODE_EXTS = /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx", "py", "go", "json", "yaml", "yml", "html", "css"]);
|
|
1770
|
+
var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "mkv", "avi"]);
|
|
1771
|
+
var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "ogg", "m4a", "flac"]);
|
|
1772
|
+
var ARCHIVE_EXTS = /* @__PURE__ */ new Set(["zip", "tar", "gz", "tgz", "rar", "7z"]);
|
|
1773
|
+
function getFileIcon(name) {
|
|
1774
|
+
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
1775
|
+
if (IMAGE_EXTS.has(ext)) return Image;
|
|
1776
|
+
if (SHEET_EXTS.has(ext)) return FileSpreadsheet;
|
|
1777
|
+
if (TEXT_EXTS.has(ext)) return FileText;
|
|
1778
|
+
if (CODE_EXTS.has(ext)) return FileCode2;
|
|
1779
|
+
if (VIDEO_EXTS.has(ext)) return Film;
|
|
1780
|
+
if (AUDIO_EXTS.has(ext)) return Music;
|
|
1781
|
+
if (ARCHIVE_EXTS.has(ext)) return Archive;
|
|
1782
|
+
return File;
|
|
1783
|
+
}
|
|
1182
1784
|
function stringValue(value) {
|
|
1183
1785
|
return typeof value === "string" ? value : void 0;
|
|
1184
1786
|
}
|
|
@@ -1202,18 +1804,14 @@ function FileCard({
|
|
|
1202
1804
|
...props
|
|
1203
1805
|
}) {
|
|
1204
1806
|
const client = useBladeClient();
|
|
1205
|
-
const [downloading, setDownloading] =
|
|
1206
|
-
const [failed, setFailed] =
|
|
1807
|
+
const [downloading, setDownloading] = useState6(false);
|
|
1808
|
+
const [failed, setFailed] = useState6(false);
|
|
1207
1809
|
const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
|
|
1208
1810
|
const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
|
|
1209
1811
|
const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
|
|
1210
1812
|
const disabled = !sessionId || !isSafeMediaPath(path);
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
);
|
|
1214
|
-
const handleDownload = async (event) => {
|
|
1215
|
-
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
1216
|
-
event.preventDefault();
|
|
1813
|
+
const Icon2 = getFileIcon(name);
|
|
1814
|
+
const handleDownload = async () => {
|
|
1217
1815
|
if (disabled || downloading || !sessionId) return;
|
|
1218
1816
|
setDownloading(true);
|
|
1219
1817
|
setFailed(false);
|
|
@@ -1225,32 +1823,33 @@ function FileCard({
|
|
|
1225
1823
|
setDownloading(false);
|
|
1226
1824
|
}
|
|
1227
1825
|
};
|
|
1228
|
-
return /* @__PURE__ */
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
"
|
|
1826
|
+
return /* @__PURE__ */ jsxs6("span", { ...props, className: cn("blade-chat-file-card", className), children: [
|
|
1827
|
+
/* @__PURE__ */ jsxs6(
|
|
1828
|
+
"button",
|
|
1231
1829
|
{
|
|
1232
|
-
|
|
1233
|
-
download: name,
|
|
1830
|
+
type: "button",
|
|
1234
1831
|
onClick: handleDownload,
|
|
1235
|
-
|
|
1236
|
-
"aria-busy": downloading || void 0,
|
|
1832
|
+
disabled: disabled || downloading,
|
|
1237
1833
|
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
|
|
1238
|
-
title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : name
|
|
1239
|
-
|
|
1240
|
-
|
|
1834
|
+
title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : `\u4E0B\u8F7D ${name}`,
|
|
1835
|
+
children: [
|
|
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" })
|
|
1839
|
+
]
|
|
1241
1840
|
}
|
|
1242
1841
|
),
|
|
1243
|
-
failed && /* @__PURE__ */
|
|
1842
|
+
failed && /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
|
|
1244
1843
|
] });
|
|
1245
1844
|
}
|
|
1246
1845
|
|
|
1247
1846
|
// src/components/MarkdownContent.tsx
|
|
1248
|
-
import { jsx as
|
|
1847
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1249
1848
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1250
1849
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1251
1850
|
const preRef = useRef5(null);
|
|
1252
|
-
const [copied, setCopied] =
|
|
1253
|
-
const [language, setLanguage] =
|
|
1851
|
+
const [copied, setCopied] = useState7(false);
|
|
1852
|
+
const [language, setLanguage] = useState7("");
|
|
1254
1853
|
useEffect4(() => {
|
|
1255
1854
|
const codeEl = preRef.current?.querySelector("code");
|
|
1256
1855
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
@@ -1262,10 +1861,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1262
1861
|
setTimeout(() => setCopied(false), 2e3);
|
|
1263
1862
|
}
|
|
1264
1863
|
};
|
|
1265
|
-
return /* @__PURE__ */
|
|
1266
|
-
/* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */
|
|
1268
|
-
/* @__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(
|
|
1269
1868
|
"button",
|
|
1270
1869
|
{
|
|
1271
1870
|
type: "button",
|
|
@@ -1275,13 +1874,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1275
1874
|
copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
|
|
1276
1875
|
),
|
|
1277
1876
|
children: [
|
|
1278
|
-
copied ? /* @__PURE__ */
|
|
1279
|
-
/* @__PURE__ */
|
|
1877
|
+
copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
|
|
1878
|
+
/* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
|
|
1280
1879
|
]
|
|
1281
1880
|
}
|
|
1282
1881
|
)
|
|
1283
1882
|
] }),
|
|
1284
|
-
/* @__PURE__ */
|
|
1883
|
+
/* @__PURE__ */ jsx8(
|
|
1285
1884
|
"pre",
|
|
1286
1885
|
{
|
|
1287
1886
|
ref: preRef,
|
|
@@ -1293,7 +1892,7 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1293
1892
|
] });
|
|
1294
1893
|
}
|
|
1295
1894
|
function ExternalAnchor({ node: _node, children, ...props }) {
|
|
1296
|
-
return /* @__PURE__ */
|
|
1895
|
+
return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
|
|
1297
1896
|
}
|
|
1298
1897
|
var MARKDOWN_COMPONENTS = {
|
|
1299
1898
|
pre: CodeBlockPre,
|
|
@@ -1304,19 +1903,19 @@ var CUSTOM_ALLOWED_TAGS = {
|
|
|
1304
1903
|
};
|
|
1305
1904
|
function MarkdownContent({ children, className, mode, sessionId }) {
|
|
1306
1905
|
const streaming = mode === "streaming";
|
|
1307
|
-
const resolvedChildren =
|
|
1906
|
+
const resolvedChildren = useMemo5(() => {
|
|
1308
1907
|
const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
|
|
1309
1908
|
if (!sessionId) return withoutReminders;
|
|
1310
1909
|
return collapseMediaTags(withoutReminders, { streaming });
|
|
1311
1910
|
}, [children, sessionId, streaming]);
|
|
1312
|
-
const components =
|
|
1911
|
+
const components = useMemo5(
|
|
1313
1912
|
() => ({
|
|
1314
1913
|
...MARKDOWN_COMPONENTS,
|
|
1315
|
-
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */
|
|
1914
|
+
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx8(FileCard, { ...fileCardProps, sessionId })
|
|
1316
1915
|
}),
|
|
1317
1916
|
[sessionId]
|
|
1318
1917
|
);
|
|
1319
|
-
return /* @__PURE__ */
|
|
1918
|
+
return /* @__PURE__ */ jsx8(
|
|
1320
1919
|
_r,
|
|
1321
1920
|
{
|
|
1322
1921
|
className: cn("blade-chat-markdown break-words", className),
|
|
@@ -1330,17 +1929,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
|
|
|
1330
1929
|
}
|
|
1331
1930
|
|
|
1332
1931
|
// src/components/Shimmer.tsx
|
|
1333
|
-
import { jsx as
|
|
1932
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1334
1933
|
function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
1335
|
-
return /* @__PURE__ */
|
|
1934
|
+
return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
|
|
1336
1935
|
}
|
|
1337
1936
|
|
|
1338
1937
|
// src/components/ToolCallBlock.tsx
|
|
1339
|
-
import { useState as
|
|
1938
|
+
import { useState as useState9 } from "react";
|
|
1340
1939
|
|
|
1341
1940
|
// src/components/AskUserQuestionBlock.tsx
|
|
1342
|
-
import { useEffect as useEffect5, useMemo as
|
|
1343
|
-
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";
|
|
1344
1943
|
function AskUserQuestionBlock({
|
|
1345
1944
|
data,
|
|
1346
1945
|
answered,
|
|
@@ -1349,16 +1948,16 @@ function AskUserQuestionBlock({
|
|
|
1349
1948
|
answerData,
|
|
1350
1949
|
onAnswer
|
|
1351
1950
|
}) {
|
|
1352
|
-
const [selections, setSelections] =
|
|
1353
|
-
const [customTexts, setCustomTexts] =
|
|
1354
|
-
const [usingCustom, setUsingCustom] =
|
|
1355
|
-
const [submitted, setSubmitted] =
|
|
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);
|
|
1356
1955
|
useEffect5(() => {
|
|
1357
1956
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1358
1957
|
setSubmitted(false);
|
|
1359
1958
|
}
|
|
1360
1959
|
}, [sessionStatus]);
|
|
1361
|
-
const displayAnswerState =
|
|
1960
|
+
const displayAnswerState = useMemo6(() => {
|
|
1362
1961
|
if (!(answered && answerData)) {
|
|
1363
1962
|
return { selections, customTexts, usingCustom };
|
|
1364
1963
|
}
|
|
@@ -1443,7 +2042,7 @@ ${parts.join("\n")}`;
|
|
|
1443
2042
|
setSubmitted(true);
|
|
1444
2043
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
1445
2044
|
};
|
|
1446
|
-
return /* @__PURE__ */
|
|
2045
|
+
return /* @__PURE__ */ jsxs8(
|
|
1447
2046
|
"div",
|
|
1448
2047
|
{
|
|
1449
2048
|
className: cn(
|
|
@@ -1451,12 +2050,12 @@ ${parts.join("\n")}`;
|
|
|
1451
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"
|
|
1452
2051
|
),
|
|
1453
2052
|
children: [
|
|
1454
|
-
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: [
|
|
1455
2054
|
"\u5B50\u667A\u80FD\u4F53\u300C",
|
|
1456
2055
|
data.source_loop.description,
|
|
1457
2056
|
"\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
|
|
1458
2057
|
] }),
|
|
1459
|
-
data.questions.map((q, qIdx) => /* @__PURE__ */
|
|
2058
|
+
data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
|
|
1460
2059
|
QuestionCard,
|
|
1461
2060
|
{
|
|
1462
2061
|
question: q,
|
|
@@ -1471,7 +2070,7 @@ ${parts.join("\n")}`;
|
|
|
1471
2070
|
},
|
|
1472
2071
|
q.question
|
|
1473
2072
|
)),
|
|
1474
|
-
!answered && !submitted && onAnswer && /* @__PURE__ */
|
|
2073
|
+
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
|
|
1475
2074
|
"button",
|
|
1476
2075
|
{
|
|
1477
2076
|
type: "button",
|
|
@@ -1481,14 +2080,14 @@ ${parts.join("\n")}`;
|
|
|
1481
2080
|
children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
|
|
1482
2081
|
}
|
|
1483
2082
|
),
|
|
1484
|
-
submitted && !answered && /* @__PURE__ */
|
|
2083
|
+
submitted && !answered && /* @__PURE__ */ jsxs8(
|
|
1485
2084
|
"button",
|
|
1486
2085
|
{
|
|
1487
2086
|
type: "button",
|
|
1488
2087
|
disabled: true,
|
|
1489
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",
|
|
1490
2089
|
children: [
|
|
1491
|
-
/* @__PURE__ */
|
|
2090
|
+
/* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
1492
2091
|
"\u786E\u8BA4\u4E2D"
|
|
1493
2092
|
]
|
|
1494
2093
|
}
|
|
@@ -1509,30 +2108,30 @@ function QuestionCard({
|
|
|
1509
2108
|
onCustomChange
|
|
1510
2109
|
}) {
|
|
1511
2110
|
const multi = question.multiSelect ?? false;
|
|
1512
|
-
return /* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
/* @__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(
|
|
1515
2114
|
MessageSquareMore,
|
|
1516
2115
|
{
|
|
1517
2116
|
size: answered ? 12 : 13,
|
|
1518
2117
|
className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
|
|
1519
2118
|
}
|
|
1520
2119
|
),
|
|
1521
|
-
/* @__PURE__ */
|
|
2120
|
+
/* @__PURE__ */ jsx10(
|
|
1522
2121
|
"div",
|
|
1523
2122
|
{
|
|
1524
2123
|
className: cn(
|
|
1525
2124
|
"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
|
|
1526
2125
|
answered ? "text-xs" : "text-sm"
|
|
1527
2126
|
),
|
|
1528
|
-
children: /* @__PURE__ */
|
|
2127
|
+
children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
|
|
1529
2128
|
}
|
|
1530
2129
|
)
|
|
1531
2130
|
] }),
|
|
1532
|
-
/* @__PURE__ */
|
|
2131
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
|
|
1533
2132
|
question.options.map((opt, optIdx) => {
|
|
1534
2133
|
const isSel = selected.has(optIdx);
|
|
1535
|
-
return /* @__PURE__ */
|
|
2134
|
+
return /* @__PURE__ */ jsxs8(
|
|
1536
2135
|
"button",
|
|
1537
2136
|
{
|
|
1538
2137
|
type: "button",
|
|
@@ -1546,14 +2145,14 @@ function QuestionCard({
|
|
|
1546
2145
|
answered && "cursor-default opacity-70"
|
|
1547
2146
|
),
|
|
1548
2147
|
children: [
|
|
1549
|
-
multi && /* @__PURE__ */
|
|
2148
|
+
multi && /* @__PURE__ */ jsx10(
|
|
1550
2149
|
"div",
|
|
1551
2150
|
{
|
|
1552
2151
|
className: cn(
|
|
1553
2152
|
"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
|
|
1554
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))]"
|
|
1555
2154
|
),
|
|
1556
|
-
children: isSel && /* @__PURE__ */
|
|
2155
|
+
children: isSel && /* @__PURE__ */ jsx10(
|
|
1557
2156
|
Check,
|
|
1558
2157
|
{
|
|
1559
2158
|
size: 9,
|
|
@@ -1562,9 +2161,9 @@ function QuestionCard({
|
|
|
1562
2161
|
)
|
|
1563
2162
|
}
|
|
1564
2163
|
),
|
|
1565
|
-
/* @__PURE__ */
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
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(
|
|
1568
2167
|
"div",
|
|
1569
2168
|
{
|
|
1570
2169
|
className: cn(
|
|
@@ -1581,7 +2180,7 @@ function QuestionCard({
|
|
|
1581
2180
|
opt.label
|
|
1582
2181
|
);
|
|
1583
2182
|
}),
|
|
1584
|
-
answered && !isCustom ? null : /* @__PURE__ */
|
|
2183
|
+
answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
|
|
1585
2184
|
"div",
|
|
1586
2185
|
{
|
|
1587
2186
|
className: cn(
|
|
@@ -1591,8 +2190,8 @@ function QuestionCard({
|
|
|
1591
2190
|
answered && "cursor-default opacity-70"
|
|
1592
2191
|
),
|
|
1593
2192
|
children: [
|
|
1594
|
-
/* @__PURE__ */
|
|
1595
|
-
/* @__PURE__ */
|
|
2193
|
+
/* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2194
|
+
/* @__PURE__ */ jsx10(
|
|
1596
2195
|
"input",
|
|
1597
2196
|
{
|
|
1598
2197
|
type: "text",
|
|
@@ -1660,7 +2259,7 @@ function normalizeOptionItem(value) {
|
|
|
1660
2259
|
}
|
|
1661
2260
|
|
|
1662
2261
|
// src/components/ToolCallBlock.tsx
|
|
1663
|
-
import { Fragment, jsx as
|
|
2262
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1664
2263
|
function resolveAskQuestionState({
|
|
1665
2264
|
toolStatus,
|
|
1666
2265
|
hasAnswerData,
|
|
@@ -1680,12 +2279,12 @@ function ToolCallBlock({
|
|
|
1680
2279
|
sessionStatus,
|
|
1681
2280
|
renderer
|
|
1682
2281
|
}) {
|
|
1683
|
-
const [expanded, setExpanded] =
|
|
2282
|
+
const [expanded, setExpanded] = useState9(false);
|
|
1684
2283
|
const normalizedName = formatToolName(toolCall.name);
|
|
1685
2284
|
if (renderer) {
|
|
1686
2285
|
const custom = renderer(toolCall);
|
|
1687
2286
|
if (custom !== null && custom !== void 0) {
|
|
1688
|
-
return /* @__PURE__ */
|
|
2287
|
+
return /* @__PURE__ */ jsx11(Fragment, { children: custom });
|
|
1689
2288
|
}
|
|
1690
2289
|
}
|
|
1691
2290
|
if (normalizedName === "AskUserQuestion") {
|
|
@@ -1697,7 +2296,7 @@ function ToolCallBlock({
|
|
|
1697
2296
|
});
|
|
1698
2297
|
const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
|
|
1699
2298
|
if (askData) {
|
|
1700
|
-
return /* @__PURE__ */
|
|
2299
|
+
return /* @__PURE__ */ jsx11(
|
|
1701
2300
|
AskUserQuestionBlock,
|
|
1702
2301
|
{
|
|
1703
2302
|
data: askData,
|
|
@@ -1710,24 +2309,24 @@ function ToolCallBlock({
|
|
|
1710
2309
|
);
|
|
1711
2310
|
}
|
|
1712
2311
|
if (toolCall.status === "pending") {
|
|
1713
|
-
return /* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
/* @__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" })
|
|
1716
2315
|
] });
|
|
1717
2316
|
}
|
|
1718
|
-
return /* @__PURE__ */
|
|
1719
|
-
/* @__PURE__ */
|
|
1720
|
-
/* @__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" })
|
|
1721
2320
|
] });
|
|
1722
2321
|
}
|
|
1723
2322
|
const tone = getToolTone(toolCall.status);
|
|
1724
2323
|
const displayName = getToolDisplayLabel(toolCall);
|
|
1725
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))]";
|
|
1726
|
-
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 });
|
|
1727
2326
|
const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
|
|
1728
|
-
return /* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__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(
|
|
1731
2330
|
"button",
|
|
1732
2331
|
{
|
|
1733
2332
|
type: "button",
|
|
@@ -1735,7 +2334,7 @@ function ToolCallBlock({
|
|
|
1735
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",
|
|
1736
2335
|
"aria-expanded": expanded,
|
|
1737
2336
|
children: [
|
|
1738
|
-
/* @__PURE__ */
|
|
2337
|
+
/* @__PURE__ */ jsx11(
|
|
1739
2338
|
ChevronRight,
|
|
1740
2339
|
{
|
|
1741
2340
|
size: 11,
|
|
@@ -1745,24 +2344,24 @@ function ToolCallBlock({
|
|
|
1745
2344
|
)
|
|
1746
2345
|
}
|
|
1747
2346
|
),
|
|
1748
|
-
/* @__PURE__ */
|
|
2347
|
+
/* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
|
|
1749
2348
|
statusIcon,
|
|
1750
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
|
|
1751
2350
|
] }),
|
|
1752
|
-
/* @__PURE__ */
|
|
2351
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
|
|
1753
2352
|
]
|
|
1754
2353
|
}
|
|
1755
2354
|
),
|
|
1756
|
-
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) })
|
|
1757
2356
|
] }),
|
|
1758
|
-
expanded && /* @__PURE__ */
|
|
1759
|
-
/* @__PURE__ */
|
|
1760
|
-
/* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
1763
|
-
toolCall.result != null && /* @__PURE__ */
|
|
1764
|
-
/* @__PURE__ */
|
|
1765
|
-
/* @__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) })
|
|
1766
2365
|
] })
|
|
1767
2366
|
] })
|
|
1768
2367
|
] });
|
|
@@ -1780,11 +2379,11 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
1780
2379
|
}
|
|
1781
2380
|
|
|
1782
2381
|
// src/components/AssistantTurnBlock.tsx
|
|
1783
|
-
import { jsx as
|
|
2382
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1784
2383
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
1785
|
-
const [open, setOpen] =
|
|
1786
|
-
return /* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
2384
|
+
const [open, setOpen] = useState10(false);
|
|
2385
|
+
return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
|
|
2386
|
+
/* @__PURE__ */ jsxs10(
|
|
1788
2387
|
"button",
|
|
1789
2388
|
{
|
|
1790
2389
|
type: "button",
|
|
@@ -1792,14 +2391,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
1792
2391
|
"aria-expanded": open,
|
|
1793
2392
|
className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
1794
2393
|
children: [
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
isStreaming ? /* @__PURE__ */
|
|
1797
|
-
/* @__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: [
|
|
1798
2397
|
"\xB7 ",
|
|
1799
2398
|
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
1800
2399
|
" \u5B57"
|
|
1801
2400
|
] }),
|
|
1802
|
-
/* @__PURE__ */
|
|
2401
|
+
/* @__PURE__ */ jsx12(
|
|
1803
2402
|
ChevronDown,
|
|
1804
2403
|
{
|
|
1805
2404
|
size: 12,
|
|
@@ -1809,7 +2408,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
1809
2408
|
]
|
|
1810
2409
|
}
|
|
1811
2410
|
),
|
|
1812
|
-
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 })
|
|
1813
2412
|
] });
|
|
1814
2413
|
}
|
|
1815
2414
|
function getMessageText(message) {
|
|
@@ -1835,21 +2434,21 @@ function AssistantTurnBlock({
|
|
|
1835
2434
|
(message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
1836
2435
|
);
|
|
1837
2436
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
1838
|
-
return /* @__PURE__ */
|
|
1839
|
-
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" }),
|
|
1840
2439
|
messages.map((message, index) => {
|
|
1841
2440
|
const isLast = index === messages.length - 1;
|
|
1842
2441
|
const streamingThis = isStreaming && isLast;
|
|
1843
2442
|
const text = getMessageText(message);
|
|
1844
2443
|
const toolCalls = message.tool_calls ?? [];
|
|
1845
2444
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
1846
|
-
return /* @__PURE__ */
|
|
2445
|
+
return /* @__PURE__ */ jsxs10(
|
|
1847
2446
|
"div",
|
|
1848
2447
|
{
|
|
1849
2448
|
className: "flex flex-col gap-3",
|
|
1850
2449
|
children: [
|
|
1851
|
-
showReasoning && message.reasoning && /* @__PURE__ */
|
|
1852
|
-
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(
|
|
1853
2452
|
MarkdownContent,
|
|
1854
2453
|
{
|
|
1855
2454
|
mode: streamingThis ? "streaming" : "static",
|
|
@@ -1858,8 +2457,8 @@ function AssistantTurnBlock({
|
|
|
1858
2457
|
children: text
|
|
1859
2458
|
}
|
|
1860
2459
|
) }),
|
|
1861
|
-
toolCalls.length > 0 && /* @__PURE__ */
|
|
1862
|
-
(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(
|
|
1863
2462
|
ToolCallBlock,
|
|
1864
2463
|
{
|
|
1865
2464
|
toolCall,
|
|
@@ -1877,13 +2476,13 @@ function AssistantTurnBlock({
|
|
|
1877
2476
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
1878
2477
|
);
|
|
1879
2478
|
}),
|
|
1880
|
-
isStreaming && !hasAnyContent && /* @__PURE__ */
|
|
2479
|
+
isStreaming && !hasAnyContent && /* @__PURE__ */ jsx12(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
|
|
1881
2480
|
] });
|
|
1882
2481
|
}
|
|
1883
2482
|
|
|
1884
2483
|
// src/components/RenderErrorBoundary.tsx
|
|
1885
2484
|
import { Component } from "react";
|
|
1886
|
-
import { jsx as
|
|
2485
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1887
2486
|
function getFirstComponentName(componentStack) {
|
|
1888
2487
|
const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
|
|
1889
2488
|
return match?.[1] ?? null;
|
|
@@ -1916,18 +2515,18 @@ var RenderErrorBoundary = class extends Component {
|
|
|
1916
2515
|
return children;
|
|
1917
2516
|
}
|
|
1918
2517
|
const componentName = getFirstComponentName(componentStack);
|
|
1919
|
-
return /* @__PURE__ */
|
|
1920
|
-
/* @__PURE__ */
|
|
1921
|
-
/* @__PURE__ */
|
|
1922
|
-
/* @__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: [
|
|
1923
2522
|
label,
|
|
1924
2523
|
"\u6E32\u67D3\u5931\u8D25"
|
|
1925
2524
|
] }),
|
|
1926
|
-
/* @__PURE__ */
|
|
2525
|
+
/* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
|
|
1927
2526
|
componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
|
|
1928
2527
|
error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
|
|
1929
2528
|
] }),
|
|
1930
|
-
details ? /* @__PURE__ */
|
|
2529
|
+
details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
|
|
1931
2530
|
] })
|
|
1932
2531
|
] }) });
|
|
1933
2532
|
}
|
|
@@ -1935,7 +2534,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
1935
2534
|
|
|
1936
2535
|
// src/components/UserMessageBubble.tsx
|
|
1937
2536
|
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
1938
|
-
import { jsx as
|
|
2537
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1939
2538
|
function isUserMessage(message) {
|
|
1940
2539
|
return message.role === "user";
|
|
1941
2540
|
}
|
|
@@ -1947,8 +2546,8 @@ function UserMessageBubble({ message, className }) {
|
|
|
1947
2546
|
const text = getTextContent2(message.content).trim();
|
|
1948
2547
|
const fileParts = getFileParts(message.content);
|
|
1949
2548
|
const imageParts = getImageParts(message.content);
|
|
1950
|
-
return /* @__PURE__ */
|
|
1951
|
-
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(
|
|
1952
2551
|
"img",
|
|
1953
2552
|
{
|
|
1954
2553
|
src: part.image_url.url,
|
|
@@ -1957,21 +2556,21 @@ function UserMessageBubble({ message, className }) {
|
|
|
1957
2556
|
},
|
|
1958
2557
|
part.image_url.url
|
|
1959
2558
|
)) }),
|
|
1960
|
-
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(
|
|
1961
2560
|
"div",
|
|
1962
2561
|
{
|
|
1963
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))]",
|
|
1964
2563
|
children: [
|
|
1965
|
-
/* @__PURE__ */
|
|
1966
|
-
/* @__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 })
|
|
1967
2566
|
]
|
|
1968
2567
|
},
|
|
1969
2568
|
`${part.name}-${part.data.length}`
|
|
1970
2569
|
)) }),
|
|
1971
|
-
text && /* @__PURE__ */
|
|
1972
|
-
text && isSending(message) && /* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
/* @__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" })
|
|
1975
2574
|
] })
|
|
1976
2575
|
] }) });
|
|
1977
2576
|
}
|
|
@@ -1980,11 +2579,11 @@ function ErrorMessageBlock({
|
|
|
1980
2579
|
className
|
|
1981
2580
|
}) {
|
|
1982
2581
|
const text = getTextContent2(message.content);
|
|
1983
|
-
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 }) });
|
|
1984
2583
|
}
|
|
1985
2584
|
|
|
1986
2585
|
// src/components/MessageList.tsx
|
|
1987
|
-
import { jsx as
|
|
2586
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1988
2587
|
function parseModeChange(message) {
|
|
1989
2588
|
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
1990
2589
|
return null;
|
|
@@ -2027,7 +2626,7 @@ function MessageList({
|
|
|
2027
2626
|
className,
|
|
2028
2627
|
sessionId
|
|
2029
2628
|
}) {
|
|
2030
|
-
const renderBlocks =
|
|
2629
|
+
const renderBlocks = useMemo7(() => {
|
|
2031
2630
|
const visible = messages.filter((message) => {
|
|
2032
2631
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
2033
2632
|
if (isHiddenInternalMessage(message)) return false;
|
|
@@ -2100,24 +2699,24 @@ function MessageList({
|
|
|
2100
2699
|
}
|
|
2101
2700
|
return blocks;
|
|
2102
2701
|
}, [messages, isStreaming]);
|
|
2103
|
-
return /* @__PURE__ */
|
|
2104
|
-
/* @__PURE__ */
|
|
2105
|
-
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */
|
|
2106
|
-
/* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
2108
|
-
/* @__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" })
|
|
2109
2708
|
] }) : renderBlocks.map((block) => {
|
|
2110
2709
|
if (block.type === "message") {
|
|
2111
|
-
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);
|
|
2112
2711
|
}
|
|
2113
2712
|
if (block.type === "assistant_turn") {
|
|
2114
|
-
return /* @__PURE__ */
|
|
2713
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx15(
|
|
2115
2714
|
RenderErrorBoundary,
|
|
2116
2715
|
{
|
|
2117
2716
|
label: "\u52A9\u624B\u6D88\u606F",
|
|
2118
2717
|
details: block.key,
|
|
2119
2718
|
resetKey: getMessageResetSignature(block.messages),
|
|
2120
|
-
children: /* @__PURE__ */
|
|
2719
|
+
children: /* @__PURE__ */ jsx15(
|
|
2121
2720
|
AssistantTurnBlock,
|
|
2122
2721
|
{
|
|
2123
2722
|
messages: block.messages,
|
|
@@ -2133,24 +2732,24 @@ function MessageList({
|
|
|
2133
2732
|
) }, block.key);
|
|
2134
2733
|
}
|
|
2135
2734
|
if (block.type === "compaction") {
|
|
2136
|
-
return /* @__PURE__ */
|
|
2735
|
+
return /* @__PURE__ */ jsxs13(
|
|
2137
2736
|
"div",
|
|
2138
2737
|
{
|
|
2139
2738
|
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
2140
2739
|
children: [
|
|
2141
|
-
/* @__PURE__ */
|
|
2142
|
-
/* @__PURE__ */
|
|
2740
|
+
/* @__PURE__ */ jsx15(Layers, { size: 12 }),
|
|
2741
|
+
/* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
2143
2742
|
]
|
|
2144
2743
|
},
|
|
2145
2744
|
block.key
|
|
2146
2745
|
);
|
|
2147
2746
|
}
|
|
2148
|
-
return /* @__PURE__ */
|
|
2747
|
+
return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
|
|
2149
2748
|
}),
|
|
2150
|
-
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
|
|
2151
2750
|
] }) }) }),
|
|
2152
|
-
/* @__PURE__ */
|
|
2153
|
-
/* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx15(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
|
|
2752
|
+
/* @__PURE__ */ jsx15(ScrollToBottomButton, {})
|
|
2154
2753
|
] }) });
|
|
2155
2754
|
}
|
|
2156
2755
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
@@ -2166,7 +2765,7 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
2166
2765
|
}
|
|
2167
2766
|
function ScrollToBottomButton() {
|
|
2168
2767
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
2169
|
-
const [visible, setVisible] =
|
|
2768
|
+
const [visible, setVisible] = useState11(false);
|
|
2170
2769
|
const hideTimerRef = useRef6(null);
|
|
2171
2770
|
useEffect6(() => {
|
|
2172
2771
|
if (isAtBottom) {
|
|
@@ -2190,7 +2789,7 @@ function ScrollToBottomButton() {
|
|
|
2190
2789
|
}
|
|
2191
2790
|
};
|
|
2192
2791
|
}, [isAtBottom]);
|
|
2193
|
-
const handleClick =
|
|
2792
|
+
const handleClick = useCallback5(() => {
|
|
2194
2793
|
if (hideTimerRef.current) {
|
|
2195
2794
|
clearTimeout(hideTimerRef.current);
|
|
2196
2795
|
hideTimerRef.current = null;
|
|
@@ -2199,7 +2798,7 @@ function ScrollToBottomButton() {
|
|
|
2199
2798
|
scrollToBottom();
|
|
2200
2799
|
}, [scrollToBottom]);
|
|
2201
2800
|
if (!visible) return null;
|
|
2202
|
-
return /* @__PURE__ */
|
|
2801
|
+
return /* @__PURE__ */ jsxs13(
|
|
2203
2802
|
"button",
|
|
2204
2803
|
{
|
|
2205
2804
|
type: "button",
|
|
@@ -2207,34 +2806,108 @@ function ScrollToBottomButton() {
|
|
|
2207
2806
|
"aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
|
|
2208
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))]",
|
|
2209
2808
|
children: [
|
|
2210
|
-
/* @__PURE__ */
|
|
2211
|
-
/* @__PURE__ */
|
|
2809
|
+
/* @__PURE__ */ jsx15(ChevronDown, { size: 14 }),
|
|
2810
|
+
/* @__PURE__ */ jsx15("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
|
|
2212
2811
|
]
|
|
2213
2812
|
}
|
|
2214
2813
|
);
|
|
2215
2814
|
}
|
|
2216
2815
|
function PlanningDivider({ kind }) {
|
|
2217
|
-
return /* @__PURE__ */
|
|
2218
|
-
/* @__PURE__ */
|
|
2219
|
-
/* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__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" })
|
|
2222
2821
|
] }),
|
|
2223
|
-
/* @__PURE__ */
|
|
2822
|
+
/* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
|
|
2224
2823
|
] });
|
|
2225
2824
|
}
|
|
2226
2825
|
|
|
2227
|
-
// src/components/
|
|
2228
|
-
import { jsx as
|
|
2826
|
+
// src/components/ChatSurface.tsx
|
|
2827
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2229
2828
|
function themeAttr(theme) {
|
|
2230
2829
|
return theme === "dark" ? "dark" : void 0;
|
|
2231
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";
|
|
2232
2905
|
function isUnauthorizedError(error) {
|
|
2233
2906
|
return error instanceof BladeApiError && error.status === 401;
|
|
2234
2907
|
}
|
|
2235
2908
|
function LoginCard({ client, onLoggedIn }) {
|
|
2236
|
-
const [loggingIn, setLoggingIn] =
|
|
2237
|
-
const [loginError, setLoginError] =
|
|
2909
|
+
const [loggingIn, setLoggingIn] = useState12(false);
|
|
2910
|
+
const [loginError, setLoginError] = useState12(null);
|
|
2238
2911
|
const handleLogin = async () => {
|
|
2239
2912
|
setLoggingIn(true);
|
|
2240
2913
|
setLoginError(null);
|
|
@@ -2247,11 +2920,11 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
2247
2920
|
setLoggingIn(false);
|
|
2248
2921
|
}
|
|
2249
2922
|
};
|
|
2250
|
-
return /* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
2254
|
-
/* @__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(
|
|
2255
2928
|
"button",
|
|
2256
2929
|
{
|
|
2257
2930
|
type: "button",
|
|
@@ -2261,20 +2934,20 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
2261
2934
|
children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
2262
2935
|
}
|
|
2263
2936
|
),
|
|
2264
|
-
loginError && /* @__PURE__ */
|
|
2937
|
+
loginError && /* @__PURE__ */ jsx17("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
|
|
2265
2938
|
] }) });
|
|
2266
2939
|
}
|
|
2267
|
-
function
|
|
2940
|
+
function AgentChat(props) {
|
|
2268
2941
|
const client = useBladeClient();
|
|
2269
|
-
const [attempt, setAttempt] =
|
|
2270
|
-
const [needLogin, setNeedLogin] =
|
|
2942
|
+
const [attempt, setAttempt] = useState12(0);
|
|
2943
|
+
const [needLogin, setNeedLogin] = useState12(() => !client.hasToken());
|
|
2271
2944
|
if (needLogin) {
|
|
2272
|
-
return /* @__PURE__ */
|
|
2945
|
+
return /* @__PURE__ */ jsx17(
|
|
2273
2946
|
"div",
|
|
2274
2947
|
{
|
|
2275
2948
|
"data-theme": themeAttr(props.theme),
|
|
2276
2949
|
className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
|
|
2277
|
-
children: /* @__PURE__ */
|
|
2950
|
+
children: /* @__PURE__ */ jsx17(
|
|
2278
2951
|
LoginCard,
|
|
2279
2952
|
{
|
|
2280
2953
|
client,
|
|
@@ -2287,7 +2960,7 @@ function ChatView(props) {
|
|
|
2287
2960
|
}
|
|
2288
2961
|
);
|
|
2289
2962
|
}
|
|
2290
|
-
return /* @__PURE__ */
|
|
2963
|
+
return /* @__PURE__ */ jsx17(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
|
|
2291
2964
|
}
|
|
2292
2965
|
function ChatSessionView({
|
|
2293
2966
|
sessionId,
|
|
@@ -2306,8 +2979,9 @@ function ChatSessionView({
|
|
|
2306
2979
|
createOptions,
|
|
2307
2980
|
onSessionCreated
|
|
2308
2981
|
});
|
|
2309
|
-
const
|
|
2310
|
-
const [
|
|
2982
|
+
const replay = useReplay(session);
|
|
2983
|
+
const [stopRequested, setStopRequested] = useState12(false);
|
|
2984
|
+
const [inputText, setInputText] = useState12("");
|
|
2311
2985
|
useEffect7(() => {
|
|
2312
2986
|
if (session) {
|
|
2313
2987
|
onSessionReady?.(session);
|
|
@@ -2346,7 +3020,7 @@ ${content}`);
|
|
|
2346
3020
|
const isStreaming = state?.isStreaming ?? false;
|
|
2347
3021
|
const isStopping = stopRequested && isStreaming;
|
|
2348
3022
|
const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
|
|
2349
|
-
const errorMessage = connectError ?? state?.errorMessage ?? null;
|
|
3023
|
+
const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
|
|
2350
3024
|
const handleSend = (text) => {
|
|
2351
3025
|
setStopRequested(false);
|
|
2352
3026
|
return session?.send(text, { mode: state?.mode ?? void 0 }) ?? Promise.resolve(false);
|
|
@@ -2355,73 +3029,281 @@ ${content}`);
|
|
|
2355
3029
|
setStopRequested(true);
|
|
2356
3030
|
void session?.stop();
|
|
2357
3031
|
};
|
|
2358
|
-
return /* @__PURE__ */
|
|
2359
|
-
|
|
3032
|
+
return /* @__PURE__ */ jsx17(
|
|
3033
|
+
ChatSurface,
|
|
2360
3034
|
{
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
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,
|
|
2369
3044
|
{
|
|
2370
|
-
|
|
2371
|
-
|
|
3045
|
+
isReplay: replay.isReplay,
|
|
3046
|
+
speed: replay.speed,
|
|
3047
|
+
canControl: replay.canControl,
|
|
3048
|
+
onSpeedChange: (next) => void replay.setSpeed(next),
|
|
3049
|
+
onExit: () => void replay.exitToAutonomous()
|
|
2372
3050
|
}
|
|
2373
3051
|
),
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
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",
|
|
2381
3153
|
{
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
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);
|
|
2391
3174
|
},
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
className: classNames?.messageList,
|
|
2395
|
-
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"
|
|
2396
3177
|
}
|
|
2397
3178
|
),
|
|
2398
|
-
/* @__PURE__ */
|
|
2399
|
-
|
|
3179
|
+
/* @__PURE__ */ jsx18(
|
|
3180
|
+
"button",
|
|
2400
3181
|
{
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
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"
|
|
2409
3190
|
}
|
|
2410
|
-
)
|
|
2411
|
-
|
|
2412
|
-
|
|
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
|
|
2413
3265
|
}
|
|
2414
3266
|
);
|
|
2415
3267
|
}
|
|
2416
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
|
+
|
|
2417
3293
|
// src/index.ts
|
|
2418
3294
|
export * from "@blade-hq/agent-client";
|
|
2419
3295
|
export {
|
|
3296
|
+
AgentChat,
|
|
2420
3297
|
BladeProvider,
|
|
2421
3298
|
ChatView,
|
|
3299
|
+
LlmChat,
|
|
2422
3300
|
MarkdownContent,
|
|
3301
|
+
ReplayBar,
|
|
3302
|
+
ReplayMismatchPrompt,
|
|
2423
3303
|
useAgentSession,
|
|
2424
|
-
useBladeClient
|
|
3304
|
+
useBladeClient,
|
|
3305
|
+
useLlmChat,
|
|
3306
|
+
useReplay
|
|
2425
3307
|
};
|
|
2426
3308
|
/*! Bundled license information:
|
|
2427
3309
|
|
|
@@ -2429,6 +3311,7 @@ lucide-react/dist/esm/shared/src/utils.js:
|
|
|
2429
3311
|
lucide-react/dist/esm/defaultAttributes.js:
|
|
2430
3312
|
lucide-react/dist/esm/Icon.js:
|
|
2431
3313
|
lucide-react/dist/esm/createLucideIcon.js:
|
|
3314
|
+
lucide-react/dist/esm/icons/archive.js:
|
|
2432
3315
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
2433
3316
|
lucide-react/dist/esm/icons/bot.js:
|
|
2434
3317
|
lucide-react/dist/esm/icons/brain.js:
|
|
@@ -2437,13 +3320,22 @@ lucide-react/dist/esm/icons/chevron-down.js:
|
|
|
2437
3320
|
lucide-react/dist/esm/icons/chevron-right.js:
|
|
2438
3321
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
2439
3322
|
lucide-react/dist/esm/icons/copy.js:
|
|
3323
|
+
lucide-react/dist/esm/icons/download.js:
|
|
3324
|
+
lucide-react/dist/esm/icons/file-code-2.js:
|
|
3325
|
+
lucide-react/dist/esm/icons/file-spreadsheet.js:
|
|
2440
3326
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3327
|
+
lucide-react/dist/esm/icons/file.js:
|
|
3328
|
+
lucide-react/dist/esm/icons/film.js:
|
|
3329
|
+
lucide-react/dist/esm/icons/image.js:
|
|
2441
3330
|
lucide-react/dist/esm/icons/layers.js:
|
|
2442
3331
|
lucide-react/dist/esm/icons/lightbulb.js:
|
|
2443
3332
|
lucide-react/dist/esm/icons/loader-circle.js:
|
|
2444
3333
|
lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
2445
3334
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
2446
3335
|
lucide-react/dist/esm/icons/message-square.js:
|
|
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:
|
|
2447
3339
|
lucide-react/dist/esm/icons/square.js:
|
|
2448
3340
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
2449
3341
|
lucide-react/dist/esm/icons/x.js:
|