@blade-hq/agent-react 2608.0.7-beta.0 → 2608.0.7-beta.2
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 +3 -155
- package/dist/components/AssistantTurnBlock.d.ts +1 -1
- package/dist/components/ChatView.d.ts +39 -20
- package/dist/components/FileCard.d.ts +16 -0
- package/dist/components/MarkdownContent.d.ts +6 -3
- package/dist/components/MessageList.d.ts +3 -10
- package/dist/embed/entry.d.ts +3 -5
- package/dist/index.d.ts +1 -14
- package/dist/index.js +429 -1668
- package/dist/index.js.map +1 -1
- package/dist/lib/media-tags.d.ts +24 -0
- package/dist/style.css +11 -52
- package/dist/style.full.css +12 -53
- package/package.json +2 -2
- package/public-api.md +21 -403
- package/dist/components/AgentChat.d.ts +0 -21
- package/dist/components/ChatSurface.d.ts +0 -68
- package/dist/components/LlmAdvancedSettings.d.ts +0 -40
- package/dist/components/LlmChat.d.ts +0 -29
- package/dist/components/PostChatFollowupBlock.d.ts +0 -59
- 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,453 +99,8 @@ function useAgentSession(sessionId, options = {}) {
|
|
|
99
99
|
return { session, state, error };
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
// src/
|
|
103
|
-
import {
|
|
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
|
|
548
|
-
import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
|
|
102
|
+
// src/components/ChatView.tsx
|
|
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
|
|
551
106
|
import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
|
|
@@ -618,18 +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/arrow-right.js
|
|
622
|
-
var ArrowRight = createLucideIcon("ArrowRight", [
|
|
623
|
-
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
624
|
-
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
625
|
-
]);
|
|
626
|
-
|
|
627
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up-right.js
|
|
628
|
-
var ArrowUpRight = createLucideIcon("ArrowUpRight", [
|
|
629
|
-
["path", { d: "M7 7h10v10", key: "1tivn9" }],
|
|
630
|
-
["path", { d: "M7 17 17 7", key: "1vkiza" }]
|
|
631
|
-
]);
|
|
632
|
-
|
|
633
176
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
|
|
634
177
|
var ArrowUp = createLucideIcon("ArrowUp", [
|
|
635
178
|
["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
|
|
@@ -706,13 +249,6 @@ var FileText = createLucideIcon("FileText", [
|
|
|
706
249
|
["path", { d: "M16 17H8", key: "z1uh3a" }]
|
|
707
250
|
]);
|
|
708
251
|
|
|
709
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
|
|
710
|
-
var Globe = createLucideIcon("Globe", [
|
|
711
|
-
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
712
|
-
["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
|
|
713
|
-
["path", { d: "M2 12h20", key: "9i4pu4" }]
|
|
714
|
-
]);
|
|
715
|
-
|
|
716
252
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
|
|
717
253
|
var Layers = createLucideIcon("Layers", [
|
|
718
254
|
[
|
|
@@ -776,34 +312,6 @@ var MessageSquare = createLucideIcon("MessageSquare", [
|
|
|
776
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" }]
|
|
777
313
|
]);
|
|
778
314
|
|
|
779
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
|
|
780
|
-
var Play = createLucideIcon("Play", [
|
|
781
|
-
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
782
|
-
]);
|
|
783
|
-
|
|
784
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
|
|
785
|
-
var Settings2 = createLucideIcon("Settings2", [
|
|
786
|
-
["path", { d: "M20 7h-9", key: "3s1dr2" }],
|
|
787
|
-
["path", { d: "M14 17H5", key: "gfn3mx" }],
|
|
788
|
-
["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
|
|
789
|
-
["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
|
|
790
|
-
]);
|
|
791
|
-
|
|
792
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/sparkles.js
|
|
793
|
-
var Sparkles = createLucideIcon("Sparkles", [
|
|
794
|
-
[
|
|
795
|
-
"path",
|
|
796
|
-
{
|
|
797
|
-
d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
|
|
798
|
-
key: "4pj2yx"
|
|
799
|
-
}
|
|
800
|
-
],
|
|
801
|
-
["path", { d: "M20 3v4", key: "1olli1" }],
|
|
802
|
-
["path", { d: "M22 5h-4", key: "1gvqau" }],
|
|
803
|
-
["path", { d: "M4 17v2", key: "vumght" }],
|
|
804
|
-
["path", { d: "M5 18H3", key: "zchphs" }]
|
|
805
|
-
]);
|
|
806
|
-
|
|
807
315
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
|
|
808
316
|
var Square = createLucideIcon("Square", [
|
|
809
317
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
@@ -828,8 +336,8 @@ var X = createLucideIcon("X", [
|
|
|
828
336
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
829
337
|
]);
|
|
830
338
|
|
|
831
|
-
// src/components/
|
|
832
|
-
import {
|
|
339
|
+
// src/components/ChatView.tsx
|
|
340
|
+
import { useEffect as useEffect7, useState as useState11 } from "react";
|
|
833
341
|
|
|
834
342
|
// src/lib/utils.ts
|
|
835
343
|
function cn(...inputs) {
|
|
@@ -844,120 +352,8 @@ async function copyToClipboard(text) {
|
|
|
844
352
|
}
|
|
845
353
|
}
|
|
846
354
|
|
|
847
|
-
// src/components/ReplayBar.tsx
|
|
848
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
849
|
-
var SPEED_OPTIONS = [1, 2, 5];
|
|
850
|
-
function ReplayBar({
|
|
851
|
-
isReplay,
|
|
852
|
-
speed,
|
|
853
|
-
onSpeedChange,
|
|
854
|
-
onExit,
|
|
855
|
-
canControl = true,
|
|
856
|
-
className
|
|
857
|
-
}) {
|
|
858
|
-
if (!isReplay) return null;
|
|
859
|
-
return /* @__PURE__ */ jsxs(
|
|
860
|
-
"div",
|
|
861
|
-
{
|
|
862
|
-
className: cn(
|
|
863
|
-
"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs",
|
|
864
|
-
className
|
|
865
|
-
),
|
|
866
|
-
children: [
|
|
867
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]", children: [
|
|
868
|
-
/* @__PURE__ */ jsx2(Play, { size: 13 }),
|
|
869
|
-
"\u56DE\u653E\u6A21\u5F0F"
|
|
870
|
-
] }),
|
|
871
|
-
/* @__PURE__ */ jsx2("span", { className: "text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u91CD\u73B0\u4E4B\u524D\u7684\u5BF9\u8BDD" }),
|
|
872
|
-
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
|
|
873
|
-
/* @__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(
|
|
874
|
-
"button",
|
|
875
|
-
{
|
|
876
|
-
type: "button",
|
|
877
|
-
onClick: () => onSpeedChange(option),
|
|
878
|
-
"aria-pressed": speed === option,
|
|
879
|
-
disabled: !canControl,
|
|
880
|
-
className: cn(
|
|
881
|
-
"h-6 rounded px-2 font-medium transition-colors",
|
|
882
|
-
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))]",
|
|
883
|
-
!canControl && "cursor-not-allowed opacity-50"
|
|
884
|
-
),
|
|
885
|
-
children: [
|
|
886
|
-
option,
|
|
887
|
-
"x"
|
|
888
|
-
]
|
|
889
|
-
},
|
|
890
|
-
option
|
|
891
|
-
)) }),
|
|
892
|
-
/* @__PURE__ */ jsx2(
|
|
893
|
-
"button",
|
|
894
|
-
{
|
|
895
|
-
type: "button",
|
|
896
|
-
onClick: onExit,
|
|
897
|
-
disabled: !canControl,
|
|
898
|
-
className: cn(
|
|
899
|
-
"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))]",
|
|
900
|
-
!canControl && "cursor-not-allowed opacity-50 hover:bg-transparent"
|
|
901
|
-
),
|
|
902
|
-
children: "\u9000\u51FA\u56DE\u653E"
|
|
903
|
-
}
|
|
904
|
-
)
|
|
905
|
-
] })
|
|
906
|
-
]
|
|
907
|
-
}
|
|
908
|
-
);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// src/components/ReplayMismatchPrompt.tsx
|
|
912
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
913
|
-
function ReplayMismatchPrompt({ mismatch, className }) {
|
|
914
|
-
if (!mismatch) return null;
|
|
915
|
-
return /* @__PURE__ */ jsxs2(
|
|
916
|
-
"div",
|
|
917
|
-
{
|
|
918
|
-
className: cn(
|
|
919
|
-
"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm",
|
|
920
|
-
className
|
|
921
|
-
),
|
|
922
|
-
children: [
|
|
923
|
-
/* @__PURE__ */ jsx3("div", { className: "font-medium text-[hsl(var(--foreground))]", children: "\u8FD9\u53E5\u8BDD\u548C\u4E4B\u524D\u5F55\u5236\u7684\u4E0D\u4E00\u6837" }),
|
|
924
|
-
/* @__PURE__ */ jsxs2("dl", { className: "mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
925
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
926
|
-
/* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u5F55\u5236\u7684\u662F" }),
|
|
927
|
-
/* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.expectedMessage || "\uFF08\u7A7A\uFF09" })
|
|
928
|
-
] }),
|
|
929
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
930
|
-
/* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u4F60\u8F93\u5165\u7684" }),
|
|
931
|
-
/* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.actualMessage || "\uFF08\u7A7A\uFF09" })
|
|
932
|
-
] })
|
|
933
|
-
] }),
|
|
934
|
-
/* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
935
|
-
/* @__PURE__ */ jsx3(
|
|
936
|
-
"button",
|
|
937
|
-
{
|
|
938
|
-
type: "button",
|
|
939
|
-
onClick: () => mismatch.resolve("keep_replay"),
|
|
940
|
-
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",
|
|
941
|
-
children: "\u6309\u5F55\u5236\u5185\u5BB9\u7EE7\u7EED"
|
|
942
|
-
}
|
|
943
|
-
),
|
|
944
|
-
/* @__PURE__ */ jsx3(
|
|
945
|
-
"button",
|
|
946
|
-
{
|
|
947
|
-
type: "button",
|
|
948
|
-
onClick: () => mismatch.resolve("continue_replay"),
|
|
949
|
-
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))]",
|
|
950
|
-
children: "\u4ECE\u8FD9\u91CC\u5F00\u59CB\u771F\u7684\u8FD0\u884C"
|
|
951
|
-
}
|
|
952
|
-
)
|
|
953
|
-
] })
|
|
954
|
-
]
|
|
955
|
-
}
|
|
956
|
-
);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
355
|
// src/components/ChatInput.tsx
|
|
960
|
-
import { jsx as
|
|
356
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
961
357
|
function ChatInput({
|
|
962
358
|
value,
|
|
963
359
|
onValueChange,
|
|
@@ -982,8 +378,8 @@ function ChatInput({
|
|
|
982
378
|
void handleSend();
|
|
983
379
|
}
|
|
984
380
|
};
|
|
985
|
-
return /* @__PURE__ */
|
|
986
|
-
/* @__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(
|
|
987
383
|
"textarea",
|
|
988
384
|
{
|
|
989
385
|
value,
|
|
@@ -1000,7 +396,7 @@ function ChatInput({
|
|
|
1000
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)]"
|
|
1001
397
|
}
|
|
1002
398
|
),
|
|
1003
|
-
isStreaming ? /* @__PURE__ */
|
|
399
|
+
isStreaming ? /* @__PURE__ */ jsx2(
|
|
1004
400
|
"button",
|
|
1005
401
|
{
|
|
1006
402
|
type: "button",
|
|
@@ -1009,9 +405,9 @@ function ChatInput({
|
|
|
1009
405
|
"aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
1010
406
|
title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
|
|
1011
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",
|
|
1012
|
-
children: isStopping ? /* @__PURE__ */
|
|
408
|
+
children: isStopping ? /* @__PURE__ */ jsx2(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Square, { size: 12, fill: "currentColor" })
|
|
1013
409
|
}
|
|
1014
|
-
) : /* @__PURE__ */
|
|
410
|
+
) : /* @__PURE__ */ jsx2(
|
|
1015
411
|
"button",
|
|
1016
412
|
{
|
|
1017
413
|
type: "button",
|
|
@@ -1020,23 +416,23 @@ function ChatInput({
|
|
|
1020
416
|
"aria-label": "\u53D1\u9001\u6D88\u606F",
|
|
1021
417
|
title: "\u53D1\u9001\u6D88\u606F",
|
|
1022
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",
|
|
1023
|
-
children: /* @__PURE__ */
|
|
419
|
+
children: /* @__PURE__ */ jsx2(ArrowUp, { size: 15 })
|
|
1024
420
|
}
|
|
1025
421
|
)
|
|
1026
422
|
] }) });
|
|
1027
423
|
}
|
|
1028
424
|
|
|
1029
425
|
// src/components/ConnectionBanner.tsx
|
|
1030
|
-
import { useEffect as
|
|
1031
|
-
import { jsx as
|
|
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";
|
|
1032
428
|
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1033
429
|
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1034
430
|
function useConnectionNoticePhase(connected) {
|
|
1035
|
-
const [phase, setPhase] =
|
|
1036
|
-
const connectedRef =
|
|
1037
|
-
const timersRef =
|
|
431
|
+
const [phase, setPhase] = useState2("hidden");
|
|
432
|
+
const connectedRef = useRef2(connected);
|
|
433
|
+
const timersRef = useRef2([]);
|
|
1038
434
|
connectedRef.current = connected;
|
|
1039
|
-
|
|
435
|
+
useEffect2(() => {
|
|
1040
436
|
const clearTimers = () => {
|
|
1041
437
|
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1042
438
|
timersRef.current = [];
|
|
@@ -1076,14 +472,14 @@ function useConnectionNoticePhase(connected) {
|
|
|
1076
472
|
return phase;
|
|
1077
473
|
}
|
|
1078
474
|
function ConnectionBanner({ connection, className }) {
|
|
1079
|
-
const hasConnectedRef =
|
|
475
|
+
const hasConnectedRef = useRef2(connection === "connected" || connection === "reconnecting");
|
|
1080
476
|
if (connection === "connected") hasConnectedRef.current = true;
|
|
1081
477
|
const connected = connection === "connected";
|
|
1082
478
|
const phase = useConnectionNoticePhase(connected);
|
|
1083
479
|
if (connected || phase === "hidden") return null;
|
|
1084
480
|
const recovering = phase === "recovering";
|
|
1085
481
|
const firstConnection = !hasConnectedRef.current;
|
|
1086
|
-
return /* @__PURE__ */
|
|
482
|
+
return /* @__PURE__ */ jsx3("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs2(
|
|
1087
483
|
"div",
|
|
1088
484
|
{
|
|
1089
485
|
className: cn(
|
|
@@ -1091,10 +487,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1091
487
|
recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
1092
488
|
),
|
|
1093
489
|
children: [
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1096
|
-
/* @__PURE__ */
|
|
1097
|
-
/* @__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" })
|
|
1098
494
|
] })
|
|
1099
495
|
]
|
|
1100
496
|
}
|
|
@@ -1103,10 +499,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1103
499
|
|
|
1104
500
|
// src/components/MessageList.tsx
|
|
1105
501
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1106
|
-
import { useCallback as
|
|
502
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef6, useState as useState10 } from "react";
|
|
1107
503
|
|
|
1108
504
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1109
|
-
import { useCallback
|
|
505
|
+
import { useCallback, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
|
|
1110
506
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1111
507
|
/**
|
|
1112
508
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1143,12 +539,12 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1143
539
|
mouseDown = false;
|
|
1144
540
|
});
|
|
1145
541
|
var useStickToBottom = (options = {}) => {
|
|
1146
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1147
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1148
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1149
|
-
const optionsRef =
|
|
542
|
+
const [escapedFromLock, updateEscapedFromLock] = useState3(false);
|
|
543
|
+
const [isAtBottom, updateIsAtBottom] = useState3(options.initial !== false);
|
|
544
|
+
const [isNearBottom, setIsNearBottom] = useState3(false);
|
|
545
|
+
const optionsRef = useRef3(null);
|
|
1150
546
|
optionsRef.current = options;
|
|
1151
|
-
const isSelecting =
|
|
547
|
+
const isSelecting = useCallback(() => {
|
|
1152
548
|
if (!mouseDown) {
|
|
1153
549
|
return false;
|
|
1154
550
|
}
|
|
@@ -1159,15 +555,15 @@ var useStickToBottom = (options = {}) => {
|
|
|
1159
555
|
const range = selection.getRangeAt(0);
|
|
1160
556
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
1161
557
|
}, []);
|
|
1162
|
-
const setIsAtBottom =
|
|
558
|
+
const setIsAtBottom = useCallback((isAtBottom2) => {
|
|
1163
559
|
state.isAtBottom = isAtBottom2;
|
|
1164
560
|
updateIsAtBottom(isAtBottom2);
|
|
1165
561
|
}, []);
|
|
1166
|
-
const setEscapedFromLock =
|
|
562
|
+
const setEscapedFromLock = useCallback((escapedFromLock2) => {
|
|
1167
563
|
state.escapedFromLock = escapedFromLock2;
|
|
1168
564
|
updateEscapedFromLock(escapedFromLock2);
|
|
1169
565
|
}, []);
|
|
1170
|
-
const state =
|
|
566
|
+
const state = useMemo2(() => {
|
|
1171
567
|
let lastCalculation;
|
|
1172
568
|
return {
|
|
1173
569
|
escapedFromLock,
|
|
@@ -1220,7 +616,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1220
616
|
}
|
|
1221
617
|
};
|
|
1222
618
|
}, []);
|
|
1223
|
-
const scrollToBottom =
|
|
619
|
+
const scrollToBottom = useCallback((scrollOptions = {}) => {
|
|
1224
620
|
if (typeof scrollOptions === "string") {
|
|
1225
621
|
scrollOptions = { animation: scrollOptions };
|
|
1226
622
|
}
|
|
@@ -1305,11 +701,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1305
701
|
}
|
|
1306
702
|
return next();
|
|
1307
703
|
}, [setIsAtBottom, isSelecting, state]);
|
|
1308
|
-
const stopScroll =
|
|
704
|
+
const stopScroll = useCallback(() => {
|
|
1309
705
|
setEscapedFromLock(true);
|
|
1310
706
|
setIsAtBottom(false);
|
|
1311
707
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
1312
|
-
const handleScroll =
|
|
708
|
+
const handleScroll = useCallback(({ target }) => {
|
|
1313
709
|
if (target !== scrollRef.current) {
|
|
1314
710
|
return;
|
|
1315
711
|
}
|
|
@@ -1348,7 +744,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1348
744
|
}
|
|
1349
745
|
}, 1);
|
|
1350
746
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
1351
|
-
const handleWheel =
|
|
747
|
+
const handleWheel = useCallback(({ target, deltaY }) => {
|
|
1352
748
|
let element = target;
|
|
1353
749
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
1354
750
|
if (!element.parentElement) {
|
|
@@ -1418,7 +814,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1418
814
|
};
|
|
1419
815
|
};
|
|
1420
816
|
function useRefCallback(callback, deps) {
|
|
1421
|
-
const result =
|
|
817
|
+
const result = useCallback((ref) => {
|
|
1422
818
|
result.current = ref;
|
|
1423
819
|
return callback(ref);
|
|
1424
820
|
}, deps);
|
|
@@ -1450,11 +846,11 @@ function mergeAnimations(...animations) {
|
|
|
1450
846
|
|
|
1451
847
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1452
848
|
import * as React from "react";
|
|
1453
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
849
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo3, useRef as useRef4 } from "react";
|
|
1454
850
|
var StickToBottomContext = createContext2(null);
|
|
1455
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
851
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
|
|
1456
852
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1457
|
-
const customTargetScrollTop =
|
|
853
|
+
const customTargetScrollTop = useRef4(null);
|
|
1458
854
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1459
855
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1460
856
|
return get?.(target, elements) ?? target;
|
|
@@ -1468,7 +864,7 @@ function StickToBottom({ instance, children, resize, initial, mass, damping, sti
|
|
|
1468
864
|
targetScrollTop
|
|
1469
865
|
});
|
|
1470
866
|
const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state } = instance ?? defaultInstance;
|
|
1471
|
-
const context =
|
|
867
|
+
const context = useMemo3(() => ({
|
|
1472
868
|
scrollToBottom,
|
|
1473
869
|
stopScroll,
|
|
1474
870
|
scrollRef,
|
|
@@ -1531,10 +927,10 @@ function useStickToBottomContext() {
|
|
|
1531
927
|
|
|
1532
928
|
// src/components/AssistantTurnBlock.tsx
|
|
1533
929
|
import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
|
|
1534
|
-
import { useState as
|
|
930
|
+
import { useState as useState9 } from "react";
|
|
1535
931
|
|
|
1536
932
|
// src/components/AgentLoopBlock.tsx
|
|
1537
|
-
import { useState as
|
|
933
|
+
import { useState as useState4 } from "react";
|
|
1538
934
|
|
|
1539
935
|
// src/components/display-utils.ts
|
|
1540
936
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1656,7 +1052,7 @@ function formatToolResult(result) {
|
|
|
1656
1052
|
}
|
|
1657
1053
|
|
|
1658
1054
|
// src/components/AgentLoopBlock.tsx
|
|
1659
|
-
import { jsx as
|
|
1055
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1660
1056
|
function parseAgentDescription(argumentsJson) {
|
|
1661
1057
|
try {
|
|
1662
1058
|
const parsed = JSON.parse(argumentsJson);
|
|
@@ -1666,12 +1062,12 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1666
1062
|
}
|
|
1667
1063
|
}
|
|
1668
1064
|
function AgentLoopBlock({ toolCall }) {
|
|
1669
|
-
const [expanded, setExpanded] =
|
|
1065
|
+
const [expanded, setExpanded] = useState4(false);
|
|
1670
1066
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1671
1067
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1672
1068
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1673
|
-
return /* @__PURE__ */
|
|
1674
|
-
/* @__PURE__ */
|
|
1069
|
+
return /* @__PURE__ */ jsxs3("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
|
|
1070
|
+
/* @__PURE__ */ jsxs3(
|
|
1675
1071
|
"div",
|
|
1676
1072
|
{
|
|
1677
1073
|
className: cn(
|
|
@@ -1679,7 +1075,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1679
1075
|
failed ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : running ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]"
|
|
1680
1076
|
),
|
|
1681
1077
|
children: [
|
|
1682
|
-
/* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsxs3(
|
|
1683
1079
|
"button",
|
|
1684
1080
|
{
|
|
1685
1081
|
type: "button",
|
|
@@ -1687,7 +1083,7 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1687
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",
|
|
1688
1084
|
"aria-expanded": expanded,
|
|
1689
1085
|
children: [
|
|
1690
|
-
/* @__PURE__ */
|
|
1086
|
+
/* @__PURE__ */ jsx4(
|
|
1691
1087
|
ChevronRight,
|
|
1692
1088
|
{
|
|
1693
1089
|
size: 11,
|
|
@@ -1697,8 +1093,8 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1697
1093
|
)
|
|
1698
1094
|
}
|
|
1699
1095
|
),
|
|
1700
|
-
/* @__PURE__ */
|
|
1701
|
-
/* @__PURE__ */
|
|
1096
|
+
/* @__PURE__ */ jsx4(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1097
|
+
/* @__PURE__ */ jsxs3(
|
|
1702
1098
|
"span",
|
|
1703
1099
|
{
|
|
1704
1100
|
className: cn(
|
|
@@ -1706,43 +1102,156 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1706
1102
|
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1707
1103
|
),
|
|
1708
1104
|
children: [
|
|
1709
|
-
running ? /* @__PURE__ */
|
|
1710
|
-
/* @__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" })
|
|
1711
1107
|
]
|
|
1712
1108
|
}
|
|
1713
1109
|
),
|
|
1714
|
-
/* @__PURE__ */
|
|
1110
|
+
/* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1715
1111
|
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1716
1112
|
description
|
|
1717
1113
|
] })
|
|
1718
1114
|
]
|
|
1719
1115
|
}
|
|
1720
1116
|
),
|
|
1721
|
-
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) })
|
|
1722
1118
|
]
|
|
1723
1119
|
}
|
|
1724
1120
|
),
|
|
1725
|
-
expanded && toolCall.result != null && /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__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) })
|
|
1728
1124
|
] })
|
|
1729
1125
|
] });
|
|
1730
1126
|
}
|
|
1731
1127
|
|
|
1732
1128
|
// src/components/MarkdownContent.tsx
|
|
1733
1129
|
import {
|
|
1734
|
-
useEffect as
|
|
1735
|
-
useMemo as
|
|
1736
|
-
useRef as
|
|
1737
|
-
useState as
|
|
1130
|
+
useEffect as useEffect4,
|
|
1131
|
+
useMemo as useMemo4,
|
|
1132
|
+
useRef as useRef5,
|
|
1133
|
+
useState as useState6
|
|
1738
1134
|
} from "react";
|
|
1739
|
-
|
|
1135
|
+
|
|
1136
|
+
// src/lib/media-tags.ts
|
|
1137
|
+
var FILE_CARD_TAG = "blade-file-card";
|
|
1138
|
+
var MEDIA_LINE_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?(?<wrap>[`"'*_]*)MEDIA:[ \t]*(?<path>.+?)\k<wrap>[ \t]*[。,、;;,]?[ \t]*\r?$/gm;
|
|
1139
|
+
var MEDIA_LINE_START_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?[`"'*_]*MEDIA:/;
|
|
1140
|
+
function basename(path) {
|
|
1141
|
+
return path.split(/[/\\]/).filter(Boolean).pop() || path;
|
|
1142
|
+
}
|
|
1143
|
+
function unwrapMediaPath(path) {
|
|
1144
|
+
if (!path.startsWith("<")) return path;
|
|
1145
|
+
const closingBracket = path.indexOf(">");
|
|
1146
|
+
if (closingBracket < 0) return null;
|
|
1147
|
+
return `${path.slice(1, closingBracket)}${path.slice(closingBracket + 1)}`;
|
|
1148
|
+
}
|
|
1149
|
+
function isSafeMediaPath(path) {
|
|
1150
|
+
if (!path) return false;
|
|
1151
|
+
return !path.split(/[/\\]/).includes("..");
|
|
1152
|
+
}
|
|
1153
|
+
function escapeAttribute(value) {
|
|
1154
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1155
|
+
}
|
|
1156
|
+
function foldMediaLines(text) {
|
|
1157
|
+
return text.replace(MEDIA_LINE_RE, (match, _wrap, rawPath) => {
|
|
1158
|
+
const path = unwrapMediaPath(rawPath);
|
|
1159
|
+
if (path === null) return match;
|
|
1160
|
+
const name = basename(path);
|
|
1161
|
+
if (!isSafeMediaPath(path)) {
|
|
1162
|
+
return `\`${name}\``;
|
|
1163
|
+
}
|
|
1164
|
+
return `<${FILE_CARD_TAG} data-path="${escapeAttribute(path)}" data-name="${escapeAttribute(name)}">${escapeAttribute(name)}</${FILE_CARD_TAG}>`;
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
function collapseMediaTags(text, options) {
|
|
1168
|
+
if (!text.includes("MEDIA:")) return text;
|
|
1169
|
+
if (!options?.streaming) return foldMediaLines(text);
|
|
1170
|
+
const newlineIndex = text.lastIndexOf("\n");
|
|
1171
|
+
const head = newlineIndex < 0 ? "" : text.slice(0, newlineIndex + 1);
|
|
1172
|
+
const lastLine = newlineIndex < 0 ? text : text.slice(newlineIndex + 1);
|
|
1173
|
+
if (MEDIA_LINE_START_RE.test(lastLine)) {
|
|
1174
|
+
return foldMediaLines(head);
|
|
1175
|
+
}
|
|
1176
|
+
return foldMediaLines(head) + lastLine;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// src/components/FileCard.tsx
|
|
1180
|
+
import { useState as useState5 } from "react";
|
|
1181
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1182
|
+
function stringValue(value) {
|
|
1183
|
+
return typeof value === "string" ? value : void 0;
|
|
1184
|
+
}
|
|
1185
|
+
function extractChildrenText(children) {
|
|
1186
|
+
if (typeof children === "string") return children;
|
|
1187
|
+
if (Array.isArray(children)) return children.map(extractChildrenText).join("");
|
|
1188
|
+
if (children && typeof children === "object" && "props" in children) {
|
|
1189
|
+
return extractChildrenText(children.props?.children);
|
|
1190
|
+
}
|
|
1191
|
+
return "";
|
|
1192
|
+
}
|
|
1193
|
+
function FileCard({
|
|
1194
|
+
node,
|
|
1195
|
+
"data-path": pathAttribute,
|
|
1196
|
+
"data-name": nameAttribute,
|
|
1197
|
+
dataPath,
|
|
1198
|
+
dataName,
|
|
1199
|
+
sessionId,
|
|
1200
|
+
children,
|
|
1201
|
+
className,
|
|
1202
|
+
...props
|
|
1203
|
+
}) {
|
|
1204
|
+
const client = useBladeClient();
|
|
1205
|
+
const [downloading, setDownloading] = useState5(false);
|
|
1206
|
+
const [failed, setFailed] = useState5(false);
|
|
1207
|
+
const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
|
|
1208
|
+
const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
|
|
1209
|
+
const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
|
|
1210
|
+
const disabled = !sessionId || !isSafeMediaPath(path);
|
|
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();
|
|
1217
|
+
if (disabled || downloading || !sessionId) return;
|
|
1218
|
+
setDownloading(true);
|
|
1219
|
+
setFailed(false);
|
|
1220
|
+
try {
|
|
1221
|
+
await client.sessions.downloadFile(sessionId, path, name);
|
|
1222
|
+
} catch {
|
|
1223
|
+
setFailed(true);
|
|
1224
|
+
} finally {
|
|
1225
|
+
setDownloading(false);
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
return /* @__PURE__ */ jsxs4("span", { ...props, className: cn("blade-chat-file-card", className), children: [
|
|
1229
|
+
/* @__PURE__ */ jsx5(
|
|
1230
|
+
"a",
|
|
1231
|
+
{
|
|
1232
|
+
href: downloadUrl,
|
|
1233
|
+
download: name,
|
|
1234
|
+
onClick: handleDownload,
|
|
1235
|
+
"aria-disabled": disabled || void 0,
|
|
1236
|
+
"aria-busy": downloading || void 0,
|
|
1237
|
+
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
|
|
1238
|
+
title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : name,
|
|
1239
|
+
className: "blade-chat-file-link",
|
|
1240
|
+
children: name
|
|
1241
|
+
}
|
|
1242
|
+
),
|
|
1243
|
+
failed && /* @__PURE__ */ jsx5("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
|
|
1244
|
+
] });
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// src/components/MarkdownContent.tsx
|
|
1248
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1740
1249
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1741
1250
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1742
|
-
const preRef =
|
|
1743
|
-
const [copied, setCopied] =
|
|
1744
|
-
const [language, setLanguage] =
|
|
1745
|
-
|
|
1251
|
+
const preRef = useRef5(null);
|
|
1252
|
+
const [copied, setCopied] = useState6(false);
|
|
1253
|
+
const [language, setLanguage] = useState6("");
|
|
1254
|
+
useEffect4(() => {
|
|
1746
1255
|
const codeEl = preRef.current?.querySelector("code");
|
|
1747
1256
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1748
1257
|
}, []);
|
|
@@ -1753,10 +1262,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1753
1262
|
setTimeout(() => setCopied(false), 2e3);
|
|
1754
1263
|
}
|
|
1755
1264
|
};
|
|
1756
|
-
return /* @__PURE__ */
|
|
1757
|
-
/* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1759
|
-
/* @__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(
|
|
1760
1269
|
"button",
|
|
1761
1270
|
{
|
|
1762
1271
|
type: "button",
|
|
@@ -1766,13 +1275,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1766
1275
|
copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
|
|
1767
1276
|
),
|
|
1768
1277
|
children: [
|
|
1769
|
-
copied ? /* @__PURE__ */
|
|
1770
|
-
/* @__PURE__ */
|
|
1278
|
+
copied ? /* @__PURE__ */ jsx6(Check, { size: 12 }) : /* @__PURE__ */ jsx6(Copy, { size: 12 }),
|
|
1279
|
+
/* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
|
|
1771
1280
|
]
|
|
1772
1281
|
}
|
|
1773
1282
|
)
|
|
1774
1283
|
] }),
|
|
1775
|
-
/* @__PURE__ */
|
|
1284
|
+
/* @__PURE__ */ jsx6(
|
|
1776
1285
|
"pre",
|
|
1777
1286
|
{
|
|
1778
1287
|
ref: preRef,
|
|
@@ -1784,22 +1293,36 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1784
1293
|
] });
|
|
1785
1294
|
}
|
|
1786
1295
|
function ExternalAnchor({ node: _node, children, ...props }) {
|
|
1787
|
-
return /* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsx6("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
|
|
1788
1297
|
}
|
|
1789
1298
|
var MARKDOWN_COMPONENTS = {
|
|
1790
1299
|
pre: CodeBlockPre,
|
|
1791
1300
|
a: ExternalAnchor
|
|
1792
1301
|
};
|
|
1302
|
+
var CUSTOM_ALLOWED_TAGS = {
|
|
1303
|
+
[FILE_CARD_TAG]: ["dataPath", "dataName", "data-path", "data-name"]
|
|
1304
|
+
};
|
|
1793
1305
|
function MarkdownContent({ children, className, mode, sessionId }) {
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1306
|
+
const streaming = mode === "streaming";
|
|
1307
|
+
const resolvedChildren = useMemo4(() => {
|
|
1308
|
+
const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
|
|
1309
|
+
if (!sessionId) return withoutReminders;
|
|
1310
|
+
return collapseMediaTags(withoutReminders, { streaming });
|
|
1311
|
+
}, [children, sessionId, streaming]);
|
|
1312
|
+
const components = useMemo4(
|
|
1313
|
+
() => ({
|
|
1314
|
+
...MARKDOWN_COMPONENTS,
|
|
1315
|
+
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx6(FileCard, { ...fileCardProps, sessionId })
|
|
1316
|
+
}),
|
|
1317
|
+
[sessionId]
|
|
1318
|
+
);
|
|
1319
|
+
return /* @__PURE__ */ jsx6(
|
|
1798
1320
|
_r,
|
|
1799
1321
|
{
|
|
1800
1322
|
className: cn("blade-chat-markdown break-words", className),
|
|
1801
1323
|
mode: mode ?? "static",
|
|
1802
|
-
components
|
|
1324
|
+
components,
|
|
1325
|
+
allowedTags: CUSTOM_ALLOWED_TAGS,
|
|
1803
1326
|
children: resolvedChildren
|
|
1804
1327
|
},
|
|
1805
1328
|
sessionId ?? "no-session"
|
|
@@ -1807,17 +1330,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
|
|
|
1807
1330
|
}
|
|
1808
1331
|
|
|
1809
1332
|
// src/components/Shimmer.tsx
|
|
1810
|
-
import { jsx as
|
|
1333
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1811
1334
|
function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
1812
|
-
return /* @__PURE__ */
|
|
1335
|
+
return /* @__PURE__ */ jsx7("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
|
|
1813
1336
|
}
|
|
1814
1337
|
|
|
1815
1338
|
// src/components/ToolCallBlock.tsx
|
|
1816
|
-
import { useState as
|
|
1339
|
+
import { useState as useState8 } from "react";
|
|
1817
1340
|
|
|
1818
1341
|
// src/components/AskUserQuestionBlock.tsx
|
|
1819
|
-
import { useEffect as
|
|
1820
|
-
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";
|
|
1821
1344
|
function AskUserQuestionBlock({
|
|
1822
1345
|
data,
|
|
1823
1346
|
answered,
|
|
@@ -1826,16 +1349,16 @@ function AskUserQuestionBlock({
|
|
|
1826
1349
|
answerData,
|
|
1827
1350
|
onAnswer
|
|
1828
1351
|
}) {
|
|
1829
|
-
const [selections, setSelections] =
|
|
1830
|
-
const [customTexts, setCustomTexts] =
|
|
1831
|
-
const [usingCustom, setUsingCustom] =
|
|
1832
|
-
const [submitted, setSubmitted] =
|
|
1833
|
-
|
|
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);
|
|
1356
|
+
useEffect5(() => {
|
|
1834
1357
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1835
1358
|
setSubmitted(false);
|
|
1836
1359
|
}
|
|
1837
1360
|
}, [sessionStatus]);
|
|
1838
|
-
const displayAnswerState =
|
|
1361
|
+
const displayAnswerState = useMemo5(() => {
|
|
1839
1362
|
if (!(answered && answerData)) {
|
|
1840
1363
|
return { selections, customTexts, usingCustom };
|
|
1841
1364
|
}
|
|
@@ -1920,7 +1443,7 @@ ${parts.join("\n")}`;
|
|
|
1920
1443
|
setSubmitted(true);
|
|
1921
1444
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
1922
1445
|
};
|
|
1923
|
-
return /* @__PURE__ */
|
|
1446
|
+
return /* @__PURE__ */ jsxs6(
|
|
1924
1447
|
"div",
|
|
1925
1448
|
{
|
|
1926
1449
|
className: cn(
|
|
@@ -1928,12 +1451,12 @@ ${parts.join("\n")}`;
|
|
|
1928
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"
|
|
1929
1452
|
),
|
|
1930
1453
|
children: [
|
|
1931
|
-
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: [
|
|
1932
1455
|
"\u5B50\u667A\u80FD\u4F53\u300C",
|
|
1933
1456
|
data.source_loop.description,
|
|
1934
1457
|
"\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
|
|
1935
1458
|
] }),
|
|
1936
|
-
data.questions.map((q, qIdx) => /* @__PURE__ */
|
|
1459
|
+
data.questions.map((q, qIdx) => /* @__PURE__ */ jsx8(
|
|
1937
1460
|
QuestionCard,
|
|
1938
1461
|
{
|
|
1939
1462
|
question: q,
|
|
@@ -1948,7 +1471,7 @@ ${parts.join("\n")}`;
|
|
|
1948
1471
|
},
|
|
1949
1472
|
q.question
|
|
1950
1473
|
)),
|
|
1951
|
-
!answered && !submitted && onAnswer && /* @__PURE__ */
|
|
1474
|
+
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx8(
|
|
1952
1475
|
"button",
|
|
1953
1476
|
{
|
|
1954
1477
|
type: "button",
|
|
@@ -1958,14 +1481,14 @@ ${parts.join("\n")}`;
|
|
|
1958
1481
|
children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
|
|
1959
1482
|
}
|
|
1960
1483
|
),
|
|
1961
|
-
submitted && !answered && /* @__PURE__ */
|
|
1484
|
+
submitted && !answered && /* @__PURE__ */ jsxs6(
|
|
1962
1485
|
"button",
|
|
1963
1486
|
{
|
|
1964
1487
|
type: "button",
|
|
1965
1488
|
disabled: true,
|
|
1966
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",
|
|
1967
1490
|
children: [
|
|
1968
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsx8(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
1969
1492
|
"\u786E\u8BA4\u4E2D"
|
|
1970
1493
|
]
|
|
1971
1494
|
}
|
|
@@ -1986,30 +1509,30 @@ function QuestionCard({
|
|
|
1986
1509
|
onCustomChange
|
|
1987
1510
|
}) {
|
|
1988
1511
|
const multi = question.multiSelect ?? false;
|
|
1989
|
-
return /* @__PURE__ */
|
|
1990
|
-
/* @__PURE__ */
|
|
1991
|
-
/* @__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(
|
|
1992
1515
|
MessageSquareMore,
|
|
1993
1516
|
{
|
|
1994
1517
|
size: answered ? 12 : 13,
|
|
1995
1518
|
className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
|
|
1996
1519
|
}
|
|
1997
1520
|
),
|
|
1998
|
-
/* @__PURE__ */
|
|
1521
|
+
/* @__PURE__ */ jsx8(
|
|
1999
1522
|
"div",
|
|
2000
1523
|
{
|
|
2001
1524
|
className: cn(
|
|
2002
1525
|
"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
|
|
2003
1526
|
answered ? "text-xs" : "text-sm"
|
|
2004
1527
|
),
|
|
2005
|
-
children: /* @__PURE__ */
|
|
1528
|
+
children: /* @__PURE__ */ jsx8(MarkdownContent, { className: "blade-chat-prose", children: question.question })
|
|
2006
1529
|
}
|
|
2007
1530
|
)
|
|
2008
1531
|
] }),
|
|
2009
|
-
/* @__PURE__ */
|
|
1532
|
+
/* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
|
|
2010
1533
|
question.options.map((opt, optIdx) => {
|
|
2011
1534
|
const isSel = selected.has(optIdx);
|
|
2012
|
-
return /* @__PURE__ */
|
|
1535
|
+
return /* @__PURE__ */ jsxs6(
|
|
2013
1536
|
"button",
|
|
2014
1537
|
{
|
|
2015
1538
|
type: "button",
|
|
@@ -2023,14 +1546,14 @@ function QuestionCard({
|
|
|
2023
1546
|
answered && "cursor-default opacity-70"
|
|
2024
1547
|
),
|
|
2025
1548
|
children: [
|
|
2026
|
-
multi && /* @__PURE__ */
|
|
1549
|
+
multi && /* @__PURE__ */ jsx8(
|
|
2027
1550
|
"div",
|
|
2028
1551
|
{
|
|
2029
1552
|
className: cn(
|
|
2030
1553
|
"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
|
|
2031
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))]"
|
|
2032
1555
|
),
|
|
2033
|
-
children: isSel && /* @__PURE__ */
|
|
1556
|
+
children: isSel && /* @__PURE__ */ jsx8(
|
|
2034
1557
|
Check,
|
|
2035
1558
|
{
|
|
2036
1559
|
size: 9,
|
|
@@ -2039,9 +1562,9 @@ function QuestionCard({
|
|
|
2039
1562
|
)
|
|
2040
1563
|
}
|
|
2041
1564
|
),
|
|
2042
|
-
/* @__PURE__ */
|
|
2043
|
-
/* @__PURE__ */
|
|
2044
|
-
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(
|
|
2045
1568
|
"div",
|
|
2046
1569
|
{
|
|
2047
1570
|
className: cn(
|
|
@@ -2058,7 +1581,7 @@ function QuestionCard({
|
|
|
2058
1581
|
opt.label
|
|
2059
1582
|
);
|
|
2060
1583
|
}),
|
|
2061
|
-
answered && !isCustom ? null : /* @__PURE__ */
|
|
1584
|
+
answered && !isCustom ? null : /* @__PURE__ */ jsxs6(
|
|
2062
1585
|
"div",
|
|
2063
1586
|
{
|
|
2064
1587
|
className: cn(
|
|
@@ -2068,8 +1591,8 @@ function QuestionCard({
|
|
|
2068
1591
|
answered && "cursor-default opacity-70"
|
|
2069
1592
|
),
|
|
2070
1593
|
children: [
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
1594
|
+
/* @__PURE__ */ jsx8("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
1595
|
+
/* @__PURE__ */ jsx8(
|
|
2073
1596
|
"input",
|
|
2074
1597
|
{
|
|
2075
1598
|
type: "text",
|
|
@@ -2137,7 +1660,7 @@ function normalizeOptionItem(value) {
|
|
|
2137
1660
|
}
|
|
2138
1661
|
|
|
2139
1662
|
// src/components/ToolCallBlock.tsx
|
|
2140
|
-
import { Fragment, jsx as
|
|
1663
|
+
import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2141
1664
|
function resolveAskQuestionState({
|
|
2142
1665
|
toolStatus,
|
|
2143
1666
|
hasAnswerData,
|
|
@@ -2157,12 +1680,12 @@ function ToolCallBlock({
|
|
|
2157
1680
|
sessionStatus,
|
|
2158
1681
|
renderer
|
|
2159
1682
|
}) {
|
|
2160
|
-
const [expanded, setExpanded] =
|
|
1683
|
+
const [expanded, setExpanded] = useState8(false);
|
|
2161
1684
|
const normalizedName = formatToolName(toolCall.name);
|
|
2162
1685
|
if (renderer) {
|
|
2163
1686
|
const custom = renderer(toolCall);
|
|
2164
1687
|
if (custom !== null && custom !== void 0) {
|
|
2165
|
-
return /* @__PURE__ */
|
|
1688
|
+
return /* @__PURE__ */ jsx9(Fragment, { children: custom });
|
|
2166
1689
|
}
|
|
2167
1690
|
}
|
|
2168
1691
|
if (normalizedName === "AskUserQuestion") {
|
|
@@ -2174,7 +1697,7 @@ function ToolCallBlock({
|
|
|
2174
1697
|
});
|
|
2175
1698
|
const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
|
|
2176
1699
|
if (askData) {
|
|
2177
|
-
return /* @__PURE__ */
|
|
1700
|
+
return /* @__PURE__ */ jsx9(
|
|
2178
1701
|
AskUserQuestionBlock,
|
|
2179
1702
|
{
|
|
2180
1703
|
data: askData,
|
|
@@ -2187,24 +1710,24 @@ function ToolCallBlock({
|
|
|
2187
1710
|
);
|
|
2188
1711
|
}
|
|
2189
1712
|
if (toolCall.status === "pending") {
|
|
2190
|
-
return /* @__PURE__ */
|
|
2191
|
-
/* @__PURE__ */
|
|
2192
|
-
/* @__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" })
|
|
2193
1716
|
] });
|
|
2194
1717
|
}
|
|
2195
|
-
return /* @__PURE__ */
|
|
2196
|
-
/* @__PURE__ */
|
|
2197
|
-
/* @__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" })
|
|
2198
1721
|
] });
|
|
2199
1722
|
}
|
|
2200
1723
|
const tone = getToolTone(toolCall.status);
|
|
2201
1724
|
const displayName = getToolDisplayLabel(toolCall);
|
|
2202
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))]";
|
|
2203
|
-
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 });
|
|
2204
1727
|
const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
|
|
2205
|
-
return /* @__PURE__ */
|
|
2206
|
-
/* @__PURE__ */
|
|
2207
|
-
/* @__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(
|
|
2208
1731
|
"button",
|
|
2209
1732
|
{
|
|
2210
1733
|
type: "button",
|
|
@@ -2212,7 +1735,7 @@ function ToolCallBlock({
|
|
|
2212
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",
|
|
2213
1736
|
"aria-expanded": expanded,
|
|
2214
1737
|
children: [
|
|
2215
|
-
/* @__PURE__ */
|
|
1738
|
+
/* @__PURE__ */ jsx9(
|
|
2216
1739
|
ChevronRight,
|
|
2217
1740
|
{
|
|
2218
1741
|
size: 11,
|
|
@@ -2222,24 +1745,24 @@ function ToolCallBlock({
|
|
|
2222
1745
|
)
|
|
2223
1746
|
}
|
|
2224
1747
|
),
|
|
2225
|
-
/* @__PURE__ */
|
|
1748
|
+
/* @__PURE__ */ jsxs7("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
|
|
2226
1749
|
statusIcon,
|
|
2227
|
-
/* @__PURE__ */
|
|
1750
|
+
/* @__PURE__ */ jsx9("span", { children: getToolStatusLabel(toolCall.status) })
|
|
2228
1751
|
] }),
|
|
2229
|
-
/* @__PURE__ */
|
|
1752
|
+
/* @__PURE__ */ jsx9("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
|
|
2230
1753
|
]
|
|
2231
1754
|
}
|
|
2232
1755
|
),
|
|
2233
|
-
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) })
|
|
2234
1757
|
] }),
|
|
2235
|
-
expanded && /* @__PURE__ */
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2238
|
-
/* @__PURE__ */
|
|
2239
|
-
/* @__PURE__ */
|
|
2240
|
-
toolCall.result != null && /* @__PURE__ */
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__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) })
|
|
2243
1766
|
] })
|
|
2244
1767
|
] })
|
|
2245
1768
|
] });
|
|
@@ -2257,11 +1780,11 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2257
1780
|
}
|
|
2258
1781
|
|
|
2259
1782
|
// src/components/AssistantTurnBlock.tsx
|
|
2260
|
-
import { jsx as
|
|
1783
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2261
1784
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2262
|
-
const [open, setOpen] =
|
|
2263
|
-
return /* @__PURE__ */
|
|
2264
|
-
/* @__PURE__ */
|
|
1785
|
+
const [open, setOpen] = useState9(false);
|
|
1786
|
+
return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
|
|
1787
|
+
/* @__PURE__ */ jsxs8(
|
|
2265
1788
|
"button",
|
|
2266
1789
|
{
|
|
2267
1790
|
type: "button",
|
|
@@ -2269,14 +1792,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
2269
1792
|
"aria-expanded": open,
|
|
2270
1793
|
className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2271
1794
|
children: [
|
|
2272
|
-
/* @__PURE__ */
|
|
2273
|
-
isStreaming ? /* @__PURE__ */
|
|
2274
|
-
/* @__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: [
|
|
2275
1798
|
"\xB7 ",
|
|
2276
1799
|
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
2277
1800
|
" \u5B57"
|
|
2278
1801
|
] }),
|
|
2279
|
-
/* @__PURE__ */
|
|
1802
|
+
/* @__PURE__ */ jsx10(
|
|
2280
1803
|
ChevronDown,
|
|
2281
1804
|
{
|
|
2282
1805
|
size: 12,
|
|
@@ -2286,7 +1809,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
2286
1809
|
]
|
|
2287
1810
|
}
|
|
2288
1811
|
),
|
|
2289
|
-
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 })
|
|
2290
1813
|
] });
|
|
2291
1814
|
}
|
|
2292
1815
|
function getMessageText(message) {
|
|
@@ -2312,21 +1835,21 @@ function AssistantTurnBlock({
|
|
|
2312
1835
|
(message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
2313
1836
|
);
|
|
2314
1837
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
2315
|
-
return /* @__PURE__ */
|
|
2316
|
-
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" }),
|
|
2317
1840
|
messages.map((message, index) => {
|
|
2318
1841
|
const isLast = index === messages.length - 1;
|
|
2319
1842
|
const streamingThis = isStreaming && isLast;
|
|
2320
1843
|
const text = getMessageText(message);
|
|
2321
1844
|
const toolCalls = message.tool_calls ?? [];
|
|
2322
1845
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
2323
|
-
return /* @__PURE__ */
|
|
1846
|
+
return /* @__PURE__ */ jsxs8(
|
|
2324
1847
|
"div",
|
|
2325
1848
|
{
|
|
2326
1849
|
className: "flex flex-col gap-3",
|
|
2327
1850
|
children: [
|
|
2328
|
-
showReasoning && message.reasoning && /* @__PURE__ */
|
|
2329
|
-
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(
|
|
2330
1853
|
MarkdownContent,
|
|
2331
1854
|
{
|
|
2332
1855
|
mode: streamingThis ? "streaming" : "static",
|
|
@@ -2335,8 +1858,8 @@ function AssistantTurnBlock({
|
|
|
2335
1858
|
children: text
|
|
2336
1859
|
}
|
|
2337
1860
|
) }),
|
|
2338
|
-
toolCalls.length > 0 && /* @__PURE__ */
|
|
2339
|
-
(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(
|
|
2340
1863
|
ToolCallBlock,
|
|
2341
1864
|
{
|
|
2342
1865
|
toolCall,
|
|
@@ -2354,13 +1877,13 @@ function AssistantTurnBlock({
|
|
|
2354
1877
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2355
1878
|
);
|
|
2356
1879
|
}),
|
|
2357
|
-
isStreaming && !hasAnyContent && /* @__PURE__ */
|
|
1880
|
+
isStreaming && !hasAnyContent && /* @__PURE__ */ jsx10(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
|
|
2358
1881
|
] });
|
|
2359
1882
|
}
|
|
2360
1883
|
|
|
2361
1884
|
// src/components/RenderErrorBoundary.tsx
|
|
2362
1885
|
import { Component } from "react";
|
|
2363
|
-
import { jsx as
|
|
1886
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2364
1887
|
function getFirstComponentName(componentStack) {
|
|
2365
1888
|
const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
|
|
2366
1889
|
return match?.[1] ?? null;
|
|
@@ -2393,426 +1916,40 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2393
1916
|
return children;
|
|
2394
1917
|
}
|
|
2395
1918
|
const componentName = getFirstComponentName(componentStack);
|
|
2396
|
-
return /* @__PURE__ */
|
|
2397
|
-
/* @__PURE__ */
|
|
2398
|
-
/* @__PURE__ */
|
|
2399
|
-
/* @__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: [
|
|
2400
1923
|
label,
|
|
2401
1924
|
"\u6E32\u67D3\u5931\u8D25"
|
|
2402
1925
|
] }),
|
|
2403
|
-
/* @__PURE__ */
|
|
1926
|
+
/* @__PURE__ */ jsxs9("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
|
|
2404
1927
|
componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
|
|
2405
1928
|
error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
|
|
2406
1929
|
] }),
|
|
2407
|
-
details ? /* @__PURE__ */
|
|
1930
|
+
details ? /* @__PURE__ */ jsx11("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
|
|
2408
1931
|
] })
|
|
2409
1932
|
] }) });
|
|
2410
1933
|
}
|
|
2411
1934
|
};
|
|
2412
1935
|
|
|
2413
|
-
// src/components/
|
|
2414
|
-
import {
|
|
2415
|
-
import {
|
|
2416
|
-
function
|
|
2417
|
-
|
|
2418
|
-
callback?.(event);
|
|
2419
|
-
} catch {
|
|
2420
|
-
}
|
|
1936
|
+
// src/components/UserMessageBubble.tsx
|
|
1937
|
+
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
1938
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1939
|
+
function isUserMessage(message) {
|
|
1940
|
+
return message.role === "user";
|
|
2421
1941
|
}
|
|
2422
|
-
function
|
|
2423
|
-
return
|
|
1942
|
+
function isErrorMessage(message) {
|
|
1943
|
+
return message.role === "error";
|
|
2424
1944
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
const client = useBladeClient();
|
|
2434
|
-
const [downloading, setDownloading] = useState11(false);
|
|
2435
|
-
const name = artifact.label || basename(artifact.target);
|
|
2436
|
-
if (artifact.kind === "link") {
|
|
2437
|
-
return /* @__PURE__ */ jsxs11(
|
|
2438
|
-
"a",
|
|
2439
|
-
{
|
|
2440
|
-
href: artifact.target,
|
|
2441
|
-
target: "_blank",
|
|
2442
|
-
rel: "noopener noreferrer",
|
|
2443
|
-
onClick: () => onArtifactOpened(artifactIndex, "link"),
|
|
2444
|
-
title: `${name}
|
|
2445
|
-
${artifact.target}`,
|
|
2446
|
-
className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]",
|
|
2447
|
-
children: [
|
|
2448
|
-
/* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
2449
|
-
/* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
2450
|
-
/* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2451
|
-
]
|
|
2452
|
-
}
|
|
2453
|
-
);
|
|
2454
|
-
}
|
|
2455
|
-
const fileName = basename(artifact.target);
|
|
2456
|
-
const downloadUrl = sessionId ? client.buildAuthedUrl(
|
|
2457
|
-
`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`
|
|
2458
|
-
) : void 0;
|
|
2459
|
-
const handleDownload = async (event) => {
|
|
2460
|
-
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
2461
|
-
event.preventDefault();
|
|
2462
|
-
if (!sessionId || downloading) return;
|
|
2463
|
-
setDownloading(true);
|
|
2464
|
-
emitInteraction(onInteraction, {
|
|
2465
|
-
type: "artifact_download_started",
|
|
2466
|
-
sessionId,
|
|
2467
|
-
assistantEntryId,
|
|
2468
|
-
artifactIndex,
|
|
2469
|
-
artifactKind: "file"
|
|
2470
|
-
});
|
|
2471
|
-
try {
|
|
2472
|
-
await client.sessions.downloadFile(sessionId, artifact.target, fileName);
|
|
2473
|
-
emitInteraction(onInteraction, {
|
|
2474
|
-
type: "artifact_download_succeeded",
|
|
2475
|
-
sessionId,
|
|
2476
|
-
assistantEntryId,
|
|
2477
|
-
artifactIndex,
|
|
2478
|
-
artifactKind: "file"
|
|
2479
|
-
});
|
|
2480
|
-
} catch {
|
|
2481
|
-
} finally {
|
|
2482
|
-
setDownloading(false);
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
return /* @__PURE__ */ jsx13(
|
|
2486
|
-
"a",
|
|
2487
|
-
{
|
|
2488
|
-
href: downloadUrl,
|
|
2489
|
-
download: fileName,
|
|
2490
|
-
onClick: handleDownload,
|
|
2491
|
-
title: fileName,
|
|
2492
|
-
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${fileName}`,
|
|
2493
|
-
"aria-disabled": !sessionId || void 0,
|
|
2494
|
-
"aria-busy": downloading || void 0,
|
|
2495
|
-
className: "min-w-0 cursor-pointer break-all text-xs text-[hsl(var(--primary))] underline aria-disabled:cursor-not-allowed aria-disabled:opacity-60 aria-busy:cursor-wait",
|
|
2496
|
-
children: fileName
|
|
2497
|
-
}
|
|
2498
|
-
);
|
|
2499
|
-
}
|
|
2500
|
-
var FEEDBACK_REASONS = [
|
|
2501
|
-
{ value: "not_solved", label: "\u6CA1\u89E3\u51B3\u95EE\u9898" },
|
|
2502
|
-
{ value: "inaccurate", label: "\u5185\u5BB9\u4E0D\u51C6\u786E" },
|
|
2503
|
-
{ value: "incomplete", label: "\u5185\u5BB9\u4E0D\u5B8C\u6574" },
|
|
2504
|
-
{ value: "needs_major_changes", label: "\u9700\u8981\u5927\u91CF\u4FEE\u6539" },
|
|
2505
|
-
{ value: "too_slow", label: "\u592A\u6162" },
|
|
2506
|
-
{ value: "other", label: "\u5176\u4ED6" }
|
|
2507
|
-
];
|
|
2508
|
-
function feedbackReasonLabel(reason) {
|
|
2509
|
-
return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null;
|
|
2510
|
-
}
|
|
2511
|
-
function HistoricalResultFeedback({ feedback }) {
|
|
2512
|
-
const label = feedbackReasonLabel(feedback.reason);
|
|
2513
|
-
return /* @__PURE__ */ jsxs11(
|
|
2514
|
-
"section",
|
|
2515
|
-
{
|
|
2516
|
-
"aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
|
|
2517
|
-
className: "mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
2518
|
-
children: [
|
|
2519
|
-
/* @__PURE__ */ jsxs11("span", { children: [
|
|
2520
|
-
"\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
|
|
2521
|
-
feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
|
|
2522
|
-
] }),
|
|
2523
|
-
label ? /* @__PURE__ */ jsxs11("span", { children: [
|
|
2524
|
-
" \xB7 ",
|
|
2525
|
-
label
|
|
2526
|
-
] }) : null
|
|
2527
|
-
]
|
|
2528
|
-
}
|
|
2529
|
-
);
|
|
2530
|
-
}
|
|
2531
|
-
function ResultFeedback({
|
|
2532
|
-
followup,
|
|
2533
|
-
sessionId,
|
|
2534
|
-
isViewer,
|
|
2535
|
-
onInteraction,
|
|
2536
|
-
savedFeedback,
|
|
2537
|
-
onFeedbackSaved
|
|
2538
|
-
}) {
|
|
2539
|
-
const client = useBladeClient();
|
|
2540
|
-
const [saved, setSaved] = useState11(savedFeedback ?? null);
|
|
2541
|
-
const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
|
|
2542
|
-
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
2543
|
-
const [saving, setSaving] = useState11(false);
|
|
2544
|
-
const [saveError, setSaveError] = useState11(false);
|
|
2545
|
-
const reportedShown = useRef7(false);
|
|
2546
|
-
const latestChoice = useRef7(null);
|
|
2547
|
-
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2548
|
-
useEffect7(() => {
|
|
2549
|
-
if (!eligible || reportedShown.current) return;
|
|
2550
|
-
reportedShown.current = true;
|
|
2551
|
-
emitInteraction(onInteraction, {
|
|
2552
|
-
type: "result_feedback_shown",
|
|
2553
|
-
sessionId,
|
|
2554
|
-
assistantEntryId: followup.assistant_entry_id
|
|
2555
|
-
});
|
|
2556
|
-
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2557
|
-
useEffect7(() => {
|
|
2558
|
-
if (!savedFeedback || latestChoice.current) return;
|
|
2559
|
-
setSaved(savedFeedback);
|
|
2560
|
-
setHelpful(savedFeedback.helpful);
|
|
2561
|
-
setReason(savedFeedback.reason);
|
|
2562
|
-
}, [savedFeedback]);
|
|
2563
|
-
const submit = useCallback5(
|
|
2564
|
-
async (nextHelpful, nextReason) => {
|
|
2565
|
-
if (!sessionId) return;
|
|
2566
|
-
const choice = { helpful: nextHelpful, reason: nextReason };
|
|
2567
|
-
latestChoice.current = choice;
|
|
2568
|
-
setHelpful(nextHelpful);
|
|
2569
|
-
setReason(nextReason);
|
|
2570
|
-
setSaving(true);
|
|
2571
|
-
setSaveError(false);
|
|
2572
|
-
try {
|
|
2573
|
-
const result = await client.sessions.putResultFeedback(
|
|
2574
|
-
sessionId,
|
|
2575
|
-
followup.assistant_entry_id,
|
|
2576
|
-
choice
|
|
2577
|
-
);
|
|
2578
|
-
if (latestChoice.current !== choice) return;
|
|
2579
|
-
setSaved(result);
|
|
2580
|
-
onFeedbackSaved?.(result);
|
|
2581
|
-
emitInteraction(onInteraction, {
|
|
2582
|
-
type: "result_feedback_submitted",
|
|
2583
|
-
sessionId,
|
|
2584
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2585
|
-
helpful: result.helpful,
|
|
2586
|
-
reason: result.reason,
|
|
2587
|
-
updatedAt: result.updated_at
|
|
2588
|
-
});
|
|
2589
|
-
} catch {
|
|
2590
|
-
if (latestChoice.current === choice) setSaveError(true);
|
|
2591
|
-
} finally {
|
|
2592
|
-
if (latestChoice.current === choice) setSaving(false);
|
|
2593
|
-
}
|
|
2594
|
-
},
|
|
2595
|
-
[client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
|
|
2596
|
-
);
|
|
2597
|
-
if (!eligible) return null;
|
|
2598
|
-
return /* @__PURE__ */ jsxs11(
|
|
2599
|
-
"section",
|
|
2600
|
-
{
|
|
2601
|
-
"aria-label": "\u7ED3\u679C\u53CD\u9988",
|
|
2602
|
-
className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
|
|
2603
|
-
children: [
|
|
2604
|
-
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
|
|
2605
|
-
/* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
2606
|
-
/* @__PURE__ */ jsx13(
|
|
2607
|
-
"button",
|
|
2608
|
-
{
|
|
2609
|
-
type: "button",
|
|
2610
|
-
"aria-pressed": helpful === true,
|
|
2611
|
-
disabled: saving,
|
|
2612
|
-
onClick: () => void submit(true, null),
|
|
2613
|
-
className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
|
|
2614
|
-
children: "\u6709\u5E2E\u52A9"
|
|
2615
|
-
}
|
|
2616
|
-
),
|
|
2617
|
-
/* @__PURE__ */ jsx13(
|
|
2618
|
-
"button",
|
|
2619
|
-
{
|
|
2620
|
-
type: "button",
|
|
2621
|
-
"aria-pressed": helpful === false,
|
|
2622
|
-
disabled: saving,
|
|
2623
|
-
onClick: () => void submit(false, reason),
|
|
2624
|
-
className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
|
|
2625
|
-
children: "\u6CA1\u5E2E\u52A9"
|
|
2626
|
-
}
|
|
2627
|
-
)
|
|
2628
|
-
] }),
|
|
2629
|
-
helpful === false ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx13(
|
|
2630
|
-
"button",
|
|
2631
|
-
{
|
|
2632
|
-
type: "button",
|
|
2633
|
-
"aria-pressed": reason === item.value,
|
|
2634
|
-
disabled: saving,
|
|
2635
|
-
onClick: () => void submit(false, item.value),
|
|
2636
|
-
className: "rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60",
|
|
2637
|
-
children: item.label
|
|
2638
|
-
},
|
|
2639
|
-
item.value
|
|
2640
|
-
)) }) : null,
|
|
2641
|
-
saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
|
|
2642
|
-
/* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
|
|
2643
|
-
/* @__PURE__ */ jsx13(
|
|
2644
|
-
"button",
|
|
2645
|
-
{
|
|
2646
|
-
type: "button",
|
|
2647
|
-
className: "underline",
|
|
2648
|
-
onClick: () => {
|
|
2649
|
-
const choice = latestChoice.current;
|
|
2650
|
-
if (choice) void submit(choice.helpful, choice.reason);
|
|
2651
|
-
},
|
|
2652
|
-
children: "\u91CD\u8BD5"
|
|
2653
|
-
}
|
|
2654
|
-
)
|
|
2655
|
-
] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
|
|
2656
|
-
]
|
|
2657
|
-
}
|
|
2658
|
-
);
|
|
2659
|
-
}
|
|
2660
|
-
function PostChatFollowupBlock({
|
|
2661
|
-
followup,
|
|
2662
|
-
sessionId,
|
|
2663
|
-
onSuggestion,
|
|
2664
|
-
isViewer = false,
|
|
2665
|
-
onInteraction,
|
|
2666
|
-
savedFeedback,
|
|
2667
|
-
onFeedbackSaved
|
|
2668
|
-
}) {
|
|
2669
|
-
const [expanded, setExpanded] = useState11(false);
|
|
2670
|
-
const adopted = useRef7(/* @__PURE__ */ new Set());
|
|
2671
|
-
const reportedSuggestions = useRef7(false);
|
|
2672
|
-
const reportedArtifacts = useRef7(/* @__PURE__ */ new Set());
|
|
2673
|
-
const openedArtifacts = useRef7(/* @__PURE__ */ new Set());
|
|
2674
|
-
const artifacts = followup.final_artifacts ?? [];
|
|
2675
|
-
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2676
|
-
useEffect7(() => {
|
|
2677
|
-
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2678
|
-
reportedSuggestions.current = true;
|
|
2679
|
-
emitInteraction(onInteraction, {
|
|
2680
|
-
type: "suggestions_shown",
|
|
2681
|
-
sessionId,
|
|
2682
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2683
|
-
count: followup.suggestions.length
|
|
2684
|
-
});
|
|
2685
|
-
}
|
|
2686
|
-
for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {
|
|
2687
|
-
if (reportedArtifacts.current.has(artifactIndex)) continue;
|
|
2688
|
-
const artifact = visibleArtifacts[artifactIndex];
|
|
2689
|
-
if (!artifact) continue;
|
|
2690
|
-
reportedArtifacts.current.add(artifactIndex);
|
|
2691
|
-
emitInteraction(onInteraction, {
|
|
2692
|
-
type: "artifact_shown",
|
|
2693
|
-
sessionId,
|
|
2694
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2695
|
-
artifactIndex,
|
|
2696
|
-
artifactKind: artifact.kind
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
}, [
|
|
2700
|
-
followup.assistant_entry_id,
|
|
2701
|
-
followup.suggestions.length,
|
|
2702
|
-
onInteraction,
|
|
2703
|
-
sessionId,
|
|
2704
|
-
visibleArtifacts
|
|
2705
|
-
]);
|
|
2706
|
-
const reportArtifactOpened = useCallback5(
|
|
2707
|
-
(artifactIndex, artifactKind) => {
|
|
2708
|
-
if (openedArtifacts.current.has(artifactIndex)) return;
|
|
2709
|
-
openedArtifacts.current.add(artifactIndex);
|
|
2710
|
-
emitInteraction(onInteraction, {
|
|
2711
|
-
type: "artifact_opened",
|
|
2712
|
-
sessionId,
|
|
2713
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2714
|
-
artifactIndex,
|
|
2715
|
-
artifactKind
|
|
2716
|
-
});
|
|
2717
|
-
},
|
|
2718
|
-
[followup.assistant_entry_id, onInteraction, sessionId]
|
|
2719
|
-
);
|
|
2720
|
-
if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
|
|
2721
|
-
return null;
|
|
2722
|
-
return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
|
|
2723
|
-
followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
|
|
2724
|
-
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
|
|
2725
|
-
/* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
|
|
2726
|
-
"\u672C\u8F6E\u5C0F\u7ED3"
|
|
2727
|
-
] }),
|
|
2728
|
-
followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
|
|
2729
|
-
artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2730
|
-
/* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
|
|
2731
|
-
ArtifactCard,
|
|
2732
|
-
{
|
|
2733
|
-
artifact,
|
|
2734
|
-
sessionId,
|
|
2735
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2736
|
-
artifactIndex,
|
|
2737
|
-
onInteraction,
|
|
2738
|
-
onArtifactOpened: reportArtifactOpened
|
|
2739
|
-
},
|
|
2740
|
-
`${artifact.kind}:${artifactIndex}`
|
|
2741
|
-
)) }),
|
|
2742
|
-
artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
|
|
2743
|
-
"button",
|
|
2744
|
-
{
|
|
2745
|
-
type: "button",
|
|
2746
|
-
onClick: () => setExpanded((value) => !value),
|
|
2747
|
-
"aria-expanded": expanded,
|
|
2748
|
-
className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
|
|
2749
|
-
children: [
|
|
2750
|
-
expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
|
|
2751
|
-
/* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
|
|
2752
|
-
]
|
|
2753
|
-
}
|
|
2754
|
-
) : null
|
|
2755
|
-
] }) : null
|
|
2756
|
-
] }) : null,
|
|
2757
|
-
followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
|
|
2758
|
-
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
|
|
2759
|
-
followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
|
|
2760
|
-
"button",
|
|
2761
|
-
{
|
|
2762
|
-
type: "button",
|
|
2763
|
-
disabled: isViewer,
|
|
2764
|
-
onClick: () => {
|
|
2765
|
-
if (!adopted.current.has(suggestionIndex)) {
|
|
2766
|
-
adopted.current.add(suggestionIndex);
|
|
2767
|
-
emitInteraction(onInteraction, {
|
|
2768
|
-
type: "suggestion_adopted",
|
|
2769
|
-
sessionId,
|
|
2770
|
-
assistantEntryId: followup.assistant_entry_id,
|
|
2771
|
-
suggestionIndex
|
|
2772
|
-
});
|
|
2773
|
-
}
|
|
2774
|
-
onSuggestion?.(suggestion);
|
|
2775
|
-
},
|
|
2776
|
-
className: "group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60",
|
|
2777
|
-
children: [
|
|
2778
|
-
/* @__PURE__ */ jsx13("span", { children: suggestion }),
|
|
2779
|
-
/* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
|
|
2780
|
-
]
|
|
2781
|
-
},
|
|
2782
|
-
suggestion
|
|
2783
|
-
))
|
|
2784
|
-
] }) : null,
|
|
2785
|
-
/* @__PURE__ */ jsx13(
|
|
2786
|
-
ResultFeedback,
|
|
2787
|
-
{
|
|
2788
|
-
followup,
|
|
2789
|
-
sessionId,
|
|
2790
|
-
isViewer,
|
|
2791
|
-
onInteraction,
|
|
2792
|
-
savedFeedback,
|
|
2793
|
-
onFeedbackSaved
|
|
2794
|
-
}
|
|
2795
|
-
)
|
|
2796
|
-
] });
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
// src/components/UserMessageBubble.tsx
|
|
2800
|
-
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
2801
|
-
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2802
|
-
function isUserMessage(message) {
|
|
2803
|
-
return message.role === "user";
|
|
2804
|
-
}
|
|
2805
|
-
function isErrorMessage(message) {
|
|
2806
|
-
return message.role === "error";
|
|
2807
|
-
}
|
|
2808
|
-
var isSending = (message) => message.status === "streaming";
|
|
2809
|
-
function UserMessageBubble({ message, className }) {
|
|
2810
|
-
const text = getTextContent2(message.content).trim();
|
|
2811
|
-
const fileParts = getFileParts(message.content);
|
|
2812
|
-
const imageParts = getImageParts(message.content);
|
|
2813
|
-
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs12("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
|
|
2814
|
-
imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
|
|
2815
|
-
"img",
|
|
1945
|
+
var isSending = (message) => message.status === "streaming";
|
|
1946
|
+
function UserMessageBubble({ message, className }) {
|
|
1947
|
+
const text = getTextContent2(message.content).trim();
|
|
1948
|
+
const fileParts = getFileParts(message.content);
|
|
1949
|
+
const imageParts = getImageParts(message.content);
|
|
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(
|
|
1952
|
+
"img",
|
|
2816
1953
|
{
|
|
2817
1954
|
src: part.image_url.url,
|
|
2818
1955
|
alt: "\u7528\u6237\u4E0A\u4F20\u7684\u56FE\u7247",
|
|
@@ -2820,21 +1957,21 @@ function UserMessageBubble({ message, className }) {
|
|
|
2820
1957
|
},
|
|
2821
1958
|
part.image_url.url
|
|
2822
1959
|
)) }),
|
|
2823
|
-
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(
|
|
2824
1961
|
"div",
|
|
2825
1962
|
{
|
|
2826
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))]",
|
|
2827
1964
|
children: [
|
|
2828
|
-
/* @__PURE__ */
|
|
2829
|
-
/* @__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 })
|
|
2830
1967
|
]
|
|
2831
1968
|
},
|
|
2832
1969
|
`${part.name}-${part.data.length}`
|
|
2833
1970
|
)) }),
|
|
2834
|
-
text && /* @__PURE__ */
|
|
2835
|
-
text && isSending(message) && /* @__PURE__ */
|
|
2836
|
-
/* @__PURE__ */
|
|
2837
|
-
/* @__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" })
|
|
2838
1975
|
] })
|
|
2839
1976
|
] }) });
|
|
2840
1977
|
}
|
|
@@ -2843,11 +1980,11 @@ function ErrorMessageBlock({
|
|
|
2843
1980
|
className
|
|
2844
1981
|
}) {
|
|
2845
1982
|
const text = getTextContent2(message.content);
|
|
2846
|
-
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 }) });
|
|
2847
1984
|
}
|
|
2848
1985
|
|
|
2849
1986
|
// src/components/MessageList.tsx
|
|
2850
|
-
import { jsx as
|
|
1987
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2851
1988
|
function parseModeChange(message) {
|
|
2852
1989
|
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
2853
1990
|
return null;
|
|
@@ -2881,8 +2018,6 @@ function getMessageResetSignature(messages) {
|
|
|
2881
2018
|
}
|
|
2882
2019
|
function MessageList({
|
|
2883
2020
|
messages,
|
|
2884
|
-
postChatFollowup,
|
|
2885
|
-
onSuggestion,
|
|
2886
2021
|
isStreaming,
|
|
2887
2022
|
sessionStatus,
|
|
2888
2023
|
askAnswers,
|
|
@@ -2890,13 +2025,9 @@ function MessageList({
|
|
|
2890
2025
|
toolCallRenderer,
|
|
2891
2026
|
emptyState,
|
|
2892
2027
|
className,
|
|
2893
|
-
sessionId
|
|
2894
|
-
isViewer = false,
|
|
2895
|
-
onFollowupInteraction,
|
|
2896
|
-
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
2897
|
-
onResultFeedbackSaved
|
|
2028
|
+
sessionId
|
|
2898
2029
|
}) {
|
|
2899
|
-
const renderBlocks =
|
|
2030
|
+
const renderBlocks = useMemo6(() => {
|
|
2900
2031
|
const visible = messages.filter((message) => {
|
|
2901
2032
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
2902
2033
|
if (isHiddenInternalMessage(message)) return false;
|
|
@@ -2942,7 +2073,7 @@ function MessageList({
|
|
|
2942
2073
|
blocks.push({
|
|
2943
2074
|
type: "message",
|
|
2944
2075
|
message,
|
|
2945
|
-
key: message.
|
|
2076
|
+
key: message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2946
2077
|
});
|
|
2947
2078
|
}
|
|
2948
2079
|
flushAssistant();
|
|
@@ -2969,86 +2100,63 @@ function MessageList({
|
|
|
2969
2100
|
}
|
|
2970
2101
|
return blocks;
|
|
2971
2102
|
}, [messages, isStreaming]);
|
|
2972
|
-
return /* @__PURE__ */
|
|
2973
|
-
/* @__PURE__ */
|
|
2974
|
-
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */
|
|
2975
|
-
/* @__PURE__ */
|
|
2976
|
-
/* @__PURE__ */
|
|
2977
|
-
/* @__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" })
|
|
2978
2109
|
] }) : renderBlocks.map((block) => {
|
|
2979
2110
|
if (block.type === "message") {
|
|
2980
|
-
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);
|
|
2981
2112
|
}
|
|
2982
2113
|
if (block.type === "assistant_turn") {
|
|
2983
|
-
|
|
2984
|
-
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
2985
|
-
).find((feedback) => feedback != null);
|
|
2986
|
-
const hasActiveFollowup = Boolean(
|
|
2987
|
-
postChatFollowup && block.messages.some(
|
|
2988
|
-
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
2989
|
-
)
|
|
2990
|
-
);
|
|
2991
|
-
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
|
|
2114
|
+
return /* @__PURE__ */ jsx13("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx13(
|
|
2992
2115
|
RenderErrorBoundary,
|
|
2993
2116
|
{
|
|
2994
2117
|
label: "\u52A9\u624B\u6D88\u606F",
|
|
2995
2118
|
details: block.key,
|
|
2996
2119
|
resetKey: getMessageResetSignature(block.messages),
|
|
2997
|
-
children:
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
),
|
|
3010
|
-
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3011
|
-
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
|
|
3012
|
-
PostChatFollowupBlock,
|
|
3013
|
-
{
|
|
3014
|
-
followup: postChatFollowup,
|
|
3015
|
-
sessionId,
|
|
3016
|
-
onSuggestion,
|
|
3017
|
-
isViewer,
|
|
3018
|
-
onInteraction: onFollowupInteraction,
|
|
3019
|
-
savedFeedback: blockFeedback,
|
|
3020
|
-
onFeedbackSaved: onResultFeedbackSaved
|
|
3021
|
-
}
|
|
3022
|
-
) : null
|
|
3023
|
-
]
|
|
2120
|
+
children: /* @__PURE__ */ jsx13(
|
|
2121
|
+
AssistantTurnBlock,
|
|
2122
|
+
{
|
|
2123
|
+
messages: block.messages,
|
|
2124
|
+
isStreaming: block.isStreaming,
|
|
2125
|
+
askAnswers,
|
|
2126
|
+
onAnswer,
|
|
2127
|
+
sessionStatus,
|
|
2128
|
+
toolCallRenderer,
|
|
2129
|
+
sessionId
|
|
2130
|
+
}
|
|
2131
|
+
)
|
|
3024
2132
|
}
|
|
3025
2133
|
) }, block.key);
|
|
3026
2134
|
}
|
|
3027
2135
|
if (block.type === "compaction") {
|
|
3028
|
-
return /* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ jsxs11(
|
|
3029
2137
|
"div",
|
|
3030
2138
|
{
|
|
3031
2139
|
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3032
2140
|
children: [
|
|
3033
|
-
/* @__PURE__ */
|
|
3034
|
-
/* @__PURE__ */
|
|
2141
|
+
/* @__PURE__ */ jsx13(Layers, { size: 12 }),
|
|
2142
|
+
/* @__PURE__ */ jsx13("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
3035
2143
|
]
|
|
3036
2144
|
},
|
|
3037
2145
|
block.key
|
|
3038
2146
|
);
|
|
3039
2147
|
}
|
|
3040
|
-
return /* @__PURE__ */
|
|
2148
|
+
return /* @__PURE__ */ jsx13(PlanningDivider, { kind: block.kind }, block.key);
|
|
3041
2149
|
}),
|
|
3042
|
-
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
|
|
3043
2151
|
] }) }) }),
|
|
3044
|
-
/* @__PURE__ */
|
|
3045
|
-
/* @__PURE__ */
|
|
2152
|
+
/* @__PURE__ */ jsx13(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
|
|
2153
|
+
/* @__PURE__ */ jsx13(ScrollToBottomButton, {})
|
|
3046
2154
|
] }) });
|
|
3047
2155
|
}
|
|
3048
2156
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
3049
2157
|
const { scrollToBottom } = useStickToBottomContext();
|
|
3050
|
-
const previousCountRef =
|
|
3051
|
-
|
|
2158
|
+
const previousCountRef = useRef6(userMessageCount);
|
|
2159
|
+
useEffect6(() => {
|
|
3052
2160
|
if (userMessageCount > previousCountRef.current) {
|
|
3053
2161
|
scrollToBottom("instant");
|
|
3054
2162
|
}
|
|
@@ -3058,9 +2166,9 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
3058
2166
|
}
|
|
3059
2167
|
function ScrollToBottomButton() {
|
|
3060
2168
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3061
|
-
const [visible, setVisible] =
|
|
3062
|
-
const hideTimerRef =
|
|
3063
|
-
|
|
2169
|
+
const [visible, setVisible] = useState10(false);
|
|
2170
|
+
const hideTimerRef = useRef6(null);
|
|
2171
|
+
useEffect6(() => {
|
|
3064
2172
|
if (isAtBottom) {
|
|
3065
2173
|
if (!hideTimerRef.current) {
|
|
3066
2174
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3082,7 +2190,7 @@ function ScrollToBottomButton() {
|
|
|
3082
2190
|
}
|
|
3083
2191
|
};
|
|
3084
2192
|
}, [isAtBottom]);
|
|
3085
|
-
const handleClick =
|
|
2193
|
+
const handleClick = useCallback3(() => {
|
|
3086
2194
|
if (hideTimerRef.current) {
|
|
3087
2195
|
clearTimeout(hideTimerRef.current);
|
|
3088
2196
|
hideTimerRef.current = null;
|
|
@@ -3091,7 +2199,7 @@ function ScrollToBottomButton() {
|
|
|
3091
2199
|
scrollToBottom();
|
|
3092
2200
|
}, [scrollToBottom]);
|
|
3093
2201
|
if (!visible) return null;
|
|
3094
|
-
return /* @__PURE__ */
|
|
2202
|
+
return /* @__PURE__ */ jsxs11(
|
|
3095
2203
|
"button",
|
|
3096
2204
|
{
|
|
3097
2205
|
type: "button",
|
|
@@ -3099,120 +2207,34 @@ function ScrollToBottomButton() {
|
|
|
3099
2207
|
"aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
|
|
3100
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))]",
|
|
3101
2209
|
children: [
|
|
3102
|
-
/* @__PURE__ */
|
|
3103
|
-
/* @__PURE__ */
|
|
2210
|
+
/* @__PURE__ */ jsx13(ChevronDown, { size: 14 }),
|
|
2211
|
+
/* @__PURE__ */ jsx13("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
|
|
3104
2212
|
]
|
|
3105
2213
|
}
|
|
3106
2214
|
);
|
|
3107
2215
|
}
|
|
3108
2216
|
function PlanningDivider({ kind }) {
|
|
3109
|
-
return /* @__PURE__ */
|
|
3110
|
-
/* @__PURE__ */
|
|
3111
|
-
/* @__PURE__ */
|
|
3112
|
-
/* @__PURE__ */
|
|
3113
|
-
/* @__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" })
|
|
3114
2222
|
] }),
|
|
3115
|
-
/* @__PURE__ */
|
|
2223
|
+
/* @__PURE__ */ jsx13("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
|
|
3116
2224
|
] });
|
|
3117
2225
|
}
|
|
3118
2226
|
|
|
3119
|
-
// src/components/
|
|
3120
|
-
import { jsx as
|
|
2227
|
+
// src/components/ChatView.tsx
|
|
2228
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3121
2229
|
function themeAttr(theme) {
|
|
3122
2230
|
return theme === "dark" ? "dark" : void 0;
|
|
3123
2231
|
}
|
|
3124
|
-
function ChatSurface({
|
|
3125
|
-
theme,
|
|
3126
|
-
classNames,
|
|
3127
|
-
renderers,
|
|
3128
|
-
slots,
|
|
3129
|
-
placeholder,
|
|
3130
|
-
connection,
|
|
3131
|
-
errorMessage,
|
|
3132
|
-
messages,
|
|
3133
|
-
postChatFollowup,
|
|
3134
|
-
isStreaming,
|
|
3135
|
-
isStopping,
|
|
3136
|
-
inputText,
|
|
3137
|
-
onInputChange,
|
|
3138
|
-
onSuggestion,
|
|
3139
|
-
onSend,
|
|
3140
|
-
onStop,
|
|
3141
|
-
sessionStatus,
|
|
3142
|
-
askAnswers,
|
|
3143
|
-
onAnswer,
|
|
3144
|
-
sessionId,
|
|
3145
|
-
isViewer,
|
|
3146
|
-
resultFeedbackByEntry,
|
|
3147
|
-
onResultFeedbackSaved,
|
|
3148
|
-
onFollowupInteraction,
|
|
3149
|
-
beforeInput,
|
|
3150
|
-
banner
|
|
3151
|
-
}) {
|
|
3152
|
-
return /* @__PURE__ */ jsxs14(
|
|
3153
|
-
"div",
|
|
3154
|
-
{
|
|
3155
|
-
"data-theme": themeAttr(theme),
|
|
3156
|
-
className: cn(
|
|
3157
|
-
"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden",
|
|
3158
|
-
classNames?.root
|
|
3159
|
-
),
|
|
3160
|
-
children: [
|
|
3161
|
-
/* @__PURE__ */ jsx16(ConnectionBanner, { connection, className: classNames?.banner }),
|
|
3162
|
-
banner,
|
|
3163
|
-
errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
|
|
3164
|
-
/* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
|
|
3165
|
-
/* @__PURE__ */ jsx16("span", { children: errorMessage })
|
|
3166
|
-
] }),
|
|
3167
|
-
slots?.header,
|
|
3168
|
-
/* @__PURE__ */ jsx16(
|
|
3169
|
-
MessageList,
|
|
3170
|
-
{
|
|
3171
|
-
messages,
|
|
3172
|
-
postChatFollowup,
|
|
3173
|
-
onSuggestion,
|
|
3174
|
-
isStreaming,
|
|
3175
|
-
sessionStatus,
|
|
3176
|
-
askAnswers,
|
|
3177
|
-
onAnswer,
|
|
3178
|
-
toolCallRenderer: renderers?.toolCall,
|
|
3179
|
-
emptyState: slots?.emptyState,
|
|
3180
|
-
className: classNames?.messageList,
|
|
3181
|
-
sessionId,
|
|
3182
|
-
isViewer,
|
|
3183
|
-
resultFeedbackByEntry,
|
|
3184
|
-
onResultFeedbackSaved,
|
|
3185
|
-
onFollowupInteraction
|
|
3186
|
-
}
|
|
3187
|
-
),
|
|
3188
|
-
beforeInput,
|
|
3189
|
-
/* @__PURE__ */ jsx16(
|
|
3190
|
-
ChatInput,
|
|
3191
|
-
{
|
|
3192
|
-
value: inputText,
|
|
3193
|
-
onValueChange: onInputChange,
|
|
3194
|
-
onSend,
|
|
3195
|
-
onStop,
|
|
3196
|
-
isStreaming,
|
|
3197
|
-
isStopping,
|
|
3198
|
-
placeholder,
|
|
3199
|
-
className: classNames?.chatInput
|
|
3200
|
-
}
|
|
3201
|
-
),
|
|
3202
|
-
slots?.footer
|
|
3203
|
-
]
|
|
3204
|
-
}
|
|
3205
|
-
);
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
|
-
// src/components/AgentChat.tsx
|
|
3209
|
-
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3210
2232
|
function isUnauthorizedError(error) {
|
|
3211
2233
|
return error instanceof BladeApiError && error.status === 401;
|
|
3212
2234
|
}
|
|
3213
2235
|
function LoginCard({ client, onLoggedIn }) {
|
|
3214
|
-
const [loggingIn, setLoggingIn] =
|
|
3215
|
-
const [loginError, setLoginError] =
|
|
2236
|
+
const [loggingIn, setLoggingIn] = useState11(false);
|
|
2237
|
+
const [loginError, setLoginError] = useState11(null);
|
|
3216
2238
|
const handleLogin = async () => {
|
|
3217
2239
|
setLoggingIn(true);
|
|
3218
2240
|
setLoginError(null);
|
|
@@ -3225,11 +2247,11 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3225
2247
|
setLoggingIn(false);
|
|
3226
2248
|
}
|
|
3227
2249
|
};
|
|
3228
|
-
return /* @__PURE__ */
|
|
3229
|
-
/* @__PURE__ */
|
|
3230
|
-
/* @__PURE__ */
|
|
3231
|
-
/* @__PURE__ */
|
|
3232
|
-
/* @__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(
|
|
3233
2255
|
"button",
|
|
3234
2256
|
{
|
|
3235
2257
|
type: "button",
|
|
@@ -3239,20 +2261,20 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3239
2261
|
children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
3240
2262
|
}
|
|
3241
2263
|
),
|
|
3242
|
-
loginError && /* @__PURE__ */
|
|
2264
|
+
loginError && /* @__PURE__ */ jsx14("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
|
|
3243
2265
|
] }) });
|
|
3244
2266
|
}
|
|
3245
|
-
function
|
|
2267
|
+
function ChatView(props) {
|
|
3246
2268
|
const client = useBladeClient();
|
|
3247
|
-
const [attempt, setAttempt] =
|
|
3248
|
-
const [needLogin, setNeedLogin] =
|
|
2269
|
+
const [attempt, setAttempt] = useState11(0);
|
|
2270
|
+
const [needLogin, setNeedLogin] = useState11(() => !client.hasToken());
|
|
3249
2271
|
if (needLogin) {
|
|
3250
|
-
return /* @__PURE__ */
|
|
2272
|
+
return /* @__PURE__ */ jsx14(
|
|
3251
2273
|
"div",
|
|
3252
2274
|
{
|
|
3253
2275
|
"data-theme": themeAttr(props.theme),
|
|
3254
2276
|
className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
|
|
3255
|
-
children: /* @__PURE__ */
|
|
2277
|
+
children: /* @__PURE__ */ jsx14(
|
|
3256
2278
|
LoginCard,
|
|
3257
2279
|
{
|
|
3258
2280
|
client,
|
|
@@ -3265,7 +2287,7 @@ function AgentChat(props) {
|
|
|
3265
2287
|
}
|
|
3266
2288
|
);
|
|
3267
2289
|
}
|
|
3268
|
-
return /* @__PURE__ */
|
|
2290
|
+
return /* @__PURE__ */ jsx14(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
|
|
3269
2291
|
}
|
|
3270
2292
|
function ChatSessionView({
|
|
3271
2293
|
sessionId,
|
|
@@ -3278,61 +2300,20 @@ function ChatSessionView({
|
|
|
3278
2300
|
slots,
|
|
3279
2301
|
placeholder,
|
|
3280
2302
|
theme,
|
|
3281
|
-
onFollowupInteraction,
|
|
3282
2303
|
onUnauthorized
|
|
3283
2304
|
}) {
|
|
3284
|
-
const client = useBladeClient();
|
|
3285
2305
|
const { session, state, error } = useAgentSession(sessionId, {
|
|
3286
2306
|
createOptions,
|
|
3287
2307
|
onSessionCreated
|
|
3288
2308
|
});
|
|
3289
|
-
const
|
|
3290
|
-
const [
|
|
3291
|
-
|
|
3292
|
-
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3293
|
-
const resolvedSessionId = session?.sessionId;
|
|
3294
|
-
const isViewer = state?.viewerRole === "viewer";
|
|
3295
|
-
useEffect9(() => {
|
|
3296
|
-
setResultFeedback([]);
|
|
3297
|
-
if (!resolvedSessionId || isViewer) return;
|
|
3298
|
-
let cancelled = false;
|
|
3299
|
-
void client.sessions.listResultFeedback(resolvedSessionId).then(
|
|
3300
|
-
(items) => {
|
|
3301
|
-
if (cancelled) return;
|
|
3302
|
-
setResultFeedback((current) => {
|
|
3303
|
-
const merged = new Map(items.map((item) => [item.assistant_entry_id, item]));
|
|
3304
|
-
for (const item of current) {
|
|
3305
|
-
const fetched = merged.get(item.assistant_entry_id);
|
|
3306
|
-
if (!fetched || fetched.updated_at < item.updated_at) {
|
|
3307
|
-
merged.set(item.assistant_entry_id, item);
|
|
3308
|
-
}
|
|
3309
|
-
}
|
|
3310
|
-
return [...merged.values()];
|
|
3311
|
-
});
|
|
3312
|
-
},
|
|
3313
|
-
() => {
|
|
3314
|
-
}
|
|
3315
|
-
);
|
|
3316
|
-
return () => {
|
|
3317
|
-
cancelled = true;
|
|
3318
|
-
};
|
|
3319
|
-
}, [client, isViewer, resolvedSessionId]);
|
|
3320
|
-
const resultFeedbackByEntry = useMemo8(
|
|
3321
|
-
() => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
|
|
3322
|
-
[resultFeedback]
|
|
3323
|
-
);
|
|
3324
|
-
const handleResultFeedbackSaved = useCallback7((saved) => {
|
|
3325
|
-
setResultFeedback((current) => [
|
|
3326
|
-
...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
|
|
3327
|
-
saved
|
|
3328
|
-
]);
|
|
3329
|
-
}, []);
|
|
3330
|
-
useEffect9(() => {
|
|
2309
|
+
const [stopRequested, setStopRequested] = useState11(false);
|
|
2310
|
+
const [inputText, setInputText] = useState11("");
|
|
2311
|
+
useEffect7(() => {
|
|
3331
2312
|
if (session) {
|
|
3332
2313
|
onSessionReady?.(session);
|
|
3333
2314
|
}
|
|
3334
2315
|
}, [session, onSessionReady]);
|
|
3335
|
-
|
|
2316
|
+
useEffect7(() => {
|
|
3336
2317
|
if (!session) return;
|
|
3337
2318
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
3338
2319
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3348,12 +2329,12 @@ ${content}`);
|
|
|
3348
2329
|
offInsert();
|
|
3349
2330
|
};
|
|
3350
2331
|
}, [session]);
|
|
3351
|
-
|
|
2332
|
+
useEffect7(() => {
|
|
3352
2333
|
if (isUnauthorizedError(error)) {
|
|
3353
2334
|
onUnauthorized();
|
|
3354
2335
|
}
|
|
3355
2336
|
}, [error, onUnauthorized]);
|
|
3356
|
-
|
|
2337
|
+
useEffect7(() => {
|
|
3357
2338
|
if (!session || !commands) return;
|
|
3358
2339
|
const unsubscribes = Object.entries(commands).map(
|
|
3359
2340
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3365,7 +2346,7 @@ ${content}`);
|
|
|
3365
2346
|
const isStreaming = state?.isStreaming ?? false;
|
|
3366
2347
|
const isStopping = stopRequested && isStreaming;
|
|
3367
2348
|
const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
|
|
3368
|
-
const errorMessage = connectError ?? state?.errorMessage ??
|
|
2349
|
+
const errorMessage = connectError ?? state?.errorMessage ?? null;
|
|
3369
2350
|
const handleSend = (text) => {
|
|
3370
2351
|
setStopRequested(false);
|
|
3371
2352
|
return session?.send(text, { mode: state?.mode ?? void 0 }) ?? Promise.resolve(false);
|
|
@@ -3374,287 +2355,73 @@ ${content}`);
|
|
|
3374
2355
|
setStopRequested(true);
|
|
3375
2356
|
void session?.stop();
|
|
3376
2357
|
};
|
|
3377
|
-
return /* @__PURE__ */
|
|
3378
|
-
|
|
2358
|
+
return /* @__PURE__ */ jsxs12(
|
|
2359
|
+
"div",
|
|
3379
2360
|
{
|
|
3380
|
-
theme,
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
/* @__PURE__ */ jsx17(
|
|
3389
|
-
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,
|
|
3390
2369
|
{
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
canControl: replay.canControl,
|
|
3394
|
-
onSpeedChange: (next) => void replay.setSpeed(next),
|
|
3395
|
-
onExit: () => void replay.exitToAutonomous()
|
|
2370
|
+
connection: state?.connection ?? "connecting",
|
|
2371
|
+
className: classNames?.banner
|
|
3396
2372
|
}
|
|
3397
2373
|
),
|
|
3398
|
-
/* @__PURE__ */
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
isStreaming,
|
|
3406
|
-
isStopping,
|
|
3407
|
-
inputText,
|
|
3408
|
-
onInputChange: setInputText,
|
|
3409
|
-
onSuggestion: setInputText,
|
|
3410
|
-
onSend: handleSend,
|
|
3411
|
-
onStop: handleStop,
|
|
3412
|
-
sessionStatus: state?.status ?? void 0,
|
|
3413
|
-
askAnswers: state?.askAnswers,
|
|
3414
|
-
onAnswer: (answer, toolCallId, answerData) => {
|
|
3415
|
-
void session?.send(answer, {
|
|
3416
|
-
mode: state?.mode ?? void 0,
|
|
3417
|
-
askUserAnswer: { tool_call_id: toolCallId, ...answerData }
|
|
3418
|
-
});
|
|
3419
|
-
},
|
|
3420
|
-
sessionId: resolvedSessionId,
|
|
3421
|
-
isViewer
|
|
3422
|
-
}
|
|
3423
|
-
);
|
|
3424
|
-
}
|
|
3425
|
-
|
|
3426
|
-
// src/components/LlmChat.tsx
|
|
3427
|
-
import { useEffect as useEffect10, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3428
|
-
|
|
3429
|
-
// src/components/LlmAdvancedSettings.tsx
|
|
3430
|
-
import { useState as useState14 } from "react";
|
|
3431
|
-
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3432
|
-
var FIELDS = [
|
|
3433
|
-
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
3434
|
-
{ id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
|
|
3435
|
-
{ id: "apiKey", label: "\u5BC6\u94A5", placeholder: "\u7559\u7A7A\u8868\u793A\u4E0D\u6539", secret: true }
|
|
3436
|
-
];
|
|
3437
|
-
function normalizeAdvanced(value) {
|
|
3438
|
-
if (!value) return null;
|
|
3439
|
-
const config = value === true ? {} : value;
|
|
3440
|
-
return {
|
|
3441
|
-
baseURL: config.baseURL ?? true,
|
|
3442
|
-
model: config.model ?? true,
|
|
3443
|
-
apiKey: config.apiKey ?? false,
|
|
3444
|
-
storage: config.storage ?? "local"
|
|
3445
|
-
};
|
|
3446
|
-
}
|
|
3447
|
-
function storageKeyFor(settings, baseURL) {
|
|
3448
|
-
const explicit = typeof settings === "object" ? settings.storageKey : void 0;
|
|
3449
|
-
return `blade-llm-override:${explicit ?? baseURL}`;
|
|
3450
|
-
}
|
|
3451
|
-
function readOverride(settings, baseURL) {
|
|
3452
|
-
const normalized = normalizeAdvanced(settings);
|
|
3453
|
-
if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return {};
|
|
3454
|
-
try {
|
|
3455
|
-
const raw = localStorage.getItem(storageKeyFor(settings, baseURL));
|
|
3456
|
-
if (!raw) return {};
|
|
3457
|
-
const stored = JSON.parse(raw);
|
|
3458
|
-
return Object.fromEntries(
|
|
3459
|
-
Object.entries(stored).filter(([key]) => normalized[key])
|
|
3460
|
-
);
|
|
3461
|
-
} catch {
|
|
3462
|
-
return {};
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
function writeOverride(settings, baseURL, override) {
|
|
3466
|
-
const normalized = normalizeAdvanced(settings);
|
|
3467
|
-
if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return;
|
|
3468
|
-
try {
|
|
3469
|
-
const key = storageKeyFor(settings, baseURL);
|
|
3470
|
-
if (Object.keys(override).length === 0) localStorage.removeItem(key);
|
|
3471
|
-
else localStorage.setItem(key, JSON.stringify(override));
|
|
3472
|
-
} catch {
|
|
3473
|
-
}
|
|
3474
|
-
}
|
|
3475
|
-
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3476
|
-
const normalized = normalizeAdvanced(settings);
|
|
3477
|
-
const [open, setOpen] = useState14(false);
|
|
3478
|
-
const [draft, setDraft] = useState14(override);
|
|
3479
|
-
if (!normalized) return null;
|
|
3480
|
-
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3481
|
-
const dirty = Object.keys(override).length > 0;
|
|
3482
|
-
return /* @__PURE__ */ jsxs16("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
|
|
3483
|
-
/* @__PURE__ */ jsxs16(
|
|
3484
|
-
"button",
|
|
3485
|
-
{
|
|
3486
|
-
type: "button",
|
|
3487
|
-
onClick: () => {
|
|
3488
|
-
setDraft(override);
|
|
3489
|
-
setOpen((value) => !value);
|
|
3490
|
-
},
|
|
3491
|
-
className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
3492
|
-
children: [
|
|
3493
|
-
/* @__PURE__ */ jsx18(Settings2, { size: 13 }),
|
|
3494
|
-
"\u9AD8\u7EA7\u8BBE\u7F6E",
|
|
3495
|
-
dirty && /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
|
|
3496
|
-
]
|
|
3497
|
-
}
|
|
3498
|
-
),
|
|
3499
|
-
open && /* @__PURE__ */ jsxs16("div", { className: "mt-2 flex flex-col gap-2", children: [
|
|
3500
|
-
fields.map((field) => /* @__PURE__ */ jsxs16("label", { className: "flex flex-col gap-1", children: [
|
|
3501
|
-
/* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
|
|
3502
|
-
/* @__PURE__ */ jsx18(
|
|
3503
|
-
"input",
|
|
3504
|
-
{
|
|
3505
|
-
type: field.secret ? "password" : "text",
|
|
3506
|
-
value: draft[field.id] ?? "",
|
|
3507
|
-
placeholder: field.id === "apiKey" ? field.placeholder : defaults[field.id] || field.placeholder,
|
|
3508
|
-
onChange: (event) => setDraft({ ...draft, [field.id]: event.target.value }),
|
|
3509
|
-
className: "rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none"
|
|
3510
|
-
}
|
|
3511
|
-
)
|
|
3512
|
-
] }, field.id)),
|
|
3513
|
-
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" }),
|
|
3514
|
-
/* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
|
|
3515
|
-
/* @__PURE__ */ jsx18(
|
|
3516
|
-
"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,
|
|
3517
2381
|
{
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
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
|
+
});
|
|
3525
2391
|
},
|
|
3526
|
-
|
|
3527
|
-
|
|
2392
|
+
toolCallRenderer: renderers?.toolCall,
|
|
2393
|
+
emptyState: slots?.emptyState,
|
|
2394
|
+
className: classNames?.messageList,
|
|
2395
|
+
sessionId: session?.sessionId
|
|
3528
2396
|
}
|
|
3529
2397
|
),
|
|
3530
|
-
/* @__PURE__ */
|
|
3531
|
-
|
|
2398
|
+
/* @__PURE__ */ jsx14(
|
|
2399
|
+
ChatInput,
|
|
3532
2400
|
{
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
}
|
|
3542
|
-
)
|
|
3543
|
-
] })
|
|
3544
|
-
] })
|
|
3545
|
-
] });
|
|
3546
|
-
}
|
|
3547
|
-
|
|
3548
|
-
// src/components/LlmChat.tsx
|
|
3549
|
-
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
3550
|
-
function LlmChat({
|
|
3551
|
-
classNames,
|
|
3552
|
-
renderers,
|
|
3553
|
-
slots,
|
|
3554
|
-
placeholder,
|
|
3555
|
-
theme,
|
|
3556
|
-
onReady,
|
|
3557
|
-
advanced,
|
|
3558
|
-
onOverrideChange,
|
|
3559
|
-
...options
|
|
3560
|
-
}) {
|
|
3561
|
-
const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
|
|
3562
|
-
const effective = { ...options, ...override };
|
|
3563
|
-
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3564
|
-
const [inputText, setInputText] = useState15("");
|
|
3565
|
-
const [stopRequested, setStopRequested] = useState15(false);
|
|
3566
|
-
const handle = useMemo9(
|
|
3567
|
-
() => ({
|
|
3568
|
-
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
3569
|
-
${text}` : text),
|
|
3570
|
-
send,
|
|
3571
|
-
reset
|
|
3572
|
-
}),
|
|
3573
|
-
[send, reset]
|
|
3574
|
-
);
|
|
3575
|
-
useEffect10(() => {
|
|
3576
|
-
onReady?.(handle);
|
|
3577
|
-
}, [handle, onReady]);
|
|
3578
|
-
return /* @__PURE__ */ jsx19(
|
|
3579
|
-
ChatSurface,
|
|
3580
|
-
{
|
|
3581
|
-
theme,
|
|
3582
|
-
classNames,
|
|
3583
|
-
renderers,
|
|
3584
|
-
slots,
|
|
3585
|
-
placeholder,
|
|
3586
|
-
connection: "connected",
|
|
3587
|
-
errorMessage: error,
|
|
3588
|
-
messages,
|
|
3589
|
-
isStreaming,
|
|
3590
|
-
isStopping: stopRequested && isStreaming,
|
|
3591
|
-
inputText,
|
|
3592
|
-
onInputChange: setInputText,
|
|
3593
|
-
onSend: async (text) => {
|
|
3594
|
-
setStopRequested(false);
|
|
3595
|
-
if (!text.trim() || isStreaming) return false;
|
|
3596
|
-
void send(text);
|
|
3597
|
-
return true;
|
|
3598
|
-
},
|
|
3599
|
-
onStop: () => {
|
|
3600
|
-
setStopRequested(true);
|
|
3601
|
-
stop();
|
|
3602
|
-
},
|
|
3603
|
-
beforeInput: advanced ? /* @__PURE__ */ jsx19(
|
|
3604
|
-
LlmAdvancedSettingsBar,
|
|
3605
|
-
{
|
|
3606
|
-
settings: advanced,
|
|
3607
|
-
defaults: { baseURL: options.baseURL, model: options.model },
|
|
3608
|
-
override,
|
|
3609
|
-
onChange: (next) => {
|
|
3610
|
-
setOverride(next);
|
|
3611
|
-
writeOverride(advanced, options.baseURL, next);
|
|
3612
|
-
onOverrideChange?.(next);
|
|
2401
|
+
value: inputText,
|
|
2402
|
+
onValueChange: setInputText,
|
|
2403
|
+
onSend: handleSend,
|
|
2404
|
+
onStop: handleStop,
|
|
2405
|
+
isStreaming,
|
|
2406
|
+
isStopping,
|
|
2407
|
+
placeholder,
|
|
2408
|
+
className: classNames?.chatInput
|
|
3613
2409
|
}
|
|
3614
|
-
|
|
3615
|
-
|
|
2410
|
+
),
|
|
2411
|
+
slots?.footer
|
|
2412
|
+
]
|
|
3616
2413
|
}
|
|
3617
2414
|
);
|
|
3618
2415
|
}
|
|
3619
2416
|
|
|
3620
|
-
// src/components/ChatView.tsx
|
|
3621
|
-
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
3622
|
-
function ChatView(props) {
|
|
3623
|
-
const { mode, llm, onLlmReady, ...rest } = props;
|
|
3624
|
-
if (mode === "llm") {
|
|
3625
|
-
if (!llm) {
|
|
3626
|
-
throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
|
|
3627
|
-
}
|
|
3628
|
-
return /* @__PURE__ */ jsx20(
|
|
3629
|
-
LlmChat,
|
|
3630
|
-
{
|
|
3631
|
-
...llm,
|
|
3632
|
-
classNames: rest.classNames,
|
|
3633
|
-
renderers: rest.renderers,
|
|
3634
|
-
slots: rest.slots,
|
|
3635
|
-
placeholder: rest.placeholder,
|
|
3636
|
-
theme: rest.theme,
|
|
3637
|
-
onReady: onLlmReady
|
|
3638
|
-
}
|
|
3639
|
-
);
|
|
3640
|
-
}
|
|
3641
|
-
return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
|
|
3642
|
-
}
|
|
3643
|
-
|
|
3644
2417
|
// src/index.ts
|
|
3645
2418
|
export * from "@blade-hq/agent-client";
|
|
3646
2419
|
export {
|
|
3647
|
-
AgentChat,
|
|
3648
2420
|
BladeProvider,
|
|
3649
2421
|
ChatView,
|
|
3650
|
-
LlmChat,
|
|
3651
2422
|
MarkdownContent,
|
|
3652
|
-
ReplayBar,
|
|
3653
|
-
ReplayMismatchPrompt,
|
|
3654
2423
|
useAgentSession,
|
|
3655
|
-
useBladeClient
|
|
3656
|
-
useLlmChat,
|
|
3657
|
-
useReplay
|
|
2424
|
+
useBladeClient
|
|
3658
2425
|
};
|
|
3659
2426
|
/*! Bundled license information:
|
|
3660
2427
|
|
|
@@ -3662,8 +2429,6 @@ lucide-react/dist/esm/shared/src/utils.js:
|
|
|
3662
2429
|
lucide-react/dist/esm/defaultAttributes.js:
|
|
3663
2430
|
lucide-react/dist/esm/Icon.js:
|
|
3664
2431
|
lucide-react/dist/esm/createLucideIcon.js:
|
|
3665
|
-
lucide-react/dist/esm/icons/arrow-right.js:
|
|
3666
|
-
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
3667
2432
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
3668
2433
|
lucide-react/dist/esm/icons/bot.js:
|
|
3669
2434
|
lucide-react/dist/esm/icons/brain.js:
|
|
@@ -3673,16 +2438,12 @@ lucide-react/dist/esm/icons/chevron-right.js:
|
|
|
3673
2438
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3674
2439
|
lucide-react/dist/esm/icons/copy.js:
|
|
3675
2440
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3676
|
-
lucide-react/dist/esm/icons/globe.js:
|
|
3677
2441
|
lucide-react/dist/esm/icons/layers.js:
|
|
3678
2442
|
lucide-react/dist/esm/icons/lightbulb.js:
|
|
3679
2443
|
lucide-react/dist/esm/icons/loader-circle.js:
|
|
3680
2444
|
lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
3681
2445
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
3682
2446
|
lucide-react/dist/esm/icons/message-square.js:
|
|
3683
|
-
lucide-react/dist/esm/icons/play.js:
|
|
3684
|
-
lucide-react/dist/esm/icons/settings-2.js:
|
|
3685
|
-
lucide-react/dist/esm/icons/sparkles.js:
|
|
3686
2447
|
lucide-react/dist/esm/icons/square.js:
|
|
3687
2448
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
3688
2449
|
lucide-react/dist/esm/icons/x.js:
|