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