@blade-hq/agent-react 2610.0.0-beta.2 → 2610.0.0-beta.21
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 +12 -1
- package/dist/components/AgentLoopBlock.d.ts +1 -4
- package/dist/components/AssistantTurnBlock.d.ts +12 -4
- package/dist/components/ConnectionBanner.d.ts +1 -1
- package/dist/components/ContextCard.d.ts +7 -0
- package/dist/components/display-utils.d.ts +8 -0
- package/dist/embed/entry.d.ts +6 -0
- package/dist/hooks/use-message-pin.d.ts +22 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1418 -475
- package/dist/index.js.map +1 -1
- package/dist/style.css +77 -1
- package/dist/style.full.css +78 -2
- package/package.json +2 -2
- package/public-api.md +157 -2
package/dist/index.js
CHANGED
|
@@ -245,6 +245,8 @@ function useLlmChat(options) {
|
|
|
245
245
|
const [error, setError] = useState3(null);
|
|
246
246
|
const [isStreaming, setIsStreaming] = useState3(false);
|
|
247
247
|
const abortRef = useRef2(null);
|
|
248
|
+
const activeStartedAtRef = useRef2(null);
|
|
249
|
+
const assistantTimingsRef = useRef2(/* @__PURE__ */ new WeakMap());
|
|
248
250
|
const generationRef = useRef2(0);
|
|
249
251
|
const historyRef = useRef2([]);
|
|
250
252
|
const optionsRef = useRef2(options);
|
|
@@ -263,16 +265,31 @@ function useLlmChat(options) {
|
|
|
263
265
|
setFailedToolIds([]);
|
|
264
266
|
setError(null);
|
|
265
267
|
setIsStreaming(false);
|
|
268
|
+
activeStartedAtRef.current = null;
|
|
269
|
+
assistantTimingsRef.current = /* @__PURE__ */ new WeakMap();
|
|
266
270
|
}, [stop]);
|
|
267
271
|
const send = useCallback2(async (text) => {
|
|
268
272
|
const content = text.trim();
|
|
269
273
|
if (!content || abortRef.current) return false;
|
|
270
274
|
const opts = optionsRef.current;
|
|
271
275
|
const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
276
|
+
const startedAt = Date.now();
|
|
277
|
+
activeStartedAtRef.current = startedAt;
|
|
272
278
|
const commit = (message) => {
|
|
273
279
|
historyRef.current = [...historyRef.current, message];
|
|
274
280
|
setHistory(historyRef.current);
|
|
275
281
|
};
|
|
282
|
+
const latestAssistantTiming = { current: null };
|
|
283
|
+
const commitAssistant = (message) => {
|
|
284
|
+
const timing = { startedAt };
|
|
285
|
+
assistantTimingsRef.current.set(message, timing);
|
|
286
|
+
latestAssistantTiming.current = timing;
|
|
287
|
+
commit(message);
|
|
288
|
+
return timing;
|
|
289
|
+
};
|
|
290
|
+
const finishTiming = (timing) => {
|
|
291
|
+
timing.durationMs = Math.max(0, Date.now() - timing.startedAt);
|
|
292
|
+
};
|
|
276
293
|
setError(null);
|
|
277
294
|
setIsStreaming(true);
|
|
278
295
|
setStreamingText("");
|
|
@@ -298,8 +315,11 @@ function useLlmChat(options) {
|
|
|
298
315
|
};
|
|
299
316
|
setStreamingText(null);
|
|
300
317
|
setStreamingCalls([]);
|
|
301
|
-
|
|
302
|
-
if (!result.toolCalls.length)
|
|
318
|
+
const assistantTiming = commitAssistant(assistant);
|
|
319
|
+
if (!result.toolCalls.length) {
|
|
320
|
+
finishTiming(assistantTiming);
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
303
323
|
const bail = (reason) => {
|
|
304
324
|
for (const call of result.toolCalls) {
|
|
305
325
|
commit({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ error: reason }) });
|
|
@@ -308,11 +328,13 @@ function useLlmChat(options) {
|
|
|
308
328
|
};
|
|
309
329
|
if (!opts.onToolCall) {
|
|
310
330
|
bail("\u8FD9\u4E2A\u5E94\u7528\u6CA1\u6709\u63D0\u4F9B\u5DE5\u5177\u6267\u884C\u5165\u53E3");
|
|
331
|
+
finishTiming(assistantTiming);
|
|
311
332
|
return true;
|
|
312
333
|
}
|
|
313
334
|
if (round >= maxRounds) {
|
|
314
335
|
bail(`\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE\u4E0A\u9650 ${maxRounds} \u8F6E\uFF0C\u6CA1\u6709\u6267\u884C`);
|
|
315
336
|
setError(`\u5DE5\u5177\u8C03\u7528\u8D85\u8FC7 ${maxRounds} \u8F6E\u4ECD\u672A\u7ED9\u51FA\u7ED3\u8BBA\uFF0C\u5DF2\u505C\u4E0B\u3002`);
|
|
337
|
+
finishTiming(assistantTiming);
|
|
316
338
|
return false;
|
|
317
339
|
}
|
|
318
340
|
for (const call of result.toolCalls) {
|
|
@@ -337,21 +359,39 @@ function useLlmChat(options) {
|
|
|
337
359
|
setStreamingCalls([]);
|
|
338
360
|
if (controller.signal.aborted) {
|
|
339
361
|
if (generation !== generationRef.current) return false;
|
|
340
|
-
|
|
362
|
+
const timing = commitAssistant({
|
|
363
|
+
role: "assistant",
|
|
364
|
+
content: partial ? `${partial}\uFF08\u5DF2\u505C\u6B62\uFF09` : "\uFF08\u5DF2\u505C\u6B62\uFF09"
|
|
365
|
+
});
|
|
366
|
+
timing.status = "interrupted";
|
|
367
|
+
finishTiming(timing);
|
|
341
368
|
return false;
|
|
342
369
|
}
|
|
343
370
|
if (partial && generation === generationRef.current) {
|
|
344
|
-
|
|
371
|
+
const timing = commitAssistant({ role: "assistant", content: partial });
|
|
372
|
+
timing.status = "failed";
|
|
373
|
+
finishTiming(timing);
|
|
374
|
+
} else if (latestAssistantTiming.current) {
|
|
375
|
+
latestAssistantTiming.current.status = "failed";
|
|
376
|
+
finishTiming(latestAssistantTiming.current);
|
|
345
377
|
}
|
|
346
378
|
setError(err instanceof Error && err.message ? err.message : "\u5BF9\u8BDD\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
347
379
|
return false;
|
|
348
380
|
} finally {
|
|
349
381
|
if (abortRef.current === controller) abortRef.current = null;
|
|
382
|
+
activeStartedAtRef.current = null;
|
|
350
383
|
setIsStreaming(false);
|
|
351
384
|
}
|
|
352
385
|
}, []);
|
|
353
386
|
const messages = useMemo2(
|
|
354
|
-
() => toChatMessages(
|
|
387
|
+
() => toChatMessages(
|
|
388
|
+
history,
|
|
389
|
+
streamingText,
|
|
390
|
+
streamingCalls,
|
|
391
|
+
failedToolIds,
|
|
392
|
+
assistantTimingsRef.current,
|
|
393
|
+
activeStartedAtRef.current
|
|
394
|
+
),
|
|
355
395
|
[history, streamingText, streamingCalls, failedToolIds]
|
|
356
396
|
);
|
|
357
397
|
return { messages, isStreaming, error, send, stop, reset };
|
|
@@ -503,7 +543,7 @@ function parseSse(raw) {
|
|
|
503
543
|
if (!delta) return null;
|
|
504
544
|
return { text: delta.content, toolCallDeltas: delta.tool_calls };
|
|
505
545
|
}
|
|
506
|
-
function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
546
|
+
function toChatMessages(history, streamingText, streamingCalls, failedToolIds, assistantTimings, activeStartedAt) {
|
|
507
547
|
const results = /* @__PURE__ */ new Map();
|
|
508
548
|
for (const msg of history) {
|
|
509
549
|
if (msg.role === "tool") results.set(msg.tool_call_id, msg.content);
|
|
@@ -515,10 +555,13 @@ function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
|
515
555
|
messages.push({ role: "user", content: msg.content, status: "completed" });
|
|
516
556
|
continue;
|
|
517
557
|
}
|
|
558
|
+
const timing = assistantTimings.get(msg);
|
|
518
559
|
messages.push({
|
|
519
560
|
role: "assistant",
|
|
520
561
|
content: msg.content,
|
|
521
|
-
status: "completed",
|
|
562
|
+
status: timing?.status ?? "completed",
|
|
563
|
+
...timing ? { timestamp: new Date(timing.startedAt).toISOString() } : {},
|
|
564
|
+
...timing?.durationMs === void 0 ? {} : { duration_ms: timing.durationMs },
|
|
522
565
|
...msg.tool_calls?.length ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
523
566
|
});
|
|
524
567
|
}
|
|
@@ -527,6 +570,7 @@ function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
|
527
570
|
role: "assistant",
|
|
528
571
|
content: streamingText,
|
|
529
572
|
status: "streaming",
|
|
573
|
+
...activeStartedAt === null ? {} : { timestamp: new Date(activeStartedAt).toISOString() },
|
|
530
574
|
...streamingCalls.length ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
531
575
|
});
|
|
532
576
|
}
|
|
@@ -544,6 +588,184 @@ function toToolCallInfo(call, results, failedToolIds) {
|
|
|
544
588
|
};
|
|
545
589
|
}
|
|
546
590
|
|
|
591
|
+
// src/hooks/use-message-pin.ts
|
|
592
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
593
|
+
var DEFAULT_MARGIN_PX = 24;
|
|
594
|
+
var MANUAL_SCROLL_TOLERANCE_PX = 2;
|
|
595
|
+
function useMessagePin({
|
|
596
|
+
targetKey,
|
|
597
|
+
pinTarget,
|
|
598
|
+
layoutKey,
|
|
599
|
+
getScrollElement,
|
|
600
|
+
getContentElement,
|
|
601
|
+
getTargetElement,
|
|
602
|
+
getTargetScrollTop,
|
|
603
|
+
getSpacerHeight,
|
|
604
|
+
setSpacerHeight,
|
|
605
|
+
stopAutoScroll,
|
|
606
|
+
scrollToBottom,
|
|
607
|
+
margin = DEFAULT_MARGIN_PX
|
|
608
|
+
}) {
|
|
609
|
+
const pinActiveRef = useRef3(false);
|
|
610
|
+
const frameRef = useRef3(null);
|
|
611
|
+
const retryTimeoutRef = useRef3(null);
|
|
612
|
+
const manualScrollCheckTimeoutRef = useRef3(null);
|
|
613
|
+
const repositionPendingRef = useRef3(false);
|
|
614
|
+
const pinnedTargetScrollTopRef = useRef3(null);
|
|
615
|
+
const appliedTargetKeyRef = useRef3(null);
|
|
616
|
+
const initialObservedTargetKeyRef = useRef3(pinTarget ? null : targetKey);
|
|
617
|
+
const observedTargetKeyRef = useRef3(initialObservedTargetKeyRef.current);
|
|
618
|
+
const setSpacerHeightRef = useRef3(setSpacerHeight);
|
|
619
|
+
setSpacerHeightRef.current = setSpacerHeight;
|
|
620
|
+
const release = useCallback3(() => {
|
|
621
|
+
if (!pinActiveRef.current) return;
|
|
622
|
+
pinActiveRef.current = false;
|
|
623
|
+
repositionPendingRef.current = false;
|
|
624
|
+
pinnedTargetScrollTopRef.current = null;
|
|
625
|
+
if (frameRef.current != null) cancelAnimationFrame(frameRef.current);
|
|
626
|
+
frameRef.current = null;
|
|
627
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
628
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
629
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
630
|
+
}
|
|
631
|
+
setSpacerHeightRef.current(0);
|
|
632
|
+
}, []);
|
|
633
|
+
const reposition = useCallback3(
|
|
634
|
+
() => {
|
|
635
|
+
const scroll = getScrollElement();
|
|
636
|
+
const target = getTargetElement();
|
|
637
|
+
if (!scroll || !pinActiveRef.current) return;
|
|
638
|
+
const targetScrollTop = target ? Math.max(
|
|
639
|
+
0,
|
|
640
|
+
scroll.scrollTop + target.getBoundingClientRect().top - scroll.getBoundingClientRect().top - margin
|
|
641
|
+
) : getTargetScrollTop?.(scroll);
|
|
642
|
+
if (targetScrollTop == null) return;
|
|
643
|
+
const previousSpacerHeight = getSpacerHeight();
|
|
644
|
+
const baseScrollHeight = scroll.scrollHeight - previousSpacerHeight;
|
|
645
|
+
const nextSpacerHeight = Math.max(
|
|
646
|
+
0,
|
|
647
|
+
targetScrollTop + scroll.clientHeight - baseScrollHeight
|
|
648
|
+
);
|
|
649
|
+
setSpacerHeightRef.current(nextSpacerHeight);
|
|
650
|
+
pinnedTargetScrollTopRef.current = targetScrollTop;
|
|
651
|
+
stopAutoScroll();
|
|
652
|
+
scroll.scrollTop = targetScrollTop;
|
|
653
|
+
if (nextSpacerHeight === 0 && previousSpacerHeight > 0) {
|
|
654
|
+
pinActiveRef.current = false;
|
|
655
|
+
scrollToBottom();
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
[
|
|
659
|
+
getScrollElement,
|
|
660
|
+
getSpacerHeight,
|
|
661
|
+
getTargetElement,
|
|
662
|
+
getTargetScrollTop,
|
|
663
|
+
margin,
|
|
664
|
+
scrollToBottom,
|
|
665
|
+
stopAutoScroll
|
|
666
|
+
]
|
|
667
|
+
);
|
|
668
|
+
const scheduleReposition = useCallback3(
|
|
669
|
+
() => {
|
|
670
|
+
if (!pinActiveRef.current) return;
|
|
671
|
+
repositionPendingRef.current = true;
|
|
672
|
+
if (frameRef.current != null) return;
|
|
673
|
+
frameRef.current = requestAnimationFrame(() => {
|
|
674
|
+
frameRef.current = null;
|
|
675
|
+
const shouldReposition = repositionPendingRef.current;
|
|
676
|
+
repositionPendingRef.current = false;
|
|
677
|
+
if (shouldReposition) reposition();
|
|
678
|
+
});
|
|
679
|
+
},
|
|
680
|
+
[reposition]
|
|
681
|
+
);
|
|
682
|
+
useEffect3(() => {
|
|
683
|
+
if (!targetKey) {
|
|
684
|
+
release();
|
|
685
|
+
appliedTargetKeyRef.current = null;
|
|
686
|
+
observedTargetKeyRef.current = null;
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (!pinTarget) {
|
|
690
|
+
if (pinActiveRef.current && appliedTargetKeyRef.current != null && appliedTargetKeyRef.current !== targetKey) {
|
|
691
|
+
release();
|
|
692
|
+
}
|
|
693
|
+
observedTargetKeyRef.current = targetKey;
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (appliedTargetKeyRef.current === targetKey) {
|
|
697
|
+
observedTargetKeyRef.current = targetKey;
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (observedTargetKeyRef.current === targetKey) return;
|
|
701
|
+
appliedTargetKeyRef.current = targetKey;
|
|
702
|
+
observedTargetKeyRef.current = targetKey;
|
|
703
|
+
pinActiveRef.current = true;
|
|
704
|
+
stopAutoScroll();
|
|
705
|
+
scheduleReposition();
|
|
706
|
+
if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current);
|
|
707
|
+
retryTimeoutRef.current = window.setTimeout(() => {
|
|
708
|
+
retryTimeoutRef.current = null;
|
|
709
|
+
scheduleReposition();
|
|
710
|
+
}, 80);
|
|
711
|
+
}, [pinTarget, release, scheduleReposition, stopAutoScroll, targetKey]);
|
|
712
|
+
useEffect3(() => {
|
|
713
|
+
if (layoutKey !== void 0) scheduleReposition();
|
|
714
|
+
}, [layoutKey, scheduleReposition]);
|
|
715
|
+
useEffect3(() => {
|
|
716
|
+
const scroll = getScrollElement();
|
|
717
|
+
if (!scroll) return;
|
|
718
|
+
const content = getContentElement?.();
|
|
719
|
+
const observer = new ResizeObserver(scheduleReposition);
|
|
720
|
+
observer.observe(scroll);
|
|
721
|
+
if (content && content !== scroll) observer.observe(content);
|
|
722
|
+
window.addEventListener("resize", scheduleReposition);
|
|
723
|
+
window.visualViewport?.addEventListener("resize", scheduleReposition);
|
|
724
|
+
const handleScroll = () => {
|
|
725
|
+
if (!pinActiveRef.current) return;
|
|
726
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
727
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
728
|
+
}
|
|
729
|
+
manualScrollCheckTimeoutRef.current = window.setTimeout(() => {
|
|
730
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
731
|
+
if (!pinActiveRef.current || repositionPendingRef.current) return;
|
|
732
|
+
const targetScrollTop = pinnedTargetScrollTopRef.current;
|
|
733
|
+
if (targetScrollTop != null && Math.abs(scroll.scrollTop - targetScrollTop) > MANUAL_SCROLL_TOLERANCE_PX) {
|
|
734
|
+
release();
|
|
735
|
+
}
|
|
736
|
+
}, 0);
|
|
737
|
+
};
|
|
738
|
+
scroll.addEventListener("scroll", handleScroll, { passive: true });
|
|
739
|
+
return () => {
|
|
740
|
+
observer.disconnect();
|
|
741
|
+
window.removeEventListener("resize", scheduleReposition);
|
|
742
|
+
window.visualViewport?.removeEventListener("resize", scheduleReposition);
|
|
743
|
+
scroll.removeEventListener("scroll", handleScroll);
|
|
744
|
+
};
|
|
745
|
+
}, [getContentElement, getScrollElement, release, scheduleReposition]);
|
|
746
|
+
useEffect3(
|
|
747
|
+
() => () => {
|
|
748
|
+
if (frameRef.current != null) cancelAnimationFrame(frameRef.current);
|
|
749
|
+
frameRef.current = null;
|
|
750
|
+
if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current);
|
|
751
|
+
retryTimeoutRef.current = null;
|
|
752
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
753
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
754
|
+
}
|
|
755
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
756
|
+
repositionPendingRef.current = false;
|
|
757
|
+
pinnedTargetScrollTopRef.current = null;
|
|
758
|
+
appliedTargetKeyRef.current = null;
|
|
759
|
+
observedTargetKeyRef.current = initialObservedTargetKeyRef.current;
|
|
760
|
+
pinActiveRef.current = false;
|
|
761
|
+
setSpacerHeightRef.current(0);
|
|
762
|
+
},
|
|
763
|
+
[]
|
|
764
|
+
);
|
|
765
|
+
const isActive = useCallback3(() => pinActiveRef.current, []);
|
|
766
|
+
return { release, isActive };
|
|
767
|
+
}
|
|
768
|
+
|
|
547
769
|
// src/components/AgentChat.tsx
|
|
548
770
|
import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
|
|
549
771
|
|
|
@@ -636,6 +858,18 @@ var ArrowUp = createLucideIcon("ArrowUp", [
|
|
|
636
858
|
["path", { d: "M12 19V5", key: "x0mq9r" }]
|
|
637
859
|
]);
|
|
638
860
|
|
|
861
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/book-open.js
|
|
862
|
+
var BookOpen = createLucideIcon("BookOpen", [
|
|
863
|
+
["path", { d: "M12 7v14", key: "1akyts" }],
|
|
864
|
+
[
|
|
865
|
+
"path",
|
|
866
|
+
{
|
|
867
|
+
d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",
|
|
868
|
+
key: "ruj8y"
|
|
869
|
+
}
|
|
870
|
+
]
|
|
871
|
+
]);
|
|
872
|
+
|
|
639
873
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/bot.js
|
|
640
874
|
var Bot = createLucideIcon("Bot", [
|
|
641
875
|
["path", { d: "M12 8V4H8", key: "hb8ula" }],
|
|
@@ -646,31 +880,6 @@ var Bot = createLucideIcon("Bot", [
|
|
|
646
880
|
["path", { d: "M9 13v2", key: "rq6x2g" }]
|
|
647
881
|
]);
|
|
648
882
|
|
|
649
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/brain.js
|
|
650
|
-
var Brain = createLucideIcon("Brain", [
|
|
651
|
-
[
|
|
652
|
-
"path",
|
|
653
|
-
{
|
|
654
|
-
d: "M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",
|
|
655
|
-
key: "l5xja"
|
|
656
|
-
}
|
|
657
|
-
],
|
|
658
|
-
[
|
|
659
|
-
"path",
|
|
660
|
-
{
|
|
661
|
-
d: "M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z",
|
|
662
|
-
key: "ep3f8r"
|
|
663
|
-
}
|
|
664
|
-
],
|
|
665
|
-
["path", { d: "M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4", key: "1p4c4q" }],
|
|
666
|
-
["path", { d: "M17.599 6.5a3 3 0 0 0 .399-1.375", key: "tmeiqw" }],
|
|
667
|
-
["path", { d: "M6.003 5.125A3 3 0 0 0 6.401 6.5", key: "105sqy" }],
|
|
668
|
-
["path", { d: "M3.477 10.896a4 4 0 0 1 .585-.396", key: "ql3yin" }],
|
|
669
|
-
["path", { d: "M19.938 10.5a4 4 0 0 1 .585.396", key: "1qfode" }],
|
|
670
|
-
["path", { d: "M6 18a4 4 0 0 1-1.967-.516", key: "2e4loj" }],
|
|
671
|
-
["path", { d: "M19.967 17.484A4 4 0 0 1 18 18", key: "159ez6" }]
|
|
672
|
-
]);
|
|
673
|
-
|
|
674
883
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/check.js
|
|
675
884
|
var Check = createLucideIcon("Check", [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]]);
|
|
676
885
|
|
|
@@ -697,11 +906,37 @@ var Copy = createLucideIcon("Copy", [
|
|
|
697
906
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
698
907
|
]);
|
|
699
908
|
|
|
700
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/
|
|
701
|
-
var
|
|
702
|
-
["path", { d: "M21
|
|
703
|
-
[
|
|
704
|
-
|
|
909
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/earth.js
|
|
910
|
+
var Earth = createLucideIcon("Earth", [
|
|
911
|
+
["path", { d: "M21.54 15H17a2 2 0 0 0-2 2v4.54", key: "1djwo0" }],
|
|
912
|
+
[
|
|
913
|
+
"path",
|
|
914
|
+
{
|
|
915
|
+
d: "M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",
|
|
916
|
+
key: "1tzkfa"
|
|
917
|
+
}
|
|
918
|
+
],
|
|
919
|
+
["path", { d: "M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05", key: "14pb5j" }],
|
|
920
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
|
|
921
|
+
]);
|
|
922
|
+
|
|
923
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-pen-line.js
|
|
924
|
+
var FilePenLine = createLucideIcon("FilePenLine", [
|
|
925
|
+
[
|
|
926
|
+
"path",
|
|
927
|
+
{
|
|
928
|
+
d: "m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2",
|
|
929
|
+
key: "142zxg"
|
|
930
|
+
}
|
|
931
|
+
],
|
|
932
|
+
[
|
|
933
|
+
"path",
|
|
934
|
+
{
|
|
935
|
+
d: "M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z",
|
|
936
|
+
key: "2t3380"
|
|
937
|
+
}
|
|
938
|
+
],
|
|
939
|
+
["path", { d: "M8 18h1", key: "13wk12" }]
|
|
705
940
|
]);
|
|
706
941
|
|
|
707
942
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
@@ -713,24 +948,6 @@ var FileText = createLucideIcon("FileText", [
|
|
|
713
948
|
["path", { d: "M16 17H8", key: "z1uh3a" }]
|
|
714
949
|
]);
|
|
715
950
|
|
|
716
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file.js
|
|
717
|
-
var File = createLucideIcon("File", [
|
|
718
|
-
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
719
|
-
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
720
|
-
]);
|
|
721
|
-
|
|
722
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/film.js
|
|
723
|
-
var Film = createLucideIcon("Film", [
|
|
724
|
-
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
|
|
725
|
-
["path", { d: "M7 3v18", key: "bbkbws" }],
|
|
726
|
-
["path", { d: "M3 7.5h4", key: "zfgn84" }],
|
|
727
|
-
["path", { d: "M3 12h18", key: "1i2n21" }],
|
|
728
|
-
["path", { d: "M3 16.5h4", key: "1230mu" }],
|
|
729
|
-
["path", { d: "M17 3v18", key: "in4fa5" }],
|
|
730
|
-
["path", { d: "M17 7.5h4", key: "myr1c1" }],
|
|
731
|
-
["path", { d: "M17 16.5h4", key: "go4c1d" }]
|
|
732
|
-
]);
|
|
733
|
-
|
|
734
951
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
|
|
735
952
|
var Globe = createLucideIcon("Globe", [
|
|
736
953
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
@@ -806,6 +1023,12 @@ var Play = createLucideIcon("Play", [
|
|
|
806
1023
|
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
807
1024
|
]);
|
|
808
1025
|
|
|
1026
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/search.js
|
|
1027
|
+
var Search = createLucideIcon("Search", [
|
|
1028
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
|
|
1029
|
+
["path", { d: "m21 21-4.3-4.3", key: "1qie3q" }]
|
|
1030
|
+
]);
|
|
1031
|
+
|
|
809
1032
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
|
|
810
1033
|
var Settings2 = createLucideIcon("Settings2", [
|
|
811
1034
|
["path", { d: "M20 7h-9", key: "3s1dr2" }],
|
|
@@ -834,6 +1057,12 @@ var Square = createLucideIcon("Square", [
|
|
|
834
1057
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
835
1058
|
]);
|
|
836
1059
|
|
|
1060
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/terminal.js
|
|
1061
|
+
var Terminal = createLucideIcon("Terminal", [
|
|
1062
|
+
["polyline", { points: "4 17 10 11 4 5", key: "akl6gq" }],
|
|
1063
|
+
["line", { x1: "12", x2: "20", y1: "19", y2: "19", key: "q2wloq" }]
|
|
1064
|
+
]);
|
|
1065
|
+
|
|
837
1066
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/triangle-alert.js
|
|
838
1067
|
var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
839
1068
|
[
|
|
@@ -847,6 +1076,17 @@ var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
|
847
1076
|
["path", { d: "M12 17h.01", key: "p32p05" }]
|
|
848
1077
|
]);
|
|
849
1078
|
|
|
1079
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/wrench.js
|
|
1080
|
+
var Wrench = createLucideIcon("Wrench", [
|
|
1081
|
+
[
|
|
1082
|
+
"path",
|
|
1083
|
+
{
|
|
1084
|
+
d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",
|
|
1085
|
+
key: "cbrjhi"
|
|
1086
|
+
}
|
|
1087
|
+
]
|
|
1088
|
+
]);
|
|
1089
|
+
|
|
850
1090
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/x.js
|
|
851
1091
|
var X = createLucideIcon("X", [
|
|
852
1092
|
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
@@ -854,7 +1094,7 @@ var X = createLucideIcon("X", [
|
|
|
854
1094
|
]);
|
|
855
1095
|
|
|
856
1096
|
// src/components/AgentChat.tsx
|
|
857
|
-
import { useCallback as
|
|
1097
|
+
import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo8, useState as useState13 } from "react";
|
|
858
1098
|
|
|
859
1099
|
// src/lib/utils.ts
|
|
860
1100
|
function cn(...inputs) {
|
|
@@ -983,6 +1223,12 @@ function ReplayMismatchPrompt({ mismatch, className }) {
|
|
|
983
1223
|
|
|
984
1224
|
// src/components/ChatInput.tsx
|
|
985
1225
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1226
|
+
function isImeCompositionKey(event) {
|
|
1227
|
+
return event.isComposing || event.keyCode === 229;
|
|
1228
|
+
}
|
|
1229
|
+
function shouldSubmitChatInput(event, menuOpen) {
|
|
1230
|
+
return event.key === "Enter" && !event.shiftKey && !menuOpen && !isImeCompositionKey(event);
|
|
1231
|
+
}
|
|
986
1232
|
function ChatInput({
|
|
987
1233
|
value,
|
|
988
1234
|
onValueChange,
|
|
@@ -1002,7 +1248,12 @@ function ChatInput({
|
|
|
1002
1248
|
onValueChange("");
|
|
1003
1249
|
};
|
|
1004
1250
|
const handleKeyDown = (event) => {
|
|
1005
|
-
if (
|
|
1251
|
+
if (shouldSubmitChatInput({
|
|
1252
|
+
key: event.key,
|
|
1253
|
+
shiftKey: event.shiftKey,
|
|
1254
|
+
isComposing: event.nativeEvent.isComposing,
|
|
1255
|
+
keyCode: event.nativeEvent.keyCode
|
|
1256
|
+
}, false)) {
|
|
1006
1257
|
event.preventDefault();
|
|
1007
1258
|
void handleSend();
|
|
1008
1259
|
}
|
|
@@ -1052,24 +1303,74 @@ function ChatInput({
|
|
|
1052
1303
|
}
|
|
1053
1304
|
|
|
1054
1305
|
// src/components/ConnectionBanner.tsx
|
|
1306
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
1055
1307
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1308
|
+
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1309
|
+
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1310
|
+
function useConnectionNoticePhase(connected) {
|
|
1311
|
+
const [phase, setPhase] = useState4("hidden");
|
|
1312
|
+
const connectedRef = useRef4(connected);
|
|
1313
|
+
const timersRef = useRef4([]);
|
|
1314
|
+
connectedRef.current = connected;
|
|
1315
|
+
useEffect4(() => {
|
|
1316
|
+
const clearTimers = () => {
|
|
1317
|
+
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1318
|
+
timersRef.current = [];
|
|
1319
|
+
};
|
|
1320
|
+
const startGracePeriod = () => {
|
|
1321
|
+
clearTimers();
|
|
1322
|
+
setPhase("hidden");
|
|
1323
|
+
timersRef.current = [
|
|
1324
|
+
setTimeout(() => setPhase("recovering"), CONNECTION_NOTICE_DELAY_MS),
|
|
1325
|
+
setTimeout(() => setPhase("failed"), CONNECTION_ERROR_DELAY_MS)
|
|
1326
|
+
];
|
|
1327
|
+
};
|
|
1328
|
+
if (connected) {
|
|
1329
|
+
clearTimers();
|
|
1330
|
+
setPhase("hidden");
|
|
1331
|
+
} else {
|
|
1332
|
+
startGracePeriod();
|
|
1333
|
+
}
|
|
1334
|
+
const handleForeground = () => {
|
|
1335
|
+
if (!connectedRef.current) startGracePeriod();
|
|
1336
|
+
};
|
|
1337
|
+
const handleVisibilityChange = () => {
|
|
1338
|
+
if (document.visibilityState === "visible") handleForeground();
|
|
1339
|
+
};
|
|
1340
|
+
window.addEventListener("blade:app-active", handleForeground);
|
|
1341
|
+
window.addEventListener("focus", handleForeground);
|
|
1342
|
+
window.addEventListener("pageshow", handleForeground);
|
|
1343
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1344
|
+
return () => {
|
|
1345
|
+
clearTimers();
|
|
1346
|
+
window.removeEventListener("blade:app-active", handleForeground);
|
|
1347
|
+
window.removeEventListener("focus", handleForeground);
|
|
1348
|
+
window.removeEventListener("pageshow", handleForeground);
|
|
1349
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1350
|
+
};
|
|
1351
|
+
}, [connected]);
|
|
1352
|
+
return phase;
|
|
1353
|
+
}
|
|
1056
1354
|
function ConnectionBanner({ connection, className }) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const
|
|
1355
|
+
const hasConnectedRef = useRef4(connection === "connected" || connection === "reconnecting");
|
|
1356
|
+
if (connection === "connected") hasConnectedRef.current = true;
|
|
1357
|
+
const connected = connection === "connected";
|
|
1358
|
+
const phase = useConnectionNoticePhase(connected);
|
|
1359
|
+
if (connected || phase === "hidden") return null;
|
|
1360
|
+
const recovering = phase === "recovering";
|
|
1361
|
+
const firstConnection = !hasConnectedRef.current;
|
|
1061
1362
|
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
1062
1363
|
"div",
|
|
1063
1364
|
{
|
|
1064
1365
|
className: cn(
|
|
1065
1366
|
"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
|
|
1066
|
-
|
|
1367
|
+
recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
1067
1368
|
),
|
|
1068
1369
|
children: [
|
|
1069
|
-
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children:
|
|
1370
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
|
|
1070
1371
|
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
1071
|
-
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children:
|
|
1072
|
-
/* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: "\
|
|
1372
|
+
/* @__PURE__ */ jsx5("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" }),
|
|
1373
|
+
/* @__PURE__ */ jsx5("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" })
|
|
1073
1374
|
] })
|
|
1074
1375
|
]
|
|
1075
1376
|
}
|
|
@@ -1078,10 +1379,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1078
1379
|
|
|
1079
1380
|
// src/components/MessageList.tsx
|
|
1080
1381
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1081
|
-
import { useCallback as
|
|
1382
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo7, useRef as useRef11, useState as useState12 } from "react";
|
|
1082
1383
|
|
|
1083
1384
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1084
|
-
import { useCallback as
|
|
1385
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
|
|
1085
1386
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1086
1387
|
/**
|
|
1087
1388
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1118,12 +1419,12 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1118
1419
|
mouseDown = false;
|
|
1119
1420
|
});
|
|
1120
1421
|
var useStickToBottom = (options = {}) => {
|
|
1121
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1122
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1123
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1124
|
-
const optionsRef =
|
|
1422
|
+
const [escapedFromLock, updateEscapedFromLock] = useState5(false);
|
|
1423
|
+
const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
|
|
1424
|
+
const [isNearBottom, setIsNearBottom] = useState5(false);
|
|
1425
|
+
const optionsRef = useRef5(null);
|
|
1125
1426
|
optionsRef.current = options;
|
|
1126
|
-
const isSelecting =
|
|
1427
|
+
const isSelecting = useCallback4(() => {
|
|
1127
1428
|
if (!mouseDown) {
|
|
1128
1429
|
return false;
|
|
1129
1430
|
}
|
|
@@ -1134,11 +1435,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1134
1435
|
const range = selection.getRangeAt(0);
|
|
1135
1436
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
1136
1437
|
}, []);
|
|
1137
|
-
const setIsAtBottom =
|
|
1438
|
+
const setIsAtBottom = useCallback4((isAtBottom2) => {
|
|
1138
1439
|
state.isAtBottom = isAtBottom2;
|
|
1139
1440
|
updateIsAtBottom(isAtBottom2);
|
|
1140
1441
|
}, []);
|
|
1141
|
-
const setEscapedFromLock =
|
|
1442
|
+
const setEscapedFromLock = useCallback4((escapedFromLock2) => {
|
|
1142
1443
|
state.escapedFromLock = escapedFromLock2;
|
|
1143
1444
|
updateEscapedFromLock(escapedFromLock2);
|
|
1144
1445
|
}, []);
|
|
@@ -1195,7 +1496,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1195
1496
|
}
|
|
1196
1497
|
};
|
|
1197
1498
|
}, []);
|
|
1198
|
-
const scrollToBottom =
|
|
1499
|
+
const scrollToBottom = useCallback4((scrollOptions = {}) => {
|
|
1199
1500
|
if (typeof scrollOptions === "string") {
|
|
1200
1501
|
scrollOptions = { animation: scrollOptions };
|
|
1201
1502
|
}
|
|
@@ -1280,11 +1581,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1280
1581
|
}
|
|
1281
1582
|
return next();
|
|
1282
1583
|
}, [setIsAtBottom, isSelecting, state]);
|
|
1283
|
-
const stopScroll =
|
|
1584
|
+
const stopScroll = useCallback4(() => {
|
|
1284
1585
|
setEscapedFromLock(true);
|
|
1285
1586
|
setIsAtBottom(false);
|
|
1286
1587
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
1287
|
-
const handleScroll =
|
|
1588
|
+
const handleScroll = useCallback4(({ target }) => {
|
|
1288
1589
|
if (target !== scrollRef.current) {
|
|
1289
1590
|
return;
|
|
1290
1591
|
}
|
|
@@ -1323,7 +1624,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1323
1624
|
}
|
|
1324
1625
|
}, 1);
|
|
1325
1626
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
1326
|
-
const handleWheel =
|
|
1627
|
+
const handleWheel = useCallback4(({ target, deltaY }) => {
|
|
1327
1628
|
let element = target;
|
|
1328
1629
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
1329
1630
|
if (!element.parentElement) {
|
|
@@ -1393,7 +1694,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1393
1694
|
};
|
|
1394
1695
|
};
|
|
1395
1696
|
function useRefCallback(callback, deps) {
|
|
1396
|
-
const result =
|
|
1697
|
+
const result = useCallback4((ref) => {
|
|
1397
1698
|
result.current = ref;
|
|
1398
1699
|
return callback(ref);
|
|
1399
1700
|
}, deps);
|
|
@@ -1425,11 +1726,11 @@ function mergeAnimations(...animations) {
|
|
|
1425
1726
|
|
|
1426
1727
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1427
1728
|
import * as React from "react";
|
|
1428
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
1729
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef6 } from "react";
|
|
1429
1730
|
var StickToBottomContext = createContext2(null);
|
|
1430
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1731
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect5;
|
|
1431
1732
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1432
|
-
const customTargetScrollTop =
|
|
1733
|
+
const customTargetScrollTop = useRef6(null);
|
|
1433
1734
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1434
1735
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1435
1736
|
return get?.(target, elements) ?? target;
|
|
@@ -1505,11 +1806,16 @@ function useStickToBottomContext() {
|
|
|
1505
1806
|
}
|
|
1506
1807
|
|
|
1507
1808
|
// src/components/AssistantTurnBlock.tsx
|
|
1508
|
-
import {
|
|
1509
|
-
|
|
1809
|
+
import {
|
|
1810
|
+
getFileParts,
|
|
1811
|
+
getImageParts,
|
|
1812
|
+
getTextContent,
|
|
1813
|
+
normalizeMessageContent
|
|
1814
|
+
} from "@blade-hq/agent-client";
|
|
1815
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState10 } from "react";
|
|
1510
1816
|
|
|
1511
1817
|
// src/components/AgentLoopBlock.tsx
|
|
1512
|
-
import { useState as
|
|
1818
|
+
import { useState as useState6 } from "react";
|
|
1513
1819
|
|
|
1514
1820
|
// src/components/display-utils.ts
|
|
1515
1821
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1525,7 +1831,9 @@ var TOOL_NAME_ALIASES = {
|
|
|
1525
1831
|
finish_task: "FinishTask",
|
|
1526
1832
|
glob: "Glob",
|
|
1527
1833
|
grep: "Grep",
|
|
1834
|
+
kb_search: "KbSearch",
|
|
1528
1835
|
ls: "Ls",
|
|
1836
|
+
multi_edit: "MultiEdit",
|
|
1529
1837
|
read: "Read",
|
|
1530
1838
|
read_skill: "ReadSkill",
|
|
1531
1839
|
web_fetch: "WebFetch",
|
|
@@ -1538,9 +1846,11 @@ var TOOL_DISPLAY_LABELS = {
|
|
|
1538
1846
|
Read: "\u8BFB\u53D6\u6587\u4EF6",
|
|
1539
1847
|
Write: "\u5199\u5165\u6587\u4EF6",
|
|
1540
1848
|
Edit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1849
|
+
MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1541
1850
|
Ls: "\u5217\u51FA\u76EE\u5F55",
|
|
1542
1851
|
Glob: "\u5339\u914D\u6587\u4EF6",
|
|
1543
1852
|
Grep: "\u641C\u7D22\u6587\u672C",
|
|
1853
|
+
KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
|
|
1544
1854
|
WebSearch: "\u641C\u7D22\u7F51\u9875",
|
|
1545
1855
|
WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
|
|
1546
1856
|
Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
|
|
@@ -1551,6 +1861,16 @@ var TOOL_DISPLAY_LABELS = {
|
|
|
1551
1861
|
ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
|
|
1552
1862
|
GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
|
|
1553
1863
|
};
|
|
1864
|
+
var PRIORITY_FILE_KEYS = [
|
|
1865
|
+
"file_path",
|
|
1866
|
+
"output_file",
|
|
1867
|
+
"output_path",
|
|
1868
|
+
"path",
|
|
1869
|
+
"filepath",
|
|
1870
|
+
"target_file",
|
|
1871
|
+
"destination",
|
|
1872
|
+
"filename"
|
|
1873
|
+
];
|
|
1554
1874
|
function safeParseJson(value) {
|
|
1555
1875
|
if (!value) return null;
|
|
1556
1876
|
try {
|
|
@@ -1559,10 +1879,53 @@ function safeParseJson(value) {
|
|
|
1559
1879
|
return null;
|
|
1560
1880
|
}
|
|
1561
1881
|
}
|
|
1882
|
+
function isPlainObject(value) {
|
|
1883
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1884
|
+
}
|
|
1885
|
+
function findFileLikeValue(value, depth = 0, allowPlainString = false) {
|
|
1886
|
+
if (depth > 3) return null;
|
|
1887
|
+
if (typeof value === "string") {
|
|
1888
|
+
const trimmed = value.trim();
|
|
1889
|
+
if (!trimmed) return null;
|
|
1890
|
+
if (allowPlainString || trimmed.includes("/") || /\.[a-z0-9]{1,8}$/i.test(trimmed)) {
|
|
1891
|
+
return trimmed;
|
|
1892
|
+
}
|
|
1893
|
+
return null;
|
|
1894
|
+
}
|
|
1895
|
+
if (Array.isArray(value)) {
|
|
1896
|
+
for (const item of value) {
|
|
1897
|
+
const found = findFileLikeValue(item, depth + 1, allowPlainString);
|
|
1898
|
+
if (found) return found;
|
|
1899
|
+
}
|
|
1900
|
+
return null;
|
|
1901
|
+
}
|
|
1902
|
+
if (isPlainObject(value)) {
|
|
1903
|
+
for (const key of PRIORITY_FILE_KEYS) {
|
|
1904
|
+
const direct = findFileLikeValue(value[key], depth + 1, true);
|
|
1905
|
+
if (direct) return direct;
|
|
1906
|
+
}
|
|
1907
|
+
for (const nested of Object.values(value)) {
|
|
1908
|
+
const found = findFileLikeValue(nested, depth + 1, allowPlainString);
|
|
1909
|
+
if (found) return found;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1562
1914
|
function getStringArgValue(args, key) {
|
|
1563
1915
|
const value = args?.[key];
|
|
1564
1916
|
return typeof value === "string" ? value.trim() : "";
|
|
1565
1917
|
}
|
|
1918
|
+
var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
|
|
1919
|
+
var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
|
|
1920
|
+
function getSkillNameFromFilePath(filePath) {
|
|
1921
|
+
if (!filePath) return null;
|
|
1922
|
+
const segments = filePath.split(/[\\/]+/).filter(Boolean);
|
|
1923
|
+
const fileName = segments.pop();
|
|
1924
|
+
if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
|
|
1925
|
+
const dirName = segments.pop();
|
|
1926
|
+
if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
|
|
1927
|
+
return dirName;
|
|
1928
|
+
}
|
|
1566
1929
|
function formatToolName(name) {
|
|
1567
1930
|
const trimmed = name.trim();
|
|
1568
1931
|
if (!trimmed) return name;
|
|
@@ -1570,6 +1933,15 @@ function formatToolName(name) {
|
|
|
1570
1933
|
const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
1571
1934
|
return TOOL_NAME_ALIASES[normalized] ?? stripped;
|
|
1572
1935
|
}
|
|
1936
|
+
function extractToolFilePath(toolCall) {
|
|
1937
|
+
const formattedName = formatToolName(toolCall.name);
|
|
1938
|
+
if (formattedName !== "Read" && formattedName !== "Write" && formattedName !== "Edit" && formattedName !== "MultiEdit") {
|
|
1939
|
+
return null;
|
|
1940
|
+
}
|
|
1941
|
+
const filePath = findFileLikeValue(safeParseJson(toolCall.arguments));
|
|
1942
|
+
if (!filePath || filePath.split("/").pop()?.toLowerCase() === "phase.json") return null;
|
|
1943
|
+
return filePath;
|
|
1944
|
+
}
|
|
1573
1945
|
function getToolDisplayLabel(toolCall) {
|
|
1574
1946
|
const normalized = formatToolName(toolCall.name);
|
|
1575
1947
|
const args = safeParseJson(toolCall.arguments);
|
|
@@ -1587,6 +1959,12 @@ function getToolDisplayLabel(toolCall) {
|
|
|
1587
1959
|
const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
|
|
1588
1960
|
return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
|
|
1589
1961
|
}
|
|
1962
|
+
if (normalized === "Read") {
|
|
1963
|
+
const skillName = getSkillNameFromFilePath(
|
|
1964
|
+
getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
|
|
1965
|
+
);
|
|
1966
|
+
if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
|
|
1967
|
+
}
|
|
1590
1968
|
if (normalized === "FinishTask") {
|
|
1591
1969
|
const title = getStringArgValue(args, "title");
|
|
1592
1970
|
return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
|
|
@@ -1641,83 +2019,68 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1641
2019
|
}
|
|
1642
2020
|
}
|
|
1643
2021
|
function AgentLoopBlock({ toolCall }) {
|
|
1644
|
-
const [expanded, setExpanded] =
|
|
2022
|
+
const [expanded, setExpanded] = useState6(false);
|
|
1645
2023
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1646
2024
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1647
2025
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1648
|
-
|
|
2026
|
+
const hasResult = toolCall.result != null;
|
|
2027
|
+
const iconClass = cn(
|
|
2028
|
+
"size-3.5 shrink-0",
|
|
2029
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2030
|
+
);
|
|
2031
|
+
return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
|
|
1649
2032
|
/* @__PURE__ */ jsxs5(
|
|
1650
|
-
"
|
|
2033
|
+
"button",
|
|
1651
2034
|
{
|
|
2035
|
+
type: "button",
|
|
2036
|
+
onClick: () => hasResult && setExpanded(!expanded),
|
|
2037
|
+
disabled: !hasResult,
|
|
2038
|
+
"aria-expanded": hasResult ? expanded : void 0,
|
|
2039
|
+
"data-testid": "execution-tool-intent",
|
|
1652
2040
|
className: cn(
|
|
1653
|
-
"
|
|
1654
|
-
|
|
2041
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-left",
|
|
2042
|
+
hasResult && "cursor-pointer hover:text-[hsl(var(--foreground))]",
|
|
2043
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
1655
2044
|
),
|
|
2045
|
+
title: `\u5B50\u4EFB\u52A1\uFF1A${description}`,
|
|
1656
2046
|
children: [
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
|
|
2047
|
+
running ? /* @__PURE__ */ jsx6(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx6(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx6(X, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx6(Bot, { className: iconClass, "aria-hidden": "true" }),
|
|
2048
|
+
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 truncate", children: [
|
|
2049
|
+
"\u5B50\u4EFB\u52A1\uFF1A",
|
|
2050
|
+
description
|
|
2051
|
+
] }),
|
|
2052
|
+
hasResult ? /* @__PURE__ */ jsx6(
|
|
2053
|
+
ChevronRight,
|
|
1659
2054
|
{
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
ChevronRight,
|
|
1667
|
-
{
|
|
1668
|
-
size: 11,
|
|
1669
|
-
className: cn(
|
|
1670
|
-
"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform",
|
|
1671
|
-
expanded && "rotate-90"
|
|
1672
|
-
)
|
|
1673
|
-
}
|
|
1674
|
-
),
|
|
1675
|
-
/* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1676
|
-
/* @__PURE__ */ jsxs5(
|
|
1677
|
-
"span",
|
|
1678
|
-
{
|
|
1679
|
-
className: cn(
|
|
1680
|
-
"flex shrink-0 items-center gap-1 text-[10px]",
|
|
1681
|
-
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1682
|
-
),
|
|
1683
|
-
children: [
|
|
1684
|
-
running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
|
|
1685
|
-
/* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
|
|
1686
|
-
]
|
|
1687
|
-
}
|
|
1688
|
-
),
|
|
1689
|
-
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1690
|
-
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1691
|
-
description
|
|
1692
|
-
] })
|
|
1693
|
-
]
|
|
2055
|
+
size: 14,
|
|
2056
|
+
className: cn(
|
|
2057
|
+
"shrink-0 transition-transform duration-300",
|
|
2058
|
+
expanded && "rotate-90"
|
|
2059
|
+
),
|
|
2060
|
+
"aria-hidden": "true"
|
|
1694
2061
|
}
|
|
1695
|
-
)
|
|
1696
|
-
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx6("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
|
|
2062
|
+
) : null
|
|
1697
2063
|
]
|
|
1698
2064
|
}
|
|
1699
2065
|
),
|
|
1700
|
-
expanded &&
|
|
1701
|
-
/* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
1702
|
-
/* @__PURE__ */ jsx6("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
|
|
1703
|
-
] })
|
|
2066
|
+
expanded && hasResult ? /* @__PURE__ */ jsx6("div", { className: "ml-[18px] mt-1.5 max-h-[400px] overflow-auto whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: formatToolResult(toolCall.result) }) : null
|
|
1704
2067
|
] });
|
|
1705
2068
|
}
|
|
1706
2069
|
|
|
1707
2070
|
// src/components/MarkdownContent.tsx
|
|
1708
2071
|
import {
|
|
1709
|
-
useEffect as
|
|
2072
|
+
useEffect as useEffect6,
|
|
1710
2073
|
useMemo as useMemo5,
|
|
1711
|
-
useRef as
|
|
1712
|
-
useState as
|
|
2074
|
+
useRef as useRef7,
|
|
2075
|
+
useState as useState7
|
|
1713
2076
|
} from "react";
|
|
1714
2077
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1715
2078
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1716
2079
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1717
|
-
const preRef =
|
|
1718
|
-
const [copied, setCopied] =
|
|
1719
|
-
const [language, setLanguage] =
|
|
1720
|
-
|
|
2080
|
+
const preRef = useRef7(null);
|
|
2081
|
+
const [copied, setCopied] = useState7(false);
|
|
2082
|
+
const [language, setLanguage] = useState7("");
|
|
2083
|
+
useEffect6(() => {
|
|
1721
2084
|
const codeEl = preRef.current?.querySelector("code");
|
|
1722
2085
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1723
2086
|
}, []);
|
|
@@ -1788,11 +2151,40 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
1788
2151
|
}
|
|
1789
2152
|
|
|
1790
2153
|
// src/components/ToolCallBlock.tsx
|
|
1791
|
-
import { useState as
|
|
2154
|
+
import { useState as useState9 } from "react";
|
|
1792
2155
|
|
|
1793
2156
|
// src/components/AskUserQuestionBlock.tsx
|
|
1794
|
-
import { useEffect as
|
|
2157
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState8 } from "react";
|
|
1795
2158
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2159
|
+
var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
|
|
2160
|
+
function resizeCustomTextarea(textarea) {
|
|
2161
|
+
textarea.style.height = "auto";
|
|
2162
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, CUSTOM_TEXTAREA_MAX_HEIGHT)}px`;
|
|
2163
|
+
textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
|
|
2164
|
+
}
|
|
2165
|
+
function useAutoResizeTextarea(value) {
|
|
2166
|
+
const textareaRef = useRef8(null);
|
|
2167
|
+
useEffect7(() => {
|
|
2168
|
+
const textarea = textareaRef.current;
|
|
2169
|
+
if (textarea?.value === value) resizeCustomTextarea(textarea);
|
|
2170
|
+
}, [value]);
|
|
2171
|
+
useEffect7(() => {
|
|
2172
|
+
const textarea = textareaRef.current;
|
|
2173
|
+
if (!textarea || typeof ResizeObserver === "undefined") return;
|
|
2174
|
+
let previousWidth = textarea.clientWidth;
|
|
2175
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
2176
|
+
if (!entry || entry.contentRect.width === previousWidth) return;
|
|
2177
|
+
previousWidth = entry.contentRect.width;
|
|
2178
|
+
resizeCustomTextarea(textarea);
|
|
2179
|
+
});
|
|
2180
|
+
observer.observe(textarea);
|
|
2181
|
+
return () => observer.disconnect();
|
|
2182
|
+
}, []);
|
|
2183
|
+
return textareaRef;
|
|
2184
|
+
}
|
|
2185
|
+
function indentAnswerContinuationLines(answer) {
|
|
2186
|
+
return answer.replaceAll("\n", "\n ");
|
|
2187
|
+
}
|
|
1796
2188
|
function AskUserQuestionBlock({
|
|
1797
2189
|
data,
|
|
1798
2190
|
answered,
|
|
@@ -1801,18 +2193,19 @@ function AskUserQuestionBlock({
|
|
|
1801
2193
|
answerData,
|
|
1802
2194
|
onAnswer
|
|
1803
2195
|
}) {
|
|
1804
|
-
const [selections, setSelections] =
|
|
1805
|
-
const [customTexts, setCustomTexts] =
|
|
1806
|
-
const [usingCustom, setUsingCustom] =
|
|
1807
|
-
const [
|
|
1808
|
-
|
|
2196
|
+
const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
|
|
2197
|
+
const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
|
|
2198
|
+
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
2199
|
+
const [note, setNote] = useState8("");
|
|
2200
|
+
const [submitted, setSubmitted] = useState8(false);
|
|
2201
|
+
useEffect7(() => {
|
|
1809
2202
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1810
2203
|
setSubmitted(false);
|
|
1811
2204
|
}
|
|
1812
2205
|
}, [sessionStatus]);
|
|
1813
2206
|
const displayAnswerState = useMemo6(() => {
|
|
1814
2207
|
if (!(answered && answerData)) {
|
|
1815
|
-
return { selections, customTexts, usingCustom };
|
|
2208
|
+
return { selections, customTexts, usingCustom, note };
|
|
1816
2209
|
}
|
|
1817
2210
|
const nextSelections = /* @__PURE__ */ new Map();
|
|
1818
2211
|
const nextCustomTexts = /* @__PURE__ */ new Map();
|
|
@@ -1828,9 +2221,10 @@ function AskUserQuestionBlock({
|
|
|
1828
2221
|
return {
|
|
1829
2222
|
selections: nextSelections,
|
|
1830
2223
|
customTexts: nextCustomTexts,
|
|
1831
|
-
usingCustom: nextUsingCustom
|
|
2224
|
+
usingCustom: nextUsingCustom,
|
|
2225
|
+
note: answerData.note ?? ""
|
|
1832
2226
|
};
|
|
1833
|
-
}, [answerData, answered, customTexts, selections, usingCustom]);
|
|
2227
|
+
}, [answerData, answered, customTexts, note, selections, usingCustom]);
|
|
1834
2228
|
const toggleOption = (qIdx, optIdx, multi) => {
|
|
1835
2229
|
if (answered || submitted) return;
|
|
1836
2230
|
setSelections((prev) => {
|
|
@@ -1878,6 +2272,7 @@ function AskUserQuestionBlock({
|
|
|
1878
2272
|
const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null);
|
|
1879
2273
|
const handleSubmit = () => {
|
|
1880
2274
|
if (answered || submitted || !allAnswered || !onAnswer) return;
|
|
2275
|
+
const trimmedNote = note.trim();
|
|
1881
2276
|
const nextAnswerData = {
|
|
1882
2277
|
selections: Object.fromEntries(
|
|
1883
2278
|
Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [
|
|
@@ -1887,11 +2282,17 @@ function AskUserQuestionBlock({
|
|
|
1887
2282
|
),
|
|
1888
2283
|
custom: Object.fromEntries(
|
|
1889
2284
|
Array.from(usingCustom).map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? "").trim()]).filter(([, text2]) => text2.length > 0)
|
|
1890
|
-
)
|
|
2285
|
+
),
|
|
2286
|
+
...trimmedNote ? { note: trimmedNote } : {}
|
|
1891
2287
|
};
|
|
1892
|
-
const parts = data.questions.map(
|
|
1893
|
-
|
|
1894
|
-
|
|
2288
|
+
const parts = data.questions.map(
|
|
2289
|
+
(q, i) => `- ${q.question} -> ${indentAnswerContinuationLines(getAnswer(i) ?? "")}`
|
|
2290
|
+
);
|
|
2291
|
+
const text = [
|
|
2292
|
+
`\u5173\u4E8E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\uFF0C\u7528\u6237\u7684\u56DE\u7B54\u5982\u4E0B\uFF1A
|
|
2293
|
+
${parts.join("\n")}`,
|
|
2294
|
+
trimmedNote ? `\u8865\u5145\u8BF4\u660E\uFF1A${indentAnswerContinuationLines(trimmedNote)}` : ""
|
|
2295
|
+
].filter(Boolean).join("\n");
|
|
1895
2296
|
setSubmitted(true);
|
|
1896
2297
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
1897
2298
|
};
|
|
@@ -1923,6 +2324,15 @@ ${parts.join("\n")}`;
|
|
|
1923
2324
|
},
|
|
1924
2325
|
q.question
|
|
1925
2326
|
)),
|
|
2327
|
+
/* @__PURE__ */ jsx9(
|
|
2328
|
+
NoteField,
|
|
2329
|
+
{
|
|
2330
|
+
answered,
|
|
2331
|
+
submitted,
|
|
2332
|
+
note: displayAnswerState.note,
|
|
2333
|
+
onChange: setNote
|
|
2334
|
+
}
|
|
2335
|
+
),
|
|
1926
2336
|
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
|
|
1927
2337
|
"button",
|
|
1928
2338
|
{
|
|
@@ -1961,6 +2371,7 @@ function QuestionCard({
|
|
|
1961
2371
|
onCustomChange
|
|
1962
2372
|
}) {
|
|
1963
2373
|
const multi = question.multiSelect ?? false;
|
|
2374
|
+
const customTextareaRef = useAutoResizeTextarea(customText);
|
|
1964
2375
|
return /* @__PURE__ */ jsxs7("div", { children: [
|
|
1965
2376
|
/* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
1966
2377
|
/* @__PURE__ */ jsx9(
|
|
@@ -2037,25 +2448,26 @@ function QuestionCard({
|
|
|
2037
2448
|
"div",
|
|
2038
2449
|
{
|
|
2039
2450
|
className: cn(
|
|
2040
|
-
"flex items-
|
|
2451
|
+
"flex items-start gap-2 rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]",
|
|
2041
2452
|
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2042
2453
|
isCustom ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
|
|
2043
2454
|
answered && "cursor-default opacity-70"
|
|
2044
2455
|
),
|
|
2045
2456
|
children: [
|
|
2046
|
-
/* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2457
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2047
2458
|
/* @__PURE__ */ jsx9(
|
|
2048
|
-
"
|
|
2459
|
+
"textarea",
|
|
2049
2460
|
{
|
|
2050
|
-
|
|
2461
|
+
ref: customTextareaRef,
|
|
2462
|
+
rows: 2,
|
|
2051
2463
|
value: customText,
|
|
2052
|
-
|
|
2464
|
+
readOnly: answered,
|
|
2053
2465
|
onChange: (e) => onCustomChange(qIdx, e.target.value),
|
|
2054
2466
|
onFocus: () => onCustomFocus(qIdx),
|
|
2055
2467
|
"aria-label": "\u81EA\u5B9A\u4E49\u56DE\u7B54",
|
|
2056
2468
|
placeholder: "\u8F93\u5165\u4F60\u7684\u7B54\u6848...",
|
|
2057
2469
|
className: cn(
|
|
2058
|
-
"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2470
|
+
"min-h-10 min-w-0 flex-1 resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2059
2471
|
answered ? "text-xs" : "text-sm"
|
|
2060
2472
|
)
|
|
2061
2473
|
}
|
|
@@ -2066,6 +2478,49 @@ function QuestionCard({
|
|
|
2066
2478
|
] })
|
|
2067
2479
|
] });
|
|
2068
2480
|
}
|
|
2481
|
+
function NoteField({
|
|
2482
|
+
answered,
|
|
2483
|
+
submitted,
|
|
2484
|
+
note,
|
|
2485
|
+
onChange
|
|
2486
|
+
}) {
|
|
2487
|
+
const textareaRef = useAutoResizeTextarea(note);
|
|
2488
|
+
const readOnly = answered || submitted;
|
|
2489
|
+
if (answered && !note.trim()) return null;
|
|
2490
|
+
return /* @__PURE__ */ jsxs7(
|
|
2491
|
+
"label",
|
|
2492
|
+
{
|
|
2493
|
+
className: cn(
|
|
2494
|
+
"block rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]",
|
|
2495
|
+
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2496
|
+
note.trim() ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
|
|
2497
|
+
readOnly && "cursor-default opacity-70"
|
|
2498
|
+
),
|
|
2499
|
+
children: [
|
|
2500
|
+
/* @__PURE__ */ jsx9("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
|
|
2501
|
+
/* @__PURE__ */ jsx9(
|
|
2502
|
+
"textarea",
|
|
2503
|
+
{
|
|
2504
|
+
ref: textareaRef,
|
|
2505
|
+
rows: 2,
|
|
2506
|
+
value: note,
|
|
2507
|
+
readOnly,
|
|
2508
|
+
onChange: (event) => {
|
|
2509
|
+
if (readOnly) return;
|
|
2510
|
+
onChange(event.target.value);
|
|
2511
|
+
},
|
|
2512
|
+
"aria-label": "\u8865\u5145\u8BF4\u660E",
|
|
2513
|
+
placeholder: "\u9009\u5B8C\u8FD8\u53EF\u4EE5\u518D\u8BB2\u4E24\u53E5\uFF0C\u7A7A\u7740\u5C31\u5F53\u6CA1\u6709",
|
|
2514
|
+
className: cn(
|
|
2515
|
+
"min-h-10 w-full resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2516
|
+
answered ? "text-xs" : "text-sm"
|
|
2517
|
+
)
|
|
2518
|
+
}
|
|
2519
|
+
)
|
|
2520
|
+
]
|
|
2521
|
+
}
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2069
2524
|
function parseAskUserQuestion(toolResult) {
|
|
2070
2525
|
if (!toolResult) return null;
|
|
2071
2526
|
try {
|
|
@@ -2132,7 +2587,7 @@ function ToolCallBlock({
|
|
|
2132
2587
|
sessionStatus,
|
|
2133
2588
|
renderer
|
|
2134
2589
|
}) {
|
|
2135
|
-
const [expanded, setExpanded] =
|
|
2590
|
+
const [expanded, setExpanded] = useState9(false);
|
|
2136
2591
|
const normalizedName = formatToolName(toolCall.name);
|
|
2137
2592
|
if (renderer) {
|
|
2138
2593
|
const custom = renderer(toolCall);
|
|
@@ -2234,45 +2689,240 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2234
2689
|
// src/components/AssistantTurnBlock.tsx
|
|
2235
2690
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2236
2691
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2237
|
-
const [open, setOpen] =
|
|
2238
|
-
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking
|
|
2692
|
+
const [open, setOpen] = useState10(false);
|
|
2693
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking text-xs", children: [
|
|
2239
2694
|
/* @__PURE__ */ jsxs9(
|
|
2240
2695
|
"button",
|
|
2241
2696
|
{
|
|
2242
2697
|
type: "button",
|
|
2243
2698
|
onClick: () => setOpen(!open),
|
|
2244
2699
|
"aria-expanded": open,
|
|
2245
|
-
className: "inline-flex items-center gap-1
|
|
2700
|
+
className: "group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2246
2701
|
children: [
|
|
2247
|
-
/* @__PURE__ */ jsx11(
|
|
2248
|
-
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
|
|
2249
|
-
/* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
|
|
2250
|
-
"\xB7 ",
|
|
2251
|
-
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
2252
|
-
" \u5B57"
|
|
2253
|
-
] }),
|
|
2702
|
+
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u5DF2\u601D\u8003" }),
|
|
2254
2703
|
/* @__PURE__ */ jsx11(
|
|
2255
|
-
|
|
2704
|
+
ChevronRight,
|
|
2256
2705
|
{
|
|
2257
|
-
size:
|
|
2258
|
-
className: cn(
|
|
2706
|
+
size: 14,
|
|
2707
|
+
className: cn(
|
|
2708
|
+
"shrink-0 opacity-0 transition-[opacity,transform] group-hover/thinking:opacity-100",
|
|
2709
|
+
open && "rotate-90 opacity-100"
|
|
2710
|
+
)
|
|
2259
2711
|
}
|
|
2260
2712
|
)
|
|
2261
2713
|
]
|
|
2262
2714
|
}
|
|
2263
2715
|
),
|
|
2264
|
-
open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap
|
|
2716
|
+
open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning })
|
|
2265
2717
|
] });
|
|
2266
2718
|
}
|
|
2267
2719
|
function getMessageText(message) {
|
|
2268
2720
|
return getTextContent(normalizeMessageContent(message.content)).trim();
|
|
2269
2721
|
}
|
|
2722
|
+
function hasRenderableMessageContent(message) {
|
|
2723
|
+
return Boolean(getMessageText(message)) || getImageParts(message.content).length > 0 || getFileParts(message.content).length > 0;
|
|
2724
|
+
}
|
|
2725
|
+
function getLastContentMessage(messages) {
|
|
2726
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2727
|
+
if (hasRenderableMessageContent(messages[index])) return messages[index];
|
|
2728
|
+
}
|
|
2729
|
+
return null;
|
|
2730
|
+
}
|
|
2270
2731
|
function findLatestReasoningMessageIndex(messages) {
|
|
2271
2732
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2272
2733
|
if (messages[index].reasoning) return index;
|
|
2273
2734
|
}
|
|
2274
2735
|
return -1;
|
|
2275
2736
|
}
|
|
2737
|
+
function resolveTurnDisplayMode({
|
|
2738
|
+
isStreaming: _isStreaming,
|
|
2739
|
+
displayMode
|
|
2740
|
+
}) {
|
|
2741
|
+
return displayMode;
|
|
2742
|
+
}
|
|
2743
|
+
function formatExecutionDuration(durationMs) {
|
|
2744
|
+
const totalSeconds = Math.max(0, Math.round(durationMs / 1e3));
|
|
2745
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2746
|
+
const seconds = totalSeconds % 60;
|
|
2747
|
+
return minutes > 0 ? `${minutes}\u5206${seconds}\u79D2` : `${seconds}\u79D2`;
|
|
2748
|
+
}
|
|
2749
|
+
function getExecutionDurationMs({
|
|
2750
|
+
messages,
|
|
2751
|
+
isStreaming,
|
|
2752
|
+
now = Date.now()
|
|
2753
|
+
}) {
|
|
2754
|
+
const knownDuration = messages.reduce(
|
|
2755
|
+
(total, message) => {
|
|
2756
|
+
if (typeof message.duration_ms === "number" && message.duration_ms > 0) {
|
|
2757
|
+
return total + message.duration_ms;
|
|
2758
|
+
}
|
|
2759
|
+
return total + (message.tool_calls ?? []).reduce(
|
|
2760
|
+
(toolTotal, toolCall) => toolTotal + (typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 ? toolCall.duration_ms : 0),
|
|
2761
|
+
0
|
|
2762
|
+
);
|
|
2763
|
+
},
|
|
2764
|
+
0
|
|
2765
|
+
);
|
|
2766
|
+
if (!isStreaming) return knownDuration;
|
|
2767
|
+
const startedAt = messages.map((message) => message.timestamp ? Date.parse(message.timestamp) : Number.NaN).filter((value) => Number.isFinite(value)).sort((a, b) => a - b)[0];
|
|
2768
|
+
if (startedAt === void 0) return knownDuration;
|
|
2769
|
+
return Math.max(knownDuration, now - startedAt);
|
|
2770
|
+
}
|
|
2771
|
+
function findLastExceptionalEvent(messages) {
|
|
2772
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
2773
|
+
const messageStatus = messages[messageIndex].status;
|
|
2774
|
+
if (messageStatus === "failed") return { messageIndex, status: "error" };
|
|
2775
|
+
if (messageStatus === "interrupted") return { messageIndex, status: "cancelled" };
|
|
2776
|
+
const toolCalls = messages[messageIndex].tool_calls ?? [];
|
|
2777
|
+
for (let toolIndex = toolCalls.length - 1; toolIndex >= 0; toolIndex -= 1) {
|
|
2778
|
+
const status = toolCalls[toolIndex].status;
|
|
2779
|
+
if (status === "error" || status === "cancelled") {
|
|
2780
|
+
return { messageIndex, status };
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
return null;
|
|
2785
|
+
}
|
|
2786
|
+
function executionSummaryLabel({
|
|
2787
|
+
messages,
|
|
2788
|
+
isStreaming,
|
|
2789
|
+
durationMs,
|
|
2790
|
+
sessionStatus,
|
|
2791
|
+
askAnswers
|
|
2792
|
+
}) {
|
|
2793
|
+
if (isStreaming) {
|
|
2794
|
+
return durationMs > 0 ? `\u6B63\u5728\u6267\u884C ${formatExecutionDuration(durationMs)}` : "\u6B63\u5728\u6267\u884C";
|
|
2795
|
+
}
|
|
2796
|
+
if (sessionStatus === "waiting_for_input" && messages.some(
|
|
2797
|
+
(message) => (message.tool_calls ?? []).some(
|
|
2798
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion" && toolCall.status === "awaiting_answer" && !askAnswers?.[toolCall.id]
|
|
2799
|
+
)
|
|
2800
|
+
)) {
|
|
2801
|
+
return "\u7B49\u5F85\u8F93\u5165";
|
|
2802
|
+
}
|
|
2803
|
+
const completedLabel = durationMs > 0 ? `\u6267\u884C\u5B8C\u6210 ${formatExecutionDuration(durationMs)}` : "\u6267\u884C\u5B8C\u6210";
|
|
2804
|
+
const lastExceptionalEvent = findLastExceptionalEvent(messages);
|
|
2805
|
+
if (lastExceptionalEvent) {
|
|
2806
|
+
const recovered = messages.slice(lastExceptionalEvent.messageIndex + 1).some(hasRenderableMessageContent);
|
|
2807
|
+
if (lastExceptionalEvent.status === "error") {
|
|
2808
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u5931\u8D25` : "\u6267\u884C\u5931\u8D25";
|
|
2809
|
+
}
|
|
2810
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u672A\u5B8C\u6210` : "\u6267\u884C\u5DF2\u4E2D\u65AD";
|
|
2811
|
+
}
|
|
2812
|
+
return completedLabel;
|
|
2813
|
+
}
|
|
2814
|
+
function executionToolTypeLabel(toolCall) {
|
|
2815
|
+
switch (formatToolName(toolCall.name)) {
|
|
2816
|
+
case "WebSearch":
|
|
2817
|
+
case "WebFetch":
|
|
2818
|
+
return "\u7F51\u7EDC\u68C0\u7D22";
|
|
2819
|
+
case "Bash":
|
|
2820
|
+
case "BgBash":
|
|
2821
|
+
return "\u547D\u4EE4\u6267\u884C";
|
|
2822
|
+
case "Read":
|
|
2823
|
+
case "ReadSkill":
|
|
2824
|
+
return "\u5185\u5BB9\u8BFB\u53D6";
|
|
2825
|
+
case "Write":
|
|
2826
|
+
case "Edit":
|
|
2827
|
+
case "MultiEdit":
|
|
2828
|
+
return "\u6587\u4EF6\u5904\u7406";
|
|
2829
|
+
case "Grep":
|
|
2830
|
+
case "Glob":
|
|
2831
|
+
return "\u5185\u5BB9\u641C\u7D22";
|
|
2832
|
+
case "Agent":
|
|
2833
|
+
return "\u5B50\u4EFB\u52A1";
|
|
2834
|
+
case "search_skills":
|
|
2835
|
+
return "\u6280\u80FD\u68C0\u7D22";
|
|
2836
|
+
case "get_skill_content":
|
|
2837
|
+
return "\u8BFB\u53D6\u6280\u80FD";
|
|
2838
|
+
case "run_skill_tool":
|
|
2839
|
+
return "\u6267\u884C\u6280\u80FD";
|
|
2840
|
+
default:
|
|
2841
|
+
return toolCall.display_name?.trim() || "\u6267\u884C\u6B65\u9AA4";
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
function executionToolIntent(toolCall) {
|
|
2845
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2846
|
+
let args = null;
|
|
2847
|
+
try {
|
|
2848
|
+
const parsed = JSON.parse(toolCall.arguments);
|
|
2849
|
+
args = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2850
|
+
} catch {
|
|
2851
|
+
args = null;
|
|
2852
|
+
}
|
|
2853
|
+
const getString = (key) => {
|
|
2854
|
+
const value = args?.[key];
|
|
2855
|
+
return typeof value === "string" ? value.trim() : "";
|
|
2856
|
+
};
|
|
2857
|
+
const explicitIntent = getString("description") || getString("_meta_display_name") || getString("display_name") || toolCall.display_name?.trim() || "";
|
|
2858
|
+
if (explicitIntent) return explicitIntent;
|
|
2859
|
+
if (normalizedName === "search_skills") return getString("query");
|
|
2860
|
+
if (normalizedName === "get_skill_content" || normalizedName === "ReadSkill") {
|
|
2861
|
+
return getString("skill_name") || getString("skill");
|
|
2862
|
+
}
|
|
2863
|
+
if (normalizedName === "FinishTask") return getString("title");
|
|
2864
|
+
return "";
|
|
2865
|
+
}
|
|
2866
|
+
function ExecutionToolRow({ toolCall }) {
|
|
2867
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2868
|
+
const typeLabel = executionToolTypeLabel(toolCall);
|
|
2869
|
+
const intent = executionToolIntent(toolCall);
|
|
2870
|
+
const label = intent ? `${typeLabel}\uFF1A${intent}` : typeLabel;
|
|
2871
|
+
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
2872
|
+
const filePath = toolCall.status === "done" && (normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit") ? extractToolFilePath(toolCall) : null;
|
|
2873
|
+
const [fileOpen, setFileOpen] = useState10(false);
|
|
2874
|
+
const iconClass = cn(
|
|
2875
|
+
"size-3.5 shrink-0",
|
|
2876
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2877
|
+
);
|
|
2878
|
+
const icon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx11(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx11(X, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "WebSearch" || normalizedName === "WebFetch" ? /* @__PURE__ */ jsx11(Earth, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Bash" || normalizedName === "BgBash" ? /* @__PURE__ */ jsx11(Terminal, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit" ? /* @__PURE__ */ jsx11(FilePenLine, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Read" || normalizedName === "ReadSkill" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Grep" || normalizedName === "Glob" || normalizedName === "search_skills" ? /* @__PURE__ */ jsx11(Search, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "get_skill_content" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Agent" ? /* @__PURE__ */ jsx11(Bot, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx11(Wrench, { className: iconClass, "aria-hidden": "true" });
|
|
2879
|
+
const rowClassName = cn(
|
|
2880
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]",
|
|
2881
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2882
|
+
);
|
|
2883
|
+
if (!filePath) {
|
|
2884
|
+
return /* @__PURE__ */ jsxs9("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
|
|
2885
|
+
icon,
|
|
2886
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label })
|
|
2887
|
+
] });
|
|
2888
|
+
}
|
|
2889
|
+
return /* @__PURE__ */ jsxs9("div", { className: "min-w-0", children: [
|
|
2890
|
+
/* @__PURE__ */ jsxs9(
|
|
2891
|
+
"button",
|
|
2892
|
+
{
|
|
2893
|
+
type: "button",
|
|
2894
|
+
"data-testid": "execution-tool-intent",
|
|
2895
|
+
className: cn(rowClassName, "w-full text-left"),
|
|
2896
|
+
title: label,
|
|
2897
|
+
"aria-expanded": fileOpen,
|
|
2898
|
+
onClick: () => setFileOpen((open) => !open),
|
|
2899
|
+
children: [
|
|
2900
|
+
icon,
|
|
2901
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label }),
|
|
2902
|
+
/* @__PURE__ */ jsx11(
|
|
2903
|
+
ChevronRight,
|
|
2904
|
+
{
|
|
2905
|
+
size: 14,
|
|
2906
|
+
className: cn("shrink-0 transition-transform", fileOpen && "rotate-90"),
|
|
2907
|
+
"aria-hidden": "true"
|
|
2908
|
+
}
|
|
2909
|
+
)
|
|
2910
|
+
]
|
|
2911
|
+
}
|
|
2912
|
+
),
|
|
2913
|
+
fileOpen ? /* @__PURE__ */ jsxs9(
|
|
2914
|
+
"div",
|
|
2915
|
+
{
|
|
2916
|
+
className: "ml-[18px] truncate text-xs leading-[22px] text-[hsl(var(--muted-foreground))]",
|
|
2917
|
+
title: filePath,
|
|
2918
|
+
children: [
|
|
2919
|
+
"\u6587\u4EF6\uFF1A",
|
|
2920
|
+
filePath
|
|
2921
|
+
]
|
|
2922
|
+
}
|
|
2923
|
+
) : null
|
|
2924
|
+
] });
|
|
2925
|
+
}
|
|
2276
2926
|
function AssistantTurnBlock({
|
|
2277
2927
|
messages,
|
|
2278
2928
|
isStreaming = false,
|
|
@@ -2283,59 +2933,281 @@ function AssistantTurnBlock({
|
|
|
2283
2933
|
sessionId
|
|
2284
2934
|
}) {
|
|
2285
2935
|
const hasInterrupted = messages.some((message) => message.status === "interrupted");
|
|
2286
|
-
const
|
|
2287
|
-
(message) =>
|
|
2936
|
+
const hasFailedWithoutContent = messages.some(
|
|
2937
|
+
(message) => message.status === "failed" && !hasRenderableMessageContent(message)
|
|
2938
|
+
);
|
|
2939
|
+
const finalMessage = getLastContentMessage(messages);
|
|
2940
|
+
const hasExecutionProcess = messages.some(
|
|
2941
|
+
(message) => message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
2288
2942
|
);
|
|
2289
2943
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
2944
|
+
const hasActionableToolCall = messages.some(
|
|
2945
|
+
(message) => message.status === "failed" || message.status === "interrupted" || (message.tool_calls ?? []).some(
|
|
2946
|
+
(toolCall) => toolCall.status === "error" || toolCall.status === "cancelled"
|
|
2947
|
+
)
|
|
2948
|
+
);
|
|
2949
|
+
const questionToolCalls = messages.flatMap(
|
|
2950
|
+
(message) => (message.tool_calls ?? []).filter(
|
|
2951
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion"
|
|
2952
|
+
)
|
|
2953
|
+
);
|
|
2954
|
+
const [displayMode, setDisplayMode] = useState10(
|
|
2955
|
+
() => isStreaming || hasActionableToolCall ? "detail" : "compact"
|
|
2956
|
+
);
|
|
2957
|
+
const userSelectedDisplayModeRef = useRef9(false);
|
|
2958
|
+
const wasStreamingRef = useRef9(isStreaming);
|
|
2959
|
+
useEffect8(() => {
|
|
2960
|
+
if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
|
|
2961
|
+
setDisplayMode(hasActionableToolCall ? "detail" : "compact");
|
|
2962
|
+
}
|
|
2963
|
+
wasStreamingRef.current = isStreaming;
|
|
2964
|
+
}, [hasActionableToolCall, isStreaming]);
|
|
2965
|
+
const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
|
|
2966
|
+
const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
|
|
2967
|
+
const [clock, setClock] = useState10(() => Date.now());
|
|
2968
|
+
const hasLiveStartTime = messages.some(
|
|
2969
|
+
(message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
|
|
2970
|
+
);
|
|
2971
|
+
useEffect8(() => {
|
|
2972
|
+
if (!isStreaming || !hasLiveStartTime) return;
|
|
2973
|
+
const timer = window.setInterval(() => setClock(Date.now()), 1e3);
|
|
2974
|
+
return () => window.clearInterval(timer);
|
|
2975
|
+
}, [hasLiveStartTime, isStreaming]);
|
|
2976
|
+
const liveExecutionDurationMs = isStreaming ? getExecutionDurationMs({ messages, isStreaming, now: clock }) : executionDurationMs;
|
|
2977
|
+
if (!hasExecutionProcess) {
|
|
2978
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2979
|
+
hasInterrupted && /* @__PURE__ */ jsx11("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" }),
|
|
2980
|
+
hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
|
|
2981
|
+
messages.map((message, index) => {
|
|
2982
|
+
return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx11(
|
|
2983
|
+
"div",
|
|
2984
|
+
{
|
|
2985
|
+
className: "flex flex-col gap-3",
|
|
2986
|
+
children: /* @__PURE__ */ jsx11(
|
|
2987
|
+
AssistantMessageContent,
|
|
2988
|
+
{
|
|
2989
|
+
message,
|
|
2990
|
+
sessionId,
|
|
2991
|
+
streaming: isStreaming && index === messages.length - 1
|
|
2992
|
+
}
|
|
2993
|
+
)
|
|
2994
|
+
},
|
|
2995
|
+
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2996
|
+
) : null;
|
|
2997
|
+
}),
|
|
2998
|
+
isStreaming && !finalMessage ? /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." }) : null
|
|
2999
|
+
] });
|
|
3000
|
+
}
|
|
2290
3001
|
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2291
3002
|
hasInterrupted && /* @__PURE__ */ jsx11("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" }),
|
|
2292
|
-
|
|
3003
|
+
hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
|
|
3004
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex w-full items-start gap-2.5", children: [
|
|
3005
|
+
/* @__PURE__ */ jsx11(
|
|
3006
|
+
"span",
|
|
3007
|
+
{
|
|
3008
|
+
className: "grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]",
|
|
3009
|
+
"aria-hidden": "true",
|
|
3010
|
+
children: /* @__PURE__ */ jsx11(Bot, { size: 16 })
|
|
3011
|
+
}
|
|
3012
|
+
),
|
|
3013
|
+
/* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
3014
|
+
/* @__PURE__ */ jsxs9(
|
|
3015
|
+
"button",
|
|
3016
|
+
{
|
|
3017
|
+
type: "button",
|
|
3018
|
+
onClick: () => {
|
|
3019
|
+
userSelectedDisplayModeRef.current = true;
|
|
3020
|
+
setDisplayMode(displayMode === "detail" ? "compact" : "detail");
|
|
3021
|
+
},
|
|
3022
|
+
"aria-expanded": effectiveMode === "detail",
|
|
3023
|
+
"aria-label": effectiveMode === "detail" ? "\u6536\u8D77\u6267\u884C\u8FC7\u7A0B" : "\u5C55\u5F00\u6267\u884C\u8FC7\u7A0B",
|
|
3024
|
+
"data-testid": "assistant-execution-summary",
|
|
3025
|
+
className: "inline-flex min-w-0 max-w-full select-none items-center gap-1 bg-transparent p-0 text-left text-xs leading-[22px] text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
|
|
3026
|
+
children: [
|
|
3027
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
|
|
3028
|
+
messages,
|
|
3029
|
+
isStreaming,
|
|
3030
|
+
durationMs: liveExecutionDurationMs,
|
|
3031
|
+
sessionStatus,
|
|
3032
|
+
askAnswers
|
|
3033
|
+
}) }),
|
|
3034
|
+
/* @__PURE__ */ jsx11(
|
|
3035
|
+
ChevronRight,
|
|
3036
|
+
{
|
|
3037
|
+
size: 14,
|
|
3038
|
+
className: cn(
|
|
3039
|
+
"shrink-0 transition-transform duration-300",
|
|
3040
|
+
effectiveMode === "detail" && "rotate-90"
|
|
3041
|
+
),
|
|
3042
|
+
"aria-hidden": "true"
|
|
3043
|
+
}
|
|
3044
|
+
)
|
|
3045
|
+
]
|
|
3046
|
+
}
|
|
3047
|
+
),
|
|
3048
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
|
|
3049
|
+
] })
|
|
3050
|
+
] }),
|
|
3051
|
+
effectiveMode === "detail" ? /* @__PURE__ */ jsx11("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
|
|
2293
3052
|
const isLast = index === messages.length - 1;
|
|
2294
3053
|
const streamingThis = isStreaming && isLast;
|
|
2295
3054
|
const text = getMessageText(message);
|
|
2296
|
-
const toolCalls = message.tool_calls ?? []
|
|
3055
|
+
const toolCalls = (message.tool_calls ?? []).filter(
|
|
3056
|
+
(toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
|
|
3057
|
+
);
|
|
2297
3058
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
2298
3059
|
return /* @__PURE__ */ jsxs9(
|
|
2299
3060
|
"div",
|
|
2300
3061
|
{
|
|
2301
3062
|
className: "flex flex-col gap-3",
|
|
2302
3063
|
children: [
|
|
2303
|
-
showReasoning && message.reasoning
|
|
2304
|
-
|
|
2305
|
-
|
|
3064
|
+
showReasoning && message.reasoning ? /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
|
|
3065
|
+
hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx11(
|
|
3066
|
+
AssistantMessageContent,
|
|
2306
3067
|
{
|
|
2307
|
-
|
|
2308
|
-
className: "blade-chat-prose",
|
|
3068
|
+
message,
|
|
2309
3069
|
sessionId,
|
|
2310
|
-
|
|
3070
|
+
streaming: streamingThis,
|
|
3071
|
+
compact: true
|
|
2311
3072
|
}
|
|
2312
|
-
)
|
|
2313
|
-
toolCalls.length > 0
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
toolCall,
|
|
2318
|
-
answerData: askAnswers?.[toolCall.id],
|
|
2319
|
-
onAnswer,
|
|
2320
|
-
answered: sessionStatus !== "waiting_for_input",
|
|
2321
|
-
sessionStatus,
|
|
2322
|
-
renderer: toolCallRenderer
|
|
2323
|
-
},
|
|
2324
|
-
toolCall.id
|
|
2325
|
-
)
|
|
2326
|
-
) })
|
|
3073
|
+
) : null,
|
|
3074
|
+
toolCalls.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
|
|
3075
|
+
const custom = toolCallRenderer?.(toolCall);
|
|
3076
|
+
return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx11("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(ExecutionToolRow, { toolCall }, toolCall.id);
|
|
3077
|
+
}) }) : null
|
|
2327
3078
|
]
|
|
2328
3079
|
},
|
|
2329
3080
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2330
3081
|
);
|
|
2331
|
-
}),
|
|
2332
|
-
|
|
3082
|
+
}) }) : null,
|
|
3083
|
+
finalMessage ? /* @__PURE__ */ jsx11("div", { className: "ml-10", children: /* @__PURE__ */ jsx11(
|
|
3084
|
+
AssistantMessageContent,
|
|
3085
|
+
{
|
|
3086
|
+
message: finalMessage,
|
|
3087
|
+
sessionId,
|
|
3088
|
+
streaming: isStreaming && finalMessage === messages[messages.length - 1]
|
|
3089
|
+
}
|
|
3090
|
+
) }) : null,
|
|
3091
|
+
questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx11(
|
|
3092
|
+
ToolCallBlock,
|
|
3093
|
+
{
|
|
3094
|
+
toolCall,
|
|
3095
|
+
answerData: askAnswers?.[toolCall.id],
|
|
3096
|
+
onAnswer,
|
|
3097
|
+
answered: sessionStatus !== "waiting_for_input",
|
|
3098
|
+
sessionStatus,
|
|
3099
|
+
renderer: toolCallRenderer
|
|
3100
|
+
},
|
|
3101
|
+
toolCall.id
|
|
3102
|
+
))
|
|
2333
3103
|
] });
|
|
2334
3104
|
}
|
|
3105
|
+
function AssistantMessageContent({
|
|
3106
|
+
message,
|
|
3107
|
+
sessionId,
|
|
3108
|
+
streaming,
|
|
3109
|
+
compact = false
|
|
3110
|
+
}) {
|
|
3111
|
+
const text = getMessageText(message);
|
|
3112
|
+
const imageParts = getImageParts(message.content);
|
|
3113
|
+
const fileParts = getFileParts(message.content);
|
|
3114
|
+
const failed = message.status === "failed";
|
|
3115
|
+
const failedBadge = failed ? /* @__PURE__ */ jsx11("div", { className: "w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }) : null;
|
|
3116
|
+
const textContent = text ? /* @__PURE__ */ jsx11(
|
|
3117
|
+
"div",
|
|
3118
|
+
{
|
|
3119
|
+
className: cn(
|
|
3120
|
+
"blade-chat-assistant-text",
|
|
3121
|
+
compact ? "text-xs leading-[22px] text-[hsl(var(--foreground))]" : "text-[15px] leading-8 text-[hsl(var(--foreground))]"
|
|
3122
|
+
),
|
|
3123
|
+
children: /* @__PURE__ */ jsx11(
|
|
3124
|
+
MarkdownContent,
|
|
3125
|
+
{
|
|
3126
|
+
mode: streaming ? "streaming" : "static",
|
|
3127
|
+
className: "blade-chat-prose",
|
|
3128
|
+
sessionId,
|
|
3129
|
+
children: text
|
|
3130
|
+
}
|
|
3131
|
+
)
|
|
3132
|
+
}
|
|
3133
|
+
) : null;
|
|
3134
|
+
if (imageParts.length === 0 && fileParts.length === 0) {
|
|
3135
|
+
if (!failed) return textContent;
|
|
3136
|
+
return failedBadge || textContent ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
|
|
3137
|
+
failedBadge,
|
|
3138
|
+
textContent
|
|
3139
|
+
] }) : null;
|
|
3140
|
+
}
|
|
3141
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3", children: [
|
|
3142
|
+
failedBadge,
|
|
3143
|
+
imageParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx11(
|
|
3144
|
+
"img",
|
|
3145
|
+
{
|
|
3146
|
+
src: part.image_url.url,
|
|
3147
|
+
alt: "\u6D88\u606F\u9644\u4EF6",
|
|
3148
|
+
className: "max-h-72 rounded-xl border border-[hsl(var(--border))] object-cover"
|
|
3149
|
+
},
|
|
3150
|
+
part.image_url.url
|
|
3151
|
+
)) }) : null,
|
|
3152
|
+
fileParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs9(
|
|
3153
|
+
"div",
|
|
3154
|
+
{
|
|
3155
|
+
className: "flex min-w-0 items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3156
|
+
title: part.name,
|
|
3157
|
+
children: [
|
|
3158
|
+
/* @__PURE__ */ jsx11(FileText, { size: 12, className: "shrink-0" }),
|
|
3159
|
+
/* @__PURE__ */ jsx11("span", { className: "max-w-56 truncate", children: part.name })
|
|
3160
|
+
]
|
|
3161
|
+
},
|
|
3162
|
+
`${part.name}-${part.data.slice(0, 32)}`
|
|
3163
|
+
)) }) : null,
|
|
3164
|
+
textContent
|
|
3165
|
+
] });
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// src/components/ContextCard.tsx
|
|
3169
|
+
import {
|
|
3170
|
+
getContextDisplayState
|
|
3171
|
+
} from "@blade-hq/agent-client";
|
|
3172
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3173
|
+
function ContextCard({ context, className }) {
|
|
3174
|
+
const display = getContextDisplayState(context);
|
|
3175
|
+
return /* @__PURE__ */ jsxs10(
|
|
3176
|
+
"details",
|
|
3177
|
+
{
|
|
3178
|
+
className: `blade-chat-context-card group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`,
|
|
3179
|
+
children: [
|
|
3180
|
+
/* @__PURE__ */ jsxs10("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
|
|
3181
|
+
/* @__PURE__ */ jsx12(
|
|
3182
|
+
Layers,
|
|
3183
|
+
{
|
|
3184
|
+
size: 15,
|
|
3185
|
+
className: "blade-chat-context-icon shrink-0 text-[hsl(var(--muted-foreground))]",
|
|
3186
|
+
"aria-hidden": "true"
|
|
3187
|
+
}
|
|
3188
|
+
),
|
|
3189
|
+
/* @__PURE__ */ jsxs10("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
|
|
3190
|
+
/* @__PURE__ */ jsx12("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
|
|
3191
|
+
/* @__PURE__ */ jsx12("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
|
|
3192
|
+
] }),
|
|
3193
|
+
/* @__PURE__ */ jsx12(
|
|
3194
|
+
ChevronDown,
|
|
3195
|
+
{
|
|
3196
|
+
size: 14,
|
|
3197
|
+
className: "blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open:rotate-180",
|
|
3198
|
+
"aria-hidden": "true"
|
|
3199
|
+
}
|
|
3200
|
+
)
|
|
3201
|
+
] }),
|
|
3202
|
+
/* @__PURE__ */ jsx12("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: display.detail })
|
|
3203
|
+
]
|
|
3204
|
+
}
|
|
3205
|
+
);
|
|
3206
|
+
}
|
|
2335
3207
|
|
|
2336
3208
|
// src/components/RenderErrorBoundary.tsx
|
|
2337
3209
|
import { Component } from "react";
|
|
2338
|
-
import { jsx as
|
|
3210
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2339
3211
|
function getFirstComponentName(componentStack) {
|
|
2340
3212
|
const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
|
|
2341
3213
|
return match?.[1] ?? null;
|
|
@@ -2368,26 +3240,26 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2368
3240
|
return children;
|
|
2369
3241
|
}
|
|
2370
3242
|
const componentName = getFirstComponentName(componentStack);
|
|
2371
|
-
return /* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2373
|
-
/* @__PURE__ */
|
|
2374
|
-
/* @__PURE__ */
|
|
3243
|
+
return /* @__PURE__ */ jsx13("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs11("div", { className: "flex items-start gap-2", children: [
|
|
3244
|
+
/* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
|
|
3245
|
+
/* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
|
|
3246
|
+
/* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
|
|
2375
3247
|
label,
|
|
2376
3248
|
"\u6E32\u67D3\u5931\u8D25"
|
|
2377
3249
|
] }),
|
|
2378
|
-
/* @__PURE__ */
|
|
3250
|
+
/* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
|
|
2379
3251
|
componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
|
|
2380
3252
|
error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
|
|
2381
3253
|
] }),
|
|
2382
|
-
details ? /* @__PURE__ */
|
|
3254
|
+
details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
|
|
2383
3255
|
] })
|
|
2384
3256
|
] }) });
|
|
2385
3257
|
}
|
|
2386
3258
|
};
|
|
2387
3259
|
|
|
2388
3260
|
// src/components/PostChatFollowupBlock.tsx
|
|
2389
|
-
import { useCallback as
|
|
2390
|
-
import { Fragment as Fragment2, jsx as
|
|
3261
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
|
|
3262
|
+
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2391
3263
|
function emitInteraction(callback, event) {
|
|
2392
3264
|
try {
|
|
2393
3265
|
callback?.(event);
|
|
@@ -2397,9 +3269,6 @@ function emitInteraction(callback, event) {
|
|
|
2397
3269
|
function basename(path) {
|
|
2398
3270
|
return path.split(/[\\/]/).filter(Boolean).pop() || path;
|
|
2399
3271
|
}
|
|
2400
|
-
function isVideo(path) {
|
|
2401
|
-
return /\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path);
|
|
2402
|
-
}
|
|
2403
3272
|
function ArtifactCard({
|
|
2404
3273
|
artifact,
|
|
2405
3274
|
sessionId,
|
|
@@ -2409,10 +3278,10 @@ function ArtifactCard({
|
|
|
2409
3278
|
onArtifactOpened
|
|
2410
3279
|
}) {
|
|
2411
3280
|
const client = useBladeClient();
|
|
2412
|
-
const [downloading, setDownloading] =
|
|
3281
|
+
const [downloading, setDownloading] = useState11(false);
|
|
2413
3282
|
const name = artifact.label || basename(artifact.target);
|
|
2414
3283
|
if (artifact.kind === "link") {
|
|
2415
|
-
return /* @__PURE__ */
|
|
3284
|
+
return /* @__PURE__ */ jsxs12(
|
|
2416
3285
|
"a",
|
|
2417
3286
|
{
|
|
2418
3287
|
href: artifact.target,
|
|
@@ -2423,51 +3292,55 @@ function ArtifactCard({
|
|
|
2423
3292
|
${artifact.target}`,
|
|
2424
3293
|
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))]",
|
|
2425
3294
|
children: [
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2428
|
-
/* @__PURE__ */
|
|
3295
|
+
/* @__PURE__ */ jsx14(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
3296
|
+
/* @__PURE__ */ jsx14("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
3297
|
+
/* @__PURE__ */ jsx14(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2429
3298
|
]
|
|
2430
3299
|
}
|
|
2431
3300
|
);
|
|
2432
3301
|
}
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2435
|
-
|
|
3302
|
+
const fileName = basename(artifact.target);
|
|
3303
|
+
const downloadUrl = sessionId ? client.buildAuthedUrl(
|
|
3304
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`
|
|
3305
|
+
) : void 0;
|
|
3306
|
+
const handleDownload = async (event) => {
|
|
3307
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
3308
|
+
event.preventDefault();
|
|
3309
|
+
if (!sessionId || downloading) return;
|
|
3310
|
+
setDownloading(true);
|
|
3311
|
+
emitInteraction(onInteraction, {
|
|
3312
|
+
type: "artifact_download_started",
|
|
3313
|
+
sessionId,
|
|
3314
|
+
assistantEntryId,
|
|
3315
|
+
artifactIndex,
|
|
3316
|
+
artifactKind: "file"
|
|
3317
|
+
});
|
|
3318
|
+
try {
|
|
3319
|
+
await client.sessions.downloadFile(sessionId, artifact.target, fileName);
|
|
3320
|
+
emitInteraction(onInteraction, {
|
|
3321
|
+
type: "artifact_download_succeeded",
|
|
3322
|
+
sessionId,
|
|
3323
|
+
assistantEntryId,
|
|
3324
|
+
artifactIndex,
|
|
3325
|
+
artifactKind: "file"
|
|
3326
|
+
});
|
|
3327
|
+
} catch {
|
|
3328
|
+
} finally {
|
|
3329
|
+
setDownloading(false);
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
return /* @__PURE__ */ jsx14(
|
|
3333
|
+
"a",
|
|
2436
3334
|
{
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
onClick:
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
artifactIndex,
|
|
2447
|
-
artifactKind: "file"
|
|
2448
|
-
});
|
|
2449
|
-
try {
|
|
2450
|
-
await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target));
|
|
2451
|
-
emitInteraction(onInteraction, {
|
|
2452
|
-
type: "artifact_download_succeeded",
|
|
2453
|
-
sessionId,
|
|
2454
|
-
assistantEntryId,
|
|
2455
|
-
artifactIndex,
|
|
2456
|
-
artifactKind: "file"
|
|
2457
|
-
});
|
|
2458
|
-
} catch {
|
|
2459
|
-
} finally {
|
|
2460
|
-
setDownloading(false);
|
|
2461
|
-
}
|
|
2462
|
-
},
|
|
2463
|
-
title: name,
|
|
2464
|
-
"aria-label": `\u4E0B\u8F7D ${name}`,
|
|
2465
|
-
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-left text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))] disabled:opacity-60",
|
|
2466
|
-
children: [
|
|
2467
|
-
/* @__PURE__ */ jsx13(Icon2, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
2468
|
-
/* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
2469
|
-
/* @__PURE__ */ jsx13(Download, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2470
|
-
]
|
|
3335
|
+
href: downloadUrl,
|
|
3336
|
+
download: fileName,
|
|
3337
|
+
onClick: handleDownload,
|
|
3338
|
+
title: fileName,
|
|
3339
|
+
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${fileName}`,
|
|
3340
|
+
"aria-disabled": !sessionId || void 0,
|
|
3341
|
+
"aria-busy": downloading || void 0,
|
|
3342
|
+
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",
|
|
3343
|
+
children: fileName
|
|
2471
3344
|
}
|
|
2472
3345
|
);
|
|
2473
3346
|
}
|
|
@@ -2484,17 +3357,17 @@ function feedbackReasonLabel(reason) {
|
|
|
2484
3357
|
}
|
|
2485
3358
|
function HistoricalResultFeedback({ feedback }) {
|
|
2486
3359
|
const label = feedbackReasonLabel(feedback.reason);
|
|
2487
|
-
return /* @__PURE__ */
|
|
3360
|
+
return /* @__PURE__ */ jsxs12(
|
|
2488
3361
|
"section",
|
|
2489
3362
|
{
|
|
2490
3363
|
"aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
|
|
2491
3364
|
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))]",
|
|
2492
3365
|
children: [
|
|
2493
|
-
/* @__PURE__ */
|
|
3366
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
2494
3367
|
"\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
|
|
2495
3368
|
feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
|
|
2496
3369
|
] }),
|
|
2497
|
-
label ? /* @__PURE__ */
|
|
3370
|
+
label ? /* @__PURE__ */ jsxs12("span", { children: [
|
|
2498
3371
|
" \xB7 ",
|
|
2499
3372
|
label
|
|
2500
3373
|
] }) : null
|
|
@@ -2511,15 +3384,15 @@ function ResultFeedback({
|
|
|
2511
3384
|
onFeedbackSaved
|
|
2512
3385
|
}) {
|
|
2513
3386
|
const client = useBladeClient();
|
|
2514
|
-
const [saved, setSaved] =
|
|
2515
|
-
const [helpful, setHelpful] =
|
|
2516
|
-
const [reason, setReason] =
|
|
2517
|
-
const [saving, setSaving] =
|
|
2518
|
-
const [saveError, setSaveError] =
|
|
2519
|
-
const reportedShown =
|
|
2520
|
-
const latestChoice =
|
|
3387
|
+
const [saved, setSaved] = useState11(savedFeedback ?? null);
|
|
3388
|
+
const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
|
|
3389
|
+
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
3390
|
+
const [saving, setSaving] = useState11(false);
|
|
3391
|
+
const [saveError, setSaveError] = useState11(false);
|
|
3392
|
+
const reportedShown = useRef10(false);
|
|
3393
|
+
const latestChoice = useRef10(null);
|
|
2521
3394
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2522
|
-
|
|
3395
|
+
useEffect9(() => {
|
|
2523
3396
|
if (!eligible || reportedShown.current) return;
|
|
2524
3397
|
reportedShown.current = true;
|
|
2525
3398
|
emitInteraction(onInteraction, {
|
|
@@ -2528,13 +3401,13 @@ function ResultFeedback({
|
|
|
2528
3401
|
assistantEntryId: followup.assistant_entry_id
|
|
2529
3402
|
});
|
|
2530
3403
|
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2531
|
-
|
|
3404
|
+
useEffect9(() => {
|
|
2532
3405
|
if (!savedFeedback || latestChoice.current) return;
|
|
2533
3406
|
setSaved(savedFeedback);
|
|
2534
3407
|
setHelpful(savedFeedback.helpful);
|
|
2535
3408
|
setReason(savedFeedback.reason);
|
|
2536
3409
|
}, [savedFeedback]);
|
|
2537
|
-
const submit =
|
|
3410
|
+
const submit = useCallback6(
|
|
2538
3411
|
async (nextHelpful, nextReason) => {
|
|
2539
3412
|
if (!sessionId) return;
|
|
2540
3413
|
const choice = { helpful: nextHelpful, reason: nextReason };
|
|
@@ -2569,15 +3442,15 @@ function ResultFeedback({
|
|
|
2569
3442
|
[client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
|
|
2570
3443
|
);
|
|
2571
3444
|
if (!eligible) return null;
|
|
2572
|
-
return /* @__PURE__ */
|
|
3445
|
+
return /* @__PURE__ */ jsxs12(
|
|
2573
3446
|
"section",
|
|
2574
3447
|
{
|
|
2575
3448
|
"aria-label": "\u7ED3\u679C\u53CD\u9988",
|
|
2576
3449
|
className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
|
|
2577
3450
|
children: [
|
|
2578
|
-
/* @__PURE__ */
|
|
2579
|
-
/* @__PURE__ */
|
|
2580
|
-
/* @__PURE__ */
|
|
3451
|
+
/* @__PURE__ */ jsx14("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
|
|
3452
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
3453
|
+
/* @__PURE__ */ jsx14(
|
|
2581
3454
|
"button",
|
|
2582
3455
|
{
|
|
2583
3456
|
type: "button",
|
|
@@ -2588,7 +3461,7 @@ function ResultFeedback({
|
|
|
2588
3461
|
children: "\u6709\u5E2E\u52A9"
|
|
2589
3462
|
}
|
|
2590
3463
|
),
|
|
2591
|
-
/* @__PURE__ */
|
|
3464
|
+
/* @__PURE__ */ jsx14(
|
|
2592
3465
|
"button",
|
|
2593
3466
|
{
|
|
2594
3467
|
type: "button",
|
|
@@ -2600,7 +3473,7 @@ function ResultFeedback({
|
|
|
2600
3473
|
}
|
|
2601
3474
|
)
|
|
2602
3475
|
] }),
|
|
2603
|
-
helpful === false ? /* @__PURE__ */
|
|
3476
|
+
helpful === false ? /* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx14(
|
|
2604
3477
|
"button",
|
|
2605
3478
|
{
|
|
2606
3479
|
type: "button",
|
|
@@ -2612,9 +3485,9 @@ function ResultFeedback({
|
|
|
2612
3485
|
},
|
|
2613
3486
|
item.value
|
|
2614
3487
|
)) }) : null,
|
|
2615
|
-
saveError ? /* @__PURE__ */
|
|
2616
|
-
/* @__PURE__ */
|
|
2617
|
-
/* @__PURE__ */
|
|
3488
|
+
saveError ? /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
|
|
3489
|
+
/* @__PURE__ */ jsx14("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
|
|
3490
|
+
/* @__PURE__ */ jsx14(
|
|
2618
3491
|
"button",
|
|
2619
3492
|
{
|
|
2620
3493
|
type: "button",
|
|
@@ -2626,7 +3499,7 @@ function ResultFeedback({
|
|
|
2626
3499
|
children: "\u91CD\u8BD5"
|
|
2627
3500
|
}
|
|
2628
3501
|
)
|
|
2629
|
-
] }) : saved ? /* @__PURE__ */
|
|
3502
|
+
] }) : saved ? /* @__PURE__ */ jsx14("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
|
|
2630
3503
|
]
|
|
2631
3504
|
}
|
|
2632
3505
|
);
|
|
@@ -2640,14 +3513,14 @@ function PostChatFollowupBlock({
|
|
|
2640
3513
|
savedFeedback,
|
|
2641
3514
|
onFeedbackSaved
|
|
2642
3515
|
}) {
|
|
2643
|
-
const [expanded, setExpanded] =
|
|
2644
|
-
const adopted =
|
|
2645
|
-
const reportedSuggestions =
|
|
2646
|
-
const reportedArtifacts =
|
|
2647
|
-
const openedArtifacts =
|
|
3516
|
+
const [expanded, setExpanded] = useState11(false);
|
|
3517
|
+
const adopted = useRef10(/* @__PURE__ */ new Set());
|
|
3518
|
+
const reportedSuggestions = useRef10(false);
|
|
3519
|
+
const reportedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
3520
|
+
const openedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
2648
3521
|
const artifacts = followup.final_artifacts ?? [];
|
|
2649
3522
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2650
|
-
|
|
3523
|
+
useEffect9(() => {
|
|
2651
3524
|
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2652
3525
|
reportedSuggestions.current = true;
|
|
2653
3526
|
emitInteraction(onInteraction, {
|
|
@@ -2677,7 +3550,7 @@ function PostChatFollowupBlock({
|
|
|
2677
3550
|
sessionId,
|
|
2678
3551
|
visibleArtifacts
|
|
2679
3552
|
]);
|
|
2680
|
-
const reportArtifactOpened =
|
|
3553
|
+
const reportArtifactOpened = useCallback6(
|
|
2681
3554
|
(artifactIndex, artifactKind) => {
|
|
2682
3555
|
if (openedArtifacts.current.has(artifactIndex)) return;
|
|
2683
3556
|
openedArtifacts.current.add(artifactIndex);
|
|
@@ -2693,15 +3566,15 @@ function PostChatFollowupBlock({
|
|
|
2693
3566
|
);
|
|
2694
3567
|
if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
|
|
2695
3568
|
return null;
|
|
2696
|
-
return /* @__PURE__ */
|
|
2697
|
-
followup.recaption || artifacts.length > 0 ? /* @__PURE__ */
|
|
2698
|
-
/* @__PURE__ */
|
|
2699
|
-
/* @__PURE__ */
|
|
3569
|
+
return /* @__PURE__ */ jsxs12("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: [
|
|
3570
|
+
followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
|
|
3571
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
|
|
3572
|
+
/* @__PURE__ */ jsx14(Sparkles, { size: 14 }),
|
|
2700
3573
|
"\u672C\u8F6E\u5C0F\u7ED3"
|
|
2701
3574
|
] }),
|
|
2702
|
-
followup.recaption ? /* @__PURE__ */
|
|
2703
|
-
artifacts.length > 0 ? /* @__PURE__ */
|
|
2704
|
-
/* @__PURE__ */
|
|
3575
|
+
followup.recaption ? /* @__PURE__ */ jsx14("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
|
|
3576
|
+
artifacts.length > 0 ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
3577
|
+
/* @__PURE__ */ jsx14("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx14(
|
|
2705
3578
|
ArtifactCard,
|
|
2706
3579
|
{
|
|
2707
3580
|
artifact,
|
|
@@ -2713,7 +3586,7 @@ function PostChatFollowupBlock({
|
|
|
2713
3586
|
},
|
|
2714
3587
|
`${artifact.kind}:${artifactIndex}`
|
|
2715
3588
|
)) }),
|
|
2716
|
-
artifacts.length > 3 ? /* @__PURE__ */
|
|
3589
|
+
artifacts.length > 3 ? /* @__PURE__ */ jsxs12(
|
|
2717
3590
|
"button",
|
|
2718
3591
|
{
|
|
2719
3592
|
type: "button",
|
|
@@ -2722,15 +3595,15 @@ function PostChatFollowupBlock({
|
|
|
2722
3595
|
className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
|
|
2723
3596
|
children: [
|
|
2724
3597
|
expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
|
|
2725
|
-
/* @__PURE__ */
|
|
3598
|
+
/* @__PURE__ */ jsx14(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
|
|
2726
3599
|
]
|
|
2727
3600
|
}
|
|
2728
3601
|
) : null
|
|
2729
3602
|
] }) : null
|
|
2730
3603
|
] }) : null,
|
|
2731
|
-
followup.suggestions.length > 0 ? /* @__PURE__ */
|
|
2732
|
-
/* @__PURE__ */
|
|
2733
|
-
followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */
|
|
3604
|
+
followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
|
|
3605
|
+
/* @__PURE__ */ jsx14("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
|
|
3606
|
+
followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs12(
|
|
2734
3607
|
"button",
|
|
2735
3608
|
{
|
|
2736
3609
|
type: "button",
|
|
@@ -2749,14 +3622,14 @@ function PostChatFollowupBlock({
|
|
|
2749
3622
|
},
|
|
2750
3623
|
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",
|
|
2751
3624
|
children: [
|
|
2752
|
-
/* @__PURE__ */
|
|
2753
|
-
/* @__PURE__ */
|
|
3625
|
+
/* @__PURE__ */ jsx14("span", { children: suggestion }),
|
|
3626
|
+
/* @__PURE__ */ jsx14(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
|
|
2754
3627
|
]
|
|
2755
3628
|
},
|
|
2756
3629
|
suggestion
|
|
2757
3630
|
))
|
|
2758
3631
|
] }) : null,
|
|
2759
|
-
/* @__PURE__ */
|
|
3632
|
+
/* @__PURE__ */ jsx14(
|
|
2760
3633
|
ResultFeedback,
|
|
2761
3634
|
{
|
|
2762
3635
|
followup,
|
|
@@ -2771,8 +3644,8 @@ function PostChatFollowupBlock({
|
|
|
2771
3644
|
}
|
|
2772
3645
|
|
|
2773
3646
|
// src/components/UserMessageBubble.tsx
|
|
2774
|
-
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
2775
|
-
import { jsx as
|
|
3647
|
+
import { getFileParts as getFileParts2, getImageParts as getImageParts2, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
3648
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2776
3649
|
function isUserMessage(message) {
|
|
2777
3650
|
return message.role === "user";
|
|
2778
3651
|
}
|
|
@@ -2782,10 +3655,10 @@ function isErrorMessage(message) {
|
|
|
2782
3655
|
var isSending = (message) => message.status === "streaming";
|
|
2783
3656
|
function UserMessageBubble({ message, className }) {
|
|
2784
3657
|
const text = getTextContent2(message.content).trim();
|
|
2785
|
-
const fileParts =
|
|
2786
|
-
const imageParts =
|
|
2787
|
-
return /* @__PURE__ */
|
|
2788
|
-
imageParts.length > 0 && /* @__PURE__ */
|
|
3658
|
+
const fileParts = getFileParts2(message.content);
|
|
3659
|
+
const imageParts = getImageParts2(message.content);
|
|
3660
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs13("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
|
|
3661
|
+
imageParts.length > 0 && /* @__PURE__ */ jsx15("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx15(
|
|
2789
3662
|
"img",
|
|
2790
3663
|
{
|
|
2791
3664
|
src: part.image_url.url,
|
|
@@ -2794,21 +3667,21 @@ function UserMessageBubble({ message, className }) {
|
|
|
2794
3667
|
},
|
|
2795
3668
|
part.image_url.url
|
|
2796
3669
|
)) }),
|
|
2797
|
-
fileParts.length > 0 && /* @__PURE__ */
|
|
3670
|
+
fileParts.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs13(
|
|
2798
3671
|
"div",
|
|
2799
3672
|
{
|
|
2800
3673
|
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))]",
|
|
2801
3674
|
children: [
|
|
2802
|
-
/* @__PURE__ */
|
|
2803
|
-
/* @__PURE__ */
|
|
3675
|
+
/* @__PURE__ */ jsx15(FileText, { size: 12, className: "shrink-0" }),
|
|
3676
|
+
/* @__PURE__ */ jsx15("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
|
|
2804
3677
|
]
|
|
2805
3678
|
},
|
|
2806
3679
|
`${part.name}-${part.data.length}`
|
|
2807
3680
|
)) }),
|
|
2808
|
-
text && /* @__PURE__ */
|
|
2809
|
-
text && isSending(message) && /* @__PURE__ */
|
|
2810
|
-
/* @__PURE__ */
|
|
2811
|
-
/* @__PURE__ */
|
|
3681
|
+
text && /* @__PURE__ */ jsx15("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__ */ jsx15(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
|
|
3682
|
+
text && isSending(message) && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
|
|
3683
|
+
/* @__PURE__ */ jsx15(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
|
|
3684
|
+
/* @__PURE__ */ jsx15("span", { children: "\u53D1\u9001\u4E2D" })
|
|
2812
3685
|
] })
|
|
2813
3686
|
] }) });
|
|
2814
3687
|
}
|
|
@@ -2817,11 +3690,11 @@ function ErrorMessageBlock({
|
|
|
2817
3690
|
className
|
|
2818
3691
|
}) {
|
|
2819
3692
|
const text = getTextContent2(message.content);
|
|
2820
|
-
return /* @__PURE__ */
|
|
3693
|
+
return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-error-row flex justify-center", className), children: /* @__PURE__ */ jsx15("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 }) });
|
|
2821
3694
|
}
|
|
2822
3695
|
|
|
2823
3696
|
// src/components/MessageList.tsx
|
|
2824
|
-
import { jsx as
|
|
3697
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2825
3698
|
function parseModeChange(message) {
|
|
2826
3699
|
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
2827
3700
|
return null;
|
|
@@ -2870,10 +3743,14 @@ function MessageList({
|
|
|
2870
3743
|
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
2871
3744
|
onResultFeedbackSaved
|
|
2872
3745
|
}) {
|
|
3746
|
+
const userMessages = messages.filter((message) => isUserMessage(message));
|
|
3747
|
+
const latestUserMessage = userMessages.at(-1);
|
|
3748
|
+
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith("local-user-"));
|
|
2873
3749
|
const renderBlocks = useMemo7(() => {
|
|
2874
3750
|
const visible = messages.filter((message) => {
|
|
2875
3751
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
2876
3752
|
if (isHiddenInternalMessage(message)) return false;
|
|
3753
|
+
if (message.kind === "context") return Boolean(message.context);
|
|
2877
3754
|
if (message.kind === "compaction") return true;
|
|
2878
3755
|
return message.role !== "tool" || getPlanningDividerKind(message) !== null;
|
|
2879
3756
|
});
|
|
@@ -2908,6 +3785,15 @@ function MessageList({
|
|
|
2908
3785
|
blocks.push({ type: "compaction", key: message.entry_id ?? `compaction-${blocks.length}` });
|
|
2909
3786
|
continue;
|
|
2910
3787
|
}
|
|
3788
|
+
if (message.kind === "context" && message.context) {
|
|
3789
|
+
flushAssistant();
|
|
3790
|
+
blocks.push({
|
|
3791
|
+
type: "context",
|
|
3792
|
+
message,
|
|
3793
|
+
key: message.entry_id ?? `context-${blocks.length}`
|
|
3794
|
+
});
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
2911
3797
|
if (message.role === "assistant") {
|
|
2912
3798
|
assistantBuffer.push(message);
|
|
2913
3799
|
continue;
|
|
@@ -2916,7 +3802,7 @@ function MessageList({
|
|
|
2916
3802
|
blocks.push({
|
|
2917
3803
|
type: "message",
|
|
2918
3804
|
message,
|
|
2919
|
-
key: message.entry_id ?? `${message.role}-${blocks.length}`
|
|
3805
|
+
key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2920
3806
|
});
|
|
2921
3807
|
}
|
|
2922
3808
|
flushAssistant();
|
|
@@ -2943,98 +3829,151 @@ function MessageList({
|
|
|
2943
3829
|
}
|
|
2944
3830
|
return blocks;
|
|
2945
3831
|
}, [messages, isStreaming]);
|
|
2946
|
-
return /* @__PURE__ */
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
postChatFollowup && block.messages.some(
|
|
2962
|
-
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
2963
|
-
)
|
|
2964
|
-
);
|
|
2965
|
-
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
|
|
2966
|
-
RenderErrorBoundary,
|
|
2967
|
-
{
|
|
2968
|
-
label: "\u52A9\u624B\u6D88\u606F",
|
|
2969
|
-
details: block.key,
|
|
2970
|
-
resetKey: getMessageResetSignature(block.messages),
|
|
2971
|
-
children: [
|
|
2972
|
-
/* @__PURE__ */ jsx15(
|
|
2973
|
-
AssistantTurnBlock,
|
|
2974
|
-
{
|
|
2975
|
-
messages: block.messages,
|
|
2976
|
-
isStreaming: block.isStreaming,
|
|
2977
|
-
askAnswers,
|
|
2978
|
-
onAnswer,
|
|
2979
|
-
sessionStatus,
|
|
2980
|
-
toolCallRenderer,
|
|
2981
|
-
sessionId
|
|
2982
|
-
}
|
|
2983
|
-
),
|
|
2984
|
-
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
2985
|
-
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
|
|
2986
|
-
PostChatFollowupBlock,
|
|
2987
|
-
{
|
|
2988
|
-
followup: postChatFollowup,
|
|
2989
|
-
sessionId,
|
|
2990
|
-
onSuggestion,
|
|
2991
|
-
isViewer,
|
|
2992
|
-
onInteraction: onFollowupInteraction,
|
|
2993
|
-
savedFeedback: blockFeedback,
|
|
2994
|
-
onFeedbackSaved: onResultFeedbackSaved
|
|
2995
|
-
}
|
|
2996
|
-
) : null
|
|
2997
|
-
]
|
|
3832
|
+
return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs14(
|
|
3833
|
+
StickToBottom,
|
|
3834
|
+
{
|
|
3835
|
+
className: "h-full overflow-y-hidden",
|
|
3836
|
+
initial: "instant",
|
|
3837
|
+
resize: "instant",
|
|
3838
|
+
children: [
|
|
3839
|
+
/* @__PURE__ */ jsx16(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx16("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col", children: [
|
|
3840
|
+
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs14("div", { className: "blade-chat-empty", children: [
|
|
3841
|
+
/* @__PURE__ */ jsx16(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
3842
|
+
/* @__PURE__ */ jsx16("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
|
|
3843
|
+
/* @__PURE__ */ jsx16("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
3844
|
+
] }) : renderBlocks.map((block) => {
|
|
3845
|
+
if (block.type === "message") {
|
|
3846
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx16(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx16(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
2998
3847
|
}
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3848
|
+
if (block.type === "assistant_turn") {
|
|
3849
|
+
const blockFeedback = block.messages.map(
|
|
3850
|
+
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
3851
|
+
).find((feedback) => feedback != null);
|
|
3852
|
+
const hasActiveFollowup = Boolean(
|
|
3853
|
+
postChatFollowup && block.messages.some(
|
|
3854
|
+
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
3855
|
+
)
|
|
3856
|
+
);
|
|
3857
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs14(
|
|
3858
|
+
RenderErrorBoundary,
|
|
3859
|
+
{
|
|
3860
|
+
label: "\u52A9\u624B\u6D88\u606F",
|
|
3861
|
+
details: block.key,
|
|
3862
|
+
resetKey: getMessageResetSignature(block.messages),
|
|
3863
|
+
children: [
|
|
3864
|
+
/* @__PURE__ */ jsx16(
|
|
3865
|
+
AssistantTurnBlock,
|
|
3866
|
+
{
|
|
3867
|
+
messages: block.messages,
|
|
3868
|
+
isStreaming: block.isStreaming,
|
|
3869
|
+
askAnswers,
|
|
3870
|
+
onAnswer,
|
|
3871
|
+
sessionStatus,
|
|
3872
|
+
toolCallRenderer,
|
|
3873
|
+
sessionId
|
|
3874
|
+
}
|
|
3875
|
+
),
|
|
3876
|
+
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx16(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3877
|
+
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx16(
|
|
3878
|
+
PostChatFollowupBlock,
|
|
3879
|
+
{
|
|
3880
|
+
followup: postChatFollowup,
|
|
3881
|
+
sessionId,
|
|
3882
|
+
onSuggestion,
|
|
3883
|
+
isViewer,
|
|
3884
|
+
onInteraction: onFollowupInteraction,
|
|
3885
|
+
savedFeedback: blockFeedback,
|
|
3886
|
+
onFeedbackSaved: onResultFeedbackSaved
|
|
3887
|
+
}
|
|
3888
|
+
) : null
|
|
3889
|
+
]
|
|
3890
|
+
}
|
|
3891
|
+
) }, block.key);
|
|
3892
|
+
}
|
|
3893
|
+
if (block.type === "context") {
|
|
3894
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.message.entry_id, children: /* @__PURE__ */ jsx16(ContextCard, { context: block.message.context }) }, block.key);
|
|
3895
|
+
}
|
|
3896
|
+
if (block.type === "compaction") {
|
|
3897
|
+
return /* @__PURE__ */ jsxs14(
|
|
3898
|
+
"div",
|
|
3899
|
+
{
|
|
3900
|
+
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3901
|
+
children: [
|
|
3902
|
+
/* @__PURE__ */ jsx16(Layers, { size: 12 }),
|
|
3903
|
+
/* @__PURE__ */ jsx16("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
3904
|
+
]
|
|
3905
|
+
},
|
|
3906
|
+
block.key
|
|
3907
|
+
);
|
|
3908
|
+
}
|
|
3909
|
+
return /* @__PURE__ */ jsx16(PlanningDivider, { kind: block.kind }, block.key);
|
|
3910
|
+
}),
|
|
3911
|
+
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx16("div", { className: "flex", children: /* @__PURE__ */ jsx16("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
|
|
3912
|
+
] }) }) }),
|
|
3913
|
+
/* @__PURE__ */ jsx16(
|
|
3914
|
+
PinLatestUserMessage,
|
|
3915
|
+
{
|
|
3916
|
+
userMessageCount: userMessages.length,
|
|
3917
|
+
shouldPinLatestUser,
|
|
3918
|
+
targetKey: latestUserMessage?.render_id ?? latestUserMessage?.entry_id ?? (latestUserMessage ? `user:${userMessages.length}` : null)
|
|
3919
|
+
},
|
|
3920
|
+
sessionId ?? "no-session"
|
|
3921
|
+
),
|
|
3922
|
+
/* @__PURE__ */ jsx16(ScrollToBottomButton, {})
|
|
3923
|
+
]
|
|
3924
|
+
},
|
|
3925
|
+
sessionId ?? "no-session"
|
|
3926
|
+
) });
|
|
3021
3927
|
}
|
|
3022
|
-
function
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3928
|
+
function PinLatestUserMessage({
|
|
3929
|
+
userMessageCount,
|
|
3930
|
+
shouldPinLatestUser,
|
|
3931
|
+
targetKey
|
|
3932
|
+
}) {
|
|
3933
|
+
const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
|
|
3934
|
+
const previousCountRef = useRef11(userMessageCount);
|
|
3935
|
+
const spacerHeightRef = useRef11(0);
|
|
3936
|
+
const getScrollElement = useCallback7(() => scrollRef.current, [scrollRef]);
|
|
3937
|
+
const getContentElement = useCallback7(() => contentRef.current, [contentRef]);
|
|
3938
|
+
const getTargetElement = useCallback7(() => {
|
|
3939
|
+
const rows = contentRef.current?.querySelectorAll(".blade-chat-user-row");
|
|
3940
|
+
return rows?.item((rows?.length ?? 0) - 1) ?? null;
|
|
3941
|
+
}, [contentRef]);
|
|
3942
|
+
const getSpacerHeight = useCallback7(() => spacerHeightRef.current, []);
|
|
3943
|
+
const setSpacerHeight = useCallback7(
|
|
3944
|
+
(height) => {
|
|
3945
|
+
spacerHeightRef.current = height;
|
|
3946
|
+
const content = contentRef.current;
|
|
3947
|
+
if (!content) return;
|
|
3948
|
+
if (height > 0) content.style.setProperty("--blade-chat-pin-spacer", `${height}px`);
|
|
3949
|
+
else content.style.removeProperty("--blade-chat-pin-spacer");
|
|
3950
|
+
},
|
|
3951
|
+
[contentRef]
|
|
3952
|
+
);
|
|
3953
|
+
useMessagePin({
|
|
3954
|
+
targetKey,
|
|
3955
|
+
pinTarget: shouldPinLatestUser,
|
|
3956
|
+
getScrollElement,
|
|
3957
|
+
getContentElement,
|
|
3958
|
+
getTargetElement,
|
|
3959
|
+
getSpacerHeight,
|
|
3960
|
+
setSpacerHeight,
|
|
3961
|
+
stopAutoScroll: stopScroll,
|
|
3962
|
+
scrollToBottom
|
|
3963
|
+
});
|
|
3964
|
+
useEffect10(() => {
|
|
3965
|
+
if (userMessageCount > previousCountRef.current && !shouldPinLatestUser) {
|
|
3027
3966
|
scrollToBottom("instant");
|
|
3028
3967
|
}
|
|
3029
3968
|
previousCountRef.current = userMessageCount;
|
|
3030
|
-
}, [
|
|
3969
|
+
}, [scrollToBottom, shouldPinLatestUser, userMessageCount]);
|
|
3031
3970
|
return null;
|
|
3032
3971
|
}
|
|
3033
3972
|
function ScrollToBottomButton() {
|
|
3034
3973
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3035
|
-
const [visible, setVisible] =
|
|
3036
|
-
const hideTimerRef =
|
|
3037
|
-
|
|
3974
|
+
const [visible, setVisible] = useState12(false);
|
|
3975
|
+
const hideTimerRef = useRef11(null);
|
|
3976
|
+
useEffect10(() => {
|
|
3038
3977
|
if (isAtBottom) {
|
|
3039
3978
|
if (!hideTimerRef.current) {
|
|
3040
3979
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3056,7 +3995,7 @@ function ScrollToBottomButton() {
|
|
|
3056
3995
|
}
|
|
3057
3996
|
};
|
|
3058
3997
|
}, [isAtBottom]);
|
|
3059
|
-
const handleClick =
|
|
3998
|
+
const handleClick = useCallback7(() => {
|
|
3060
3999
|
if (hideTimerRef.current) {
|
|
3061
4000
|
clearTimeout(hideTimerRef.current);
|
|
3062
4001
|
hideTimerRef.current = null;
|
|
@@ -3065,7 +4004,7 @@ function ScrollToBottomButton() {
|
|
|
3065
4004
|
scrollToBottom();
|
|
3066
4005
|
}, [scrollToBottom]);
|
|
3067
4006
|
if (!visible) return null;
|
|
3068
|
-
return /* @__PURE__ */
|
|
4007
|
+
return /* @__PURE__ */ jsxs14(
|
|
3069
4008
|
"button",
|
|
3070
4009
|
{
|
|
3071
4010
|
type: "button",
|
|
@@ -3073,25 +4012,25 @@ function ScrollToBottomButton() {
|
|
|
3073
4012
|
"aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
|
|
3074
4013
|
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))]",
|
|
3075
4014
|
children: [
|
|
3076
|
-
/* @__PURE__ */
|
|
3077
|
-
/* @__PURE__ */
|
|
4015
|
+
/* @__PURE__ */ jsx16(ChevronDown, { size: 14 }),
|
|
4016
|
+
/* @__PURE__ */ jsx16("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
|
|
3078
4017
|
]
|
|
3079
4018
|
}
|
|
3080
4019
|
);
|
|
3081
4020
|
}
|
|
3082
4021
|
function PlanningDivider({ kind }) {
|
|
3083
|
-
return /* @__PURE__ */
|
|
3084
|
-
/* @__PURE__ */
|
|
3085
|
-
/* @__PURE__ */
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
4022
|
+
return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3 py-1", children: [
|
|
4023
|
+
/* @__PURE__ */ jsx16("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
|
|
4024
|
+
/* @__PURE__ */ jsxs14("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: [
|
|
4025
|
+
/* @__PURE__ */ jsx16(Lightbulb, { size: 12 }),
|
|
4026
|
+
/* @__PURE__ */ jsx16("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
|
|
3088
4027
|
] }),
|
|
3089
|
-
/* @__PURE__ */
|
|
4028
|
+
/* @__PURE__ */ jsx16("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
|
|
3090
4029
|
] });
|
|
3091
4030
|
}
|
|
3092
4031
|
|
|
3093
4032
|
// src/components/ChatSurface.tsx
|
|
3094
|
-
import { jsx as
|
|
4033
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3095
4034
|
function themeAttr(theme) {
|
|
3096
4035
|
return theme === "dark" ? "dark" : void 0;
|
|
3097
4036
|
}
|
|
@@ -3123,7 +4062,7 @@ function ChatSurface({
|
|
|
3123
4062
|
beforeInput,
|
|
3124
4063
|
banner
|
|
3125
4064
|
}) {
|
|
3126
|
-
return /* @__PURE__ */
|
|
4065
|
+
return /* @__PURE__ */ jsxs15(
|
|
3127
4066
|
"div",
|
|
3128
4067
|
{
|
|
3129
4068
|
"data-theme": themeAttr(theme),
|
|
@@ -3132,14 +4071,14 @@ function ChatSurface({
|
|
|
3132
4071
|
classNames?.root
|
|
3133
4072
|
),
|
|
3134
4073
|
children: [
|
|
3135
|
-
/* @__PURE__ */
|
|
4074
|
+
/* @__PURE__ */ jsx17(ConnectionBanner, { connection, className: classNames?.banner }),
|
|
3136
4075
|
banner,
|
|
3137
|
-
errorMessage && /* @__PURE__ */
|
|
3138
|
-
/* @__PURE__ */
|
|
3139
|
-
/* @__PURE__ */
|
|
4076
|
+
errorMessage && /* @__PURE__ */ jsxs15("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
|
|
4077
|
+
/* @__PURE__ */ jsx17(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
|
|
4078
|
+
/* @__PURE__ */ jsx17("span", { children: errorMessage })
|
|
3140
4079
|
] }),
|
|
3141
4080
|
slots?.header,
|
|
3142
|
-
/* @__PURE__ */
|
|
4081
|
+
/* @__PURE__ */ jsx17(
|
|
3143
4082
|
MessageList,
|
|
3144
4083
|
{
|
|
3145
4084
|
messages,
|
|
@@ -3160,7 +4099,7 @@ function ChatSurface({
|
|
|
3160
4099
|
}
|
|
3161
4100
|
),
|
|
3162
4101
|
beforeInput,
|
|
3163
|
-
/* @__PURE__ */
|
|
4102
|
+
/* @__PURE__ */ jsx17(
|
|
3164
4103
|
ChatInput,
|
|
3165
4104
|
{
|
|
3166
4105
|
value: inputText,
|
|
@@ -3180,13 +4119,13 @@ function ChatSurface({
|
|
|
3180
4119
|
}
|
|
3181
4120
|
|
|
3182
4121
|
// src/components/AgentChat.tsx
|
|
3183
|
-
import { Fragment as Fragment3, jsx as
|
|
4122
|
+
import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3184
4123
|
function isUnauthorizedError(error) {
|
|
3185
4124
|
return error instanceof BladeApiError && error.status === 401;
|
|
3186
4125
|
}
|
|
3187
4126
|
function LoginCard({ client, onLoggedIn }) {
|
|
3188
|
-
const [loggingIn, setLoggingIn] =
|
|
3189
|
-
const [loginError, setLoginError] =
|
|
4127
|
+
const [loggingIn, setLoggingIn] = useState13(false);
|
|
4128
|
+
const [loginError, setLoginError] = useState13(null);
|
|
3190
4129
|
const handleLogin = async () => {
|
|
3191
4130
|
setLoggingIn(true);
|
|
3192
4131
|
setLoginError(null);
|
|
@@ -3199,11 +4138,11 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3199
4138
|
setLoggingIn(false);
|
|
3200
4139
|
}
|
|
3201
4140
|
};
|
|
3202
|
-
return /* @__PURE__ */
|
|
3203
|
-
/* @__PURE__ */
|
|
3204
|
-
/* @__PURE__ */
|
|
3205
|
-
/* @__PURE__ */
|
|
3206
|
-
/* @__PURE__ */
|
|
4141
|
+
return /* @__PURE__ */ jsx18("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs16("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: [
|
|
4142
|
+
/* @__PURE__ */ jsx18(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
|
|
4143
|
+
/* @__PURE__ */ jsx18("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
|
|
4144
|
+
/* @__PURE__ */ jsx18("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" }),
|
|
4145
|
+
/* @__PURE__ */ jsx18(
|
|
3207
4146
|
"button",
|
|
3208
4147
|
{
|
|
3209
4148
|
type: "button",
|
|
@@ -3213,20 +4152,20 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3213
4152
|
children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
3214
4153
|
}
|
|
3215
4154
|
),
|
|
3216
|
-
loginError && /* @__PURE__ */
|
|
4155
|
+
loginError && /* @__PURE__ */ jsx18("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
|
|
3217
4156
|
] }) });
|
|
3218
4157
|
}
|
|
3219
4158
|
function AgentChat(props) {
|
|
3220
4159
|
const client = useBladeClient();
|
|
3221
|
-
const [attempt, setAttempt] =
|
|
3222
|
-
const [needLogin, setNeedLogin] =
|
|
4160
|
+
const [attempt, setAttempt] = useState13(0);
|
|
4161
|
+
const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
|
|
3223
4162
|
if (needLogin) {
|
|
3224
|
-
return /* @__PURE__ */
|
|
4163
|
+
return /* @__PURE__ */ jsx18(
|
|
3225
4164
|
"div",
|
|
3226
4165
|
{
|
|
3227
4166
|
"data-theme": themeAttr(props.theme),
|
|
3228
4167
|
className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
|
|
3229
|
-
children: /* @__PURE__ */
|
|
4168
|
+
children: /* @__PURE__ */ jsx18(
|
|
3230
4169
|
LoginCard,
|
|
3231
4170
|
{
|
|
3232
4171
|
client,
|
|
@@ -3239,7 +4178,7 @@ function AgentChat(props) {
|
|
|
3239
4178
|
}
|
|
3240
4179
|
);
|
|
3241
4180
|
}
|
|
3242
|
-
return /* @__PURE__ */
|
|
4181
|
+
return /* @__PURE__ */ jsx18(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
|
|
3243
4182
|
}
|
|
3244
4183
|
function ChatSessionView({
|
|
3245
4184
|
sessionId,
|
|
@@ -3261,12 +4200,12 @@ function ChatSessionView({
|
|
|
3261
4200
|
onSessionCreated
|
|
3262
4201
|
});
|
|
3263
4202
|
const replay = useReplay(session);
|
|
3264
|
-
const [stopRequested, setStopRequested] =
|
|
3265
|
-
const [inputText, setInputText] =
|
|
3266
|
-
const [resultFeedback, setResultFeedback] =
|
|
4203
|
+
const [stopRequested, setStopRequested] = useState13(false);
|
|
4204
|
+
const [inputText, setInputText] = useState13("");
|
|
4205
|
+
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3267
4206
|
const resolvedSessionId = session?.sessionId;
|
|
3268
4207
|
const isViewer = state?.viewerRole === "viewer";
|
|
3269
|
-
|
|
4208
|
+
useEffect11(() => {
|
|
3270
4209
|
setResultFeedback([]);
|
|
3271
4210
|
if (!resolvedSessionId || isViewer) return;
|
|
3272
4211
|
let cancelled = false;
|
|
@@ -3295,18 +4234,18 @@ function ChatSessionView({
|
|
|
3295
4234
|
() => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
|
|
3296
4235
|
[resultFeedback]
|
|
3297
4236
|
);
|
|
3298
|
-
const handleResultFeedbackSaved =
|
|
4237
|
+
const handleResultFeedbackSaved = useCallback8((saved) => {
|
|
3299
4238
|
setResultFeedback((current) => [
|
|
3300
4239
|
...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
|
|
3301
4240
|
saved
|
|
3302
4241
|
]);
|
|
3303
4242
|
}, []);
|
|
3304
|
-
|
|
4243
|
+
useEffect11(() => {
|
|
3305
4244
|
if (session) {
|
|
3306
4245
|
onSessionReady?.(session);
|
|
3307
4246
|
}
|
|
3308
4247
|
}, [session, onSessionReady]);
|
|
3309
|
-
|
|
4248
|
+
useEffect11(() => {
|
|
3310
4249
|
if (!session) return;
|
|
3311
4250
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
3312
4251
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3322,12 +4261,12 @@ ${content}`);
|
|
|
3322
4261
|
offInsert();
|
|
3323
4262
|
};
|
|
3324
4263
|
}, [session]);
|
|
3325
|
-
|
|
4264
|
+
useEffect11(() => {
|
|
3326
4265
|
if (isUnauthorizedError(error)) {
|
|
3327
4266
|
onUnauthorized();
|
|
3328
4267
|
}
|
|
3329
4268
|
}, [error, onUnauthorized]);
|
|
3330
|
-
|
|
4269
|
+
useEffect11(() => {
|
|
3331
4270
|
if (!session || !commands) return;
|
|
3332
4271
|
const unsubscribes = Object.entries(commands).map(
|
|
3333
4272
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3348,7 +4287,7 @@ ${content}`);
|
|
|
3348
4287
|
setStopRequested(true);
|
|
3349
4288
|
void session?.stop();
|
|
3350
4289
|
};
|
|
3351
|
-
return /* @__PURE__ */
|
|
4290
|
+
return /* @__PURE__ */ jsx18(
|
|
3352
4291
|
ChatSurface,
|
|
3353
4292
|
{
|
|
3354
4293
|
theme,
|
|
@@ -3358,8 +4297,8 @@ ${content}`);
|
|
|
3358
4297
|
slots,
|
|
3359
4298
|
placeholder,
|
|
3360
4299
|
connection: state?.connection ?? "connecting",
|
|
3361
|
-
banner: /* @__PURE__ */
|
|
3362
|
-
/* @__PURE__ */
|
|
4300
|
+
banner: /* @__PURE__ */ jsxs16(Fragment3, { children: [
|
|
4301
|
+
/* @__PURE__ */ jsx18(
|
|
3363
4302
|
ReplayBar,
|
|
3364
4303
|
{
|
|
3365
4304
|
isReplay: replay.isReplay,
|
|
@@ -3369,7 +4308,7 @@ ${content}`);
|
|
|
3369
4308
|
onExit: () => void replay.exitToAutonomous()
|
|
3370
4309
|
}
|
|
3371
4310
|
),
|
|
3372
|
-
/* @__PURE__ */
|
|
4311
|
+
/* @__PURE__ */ jsx18(ReplayMismatchPrompt, { mismatch: replay.mismatch })
|
|
3373
4312
|
] }),
|
|
3374
4313
|
errorMessage,
|
|
3375
4314
|
messages: state?.messages ?? [],
|
|
@@ -3398,11 +4337,11 @@ ${content}`);
|
|
|
3398
4337
|
}
|
|
3399
4338
|
|
|
3400
4339
|
// src/components/LlmChat.tsx
|
|
3401
|
-
import { useEffect as
|
|
4340
|
+
import { useEffect as useEffect12, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3402
4341
|
|
|
3403
4342
|
// src/components/LlmAdvancedSettings.tsx
|
|
3404
|
-
import { useState as
|
|
3405
|
-
import { jsx as
|
|
4343
|
+
import { useState as useState14 } from "react";
|
|
4344
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3406
4345
|
var FIELDS = [
|
|
3407
4346
|
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
3408
4347
|
{ id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
|
|
@@ -3448,13 +4387,13 @@ function writeOverride(settings, baseURL, override) {
|
|
|
3448
4387
|
}
|
|
3449
4388
|
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3450
4389
|
const normalized = normalizeAdvanced(settings);
|
|
3451
|
-
const [open, setOpen] =
|
|
3452
|
-
const [draft, setDraft] =
|
|
4390
|
+
const [open, setOpen] = useState14(false);
|
|
4391
|
+
const [draft, setDraft] = useState14(override);
|
|
3453
4392
|
if (!normalized) return null;
|
|
3454
4393
|
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3455
4394
|
const dirty = Object.keys(override).length > 0;
|
|
3456
|
-
return /* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
4395
|
+
return /* @__PURE__ */ jsxs17("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
|
|
4396
|
+
/* @__PURE__ */ jsxs17(
|
|
3458
4397
|
"button",
|
|
3459
4398
|
{
|
|
3460
4399
|
type: "button",
|
|
@@ -3464,16 +4403,16 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
|
3464
4403
|
},
|
|
3465
4404
|
className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
3466
4405
|
children: [
|
|
3467
|
-
/* @__PURE__ */
|
|
4406
|
+
/* @__PURE__ */ jsx19(Settings2, { size: 13 }),
|
|
3468
4407
|
"\u9AD8\u7EA7\u8BBE\u7F6E",
|
|
3469
|
-
dirty && /* @__PURE__ */
|
|
4408
|
+
dirty && /* @__PURE__ */ jsx19("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
|
|
3470
4409
|
]
|
|
3471
4410
|
}
|
|
3472
4411
|
),
|
|
3473
|
-
open && /* @__PURE__ */
|
|
3474
|
-
fields.map((field) => /* @__PURE__ */
|
|
3475
|
-
/* @__PURE__ */
|
|
3476
|
-
/* @__PURE__ */
|
|
4412
|
+
open && /* @__PURE__ */ jsxs17("div", { className: "mt-2 flex flex-col gap-2", children: [
|
|
4413
|
+
fields.map((field) => /* @__PURE__ */ jsxs17("label", { className: "flex flex-col gap-1", children: [
|
|
4414
|
+
/* @__PURE__ */ jsx19("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
|
|
4415
|
+
/* @__PURE__ */ jsx19(
|
|
3477
4416
|
"input",
|
|
3478
4417
|
{
|
|
3479
4418
|
type: field.secret ? "password" : "text",
|
|
@@ -3484,9 +4423,9 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
|
3484
4423
|
}
|
|
3485
4424
|
)
|
|
3486
4425
|
] }, field.id)),
|
|
3487
|
-
normalized.apiKey && /* @__PURE__ */
|
|
3488
|
-
/* @__PURE__ */
|
|
3489
|
-
/* @__PURE__ */
|
|
4426
|
+
normalized.apiKey && /* @__PURE__ */ jsx19("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" }),
|
|
4427
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex gap-2", children: [
|
|
4428
|
+
/* @__PURE__ */ jsx19(
|
|
3490
4429
|
"button",
|
|
3491
4430
|
{
|
|
3492
4431
|
type: "button",
|
|
@@ -3501,7 +4440,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
|
3501
4440
|
children: "\u4FDD\u5B58"
|
|
3502
4441
|
}
|
|
3503
4442
|
),
|
|
3504
|
-
/* @__PURE__ */
|
|
4443
|
+
/* @__PURE__ */ jsx19(
|
|
3505
4444
|
"button",
|
|
3506
4445
|
{
|
|
3507
4446
|
type: "button",
|
|
@@ -3520,7 +4459,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
|
3520
4459
|
}
|
|
3521
4460
|
|
|
3522
4461
|
// src/components/LlmChat.tsx
|
|
3523
|
-
import { jsx as
|
|
4462
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
3524
4463
|
function LlmChat({
|
|
3525
4464
|
classNames,
|
|
3526
4465
|
renderers,
|
|
@@ -3532,11 +4471,11 @@ function LlmChat({
|
|
|
3532
4471
|
onOverrideChange,
|
|
3533
4472
|
...options
|
|
3534
4473
|
}) {
|
|
3535
|
-
const [override, setOverride] =
|
|
4474
|
+
const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
|
|
3536
4475
|
const effective = { ...options, ...override };
|
|
3537
4476
|
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3538
|
-
const [inputText, setInputText] =
|
|
3539
|
-
const [stopRequested, setStopRequested] =
|
|
4477
|
+
const [inputText, setInputText] = useState15("");
|
|
4478
|
+
const [stopRequested, setStopRequested] = useState15(false);
|
|
3540
4479
|
const handle = useMemo9(
|
|
3541
4480
|
() => ({
|
|
3542
4481
|
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
@@ -3546,10 +4485,10 @@ ${text}` : text),
|
|
|
3546
4485
|
}),
|
|
3547
4486
|
[send, reset]
|
|
3548
4487
|
);
|
|
3549
|
-
|
|
4488
|
+
useEffect12(() => {
|
|
3550
4489
|
onReady?.(handle);
|
|
3551
4490
|
}, [handle, onReady]);
|
|
3552
|
-
return /* @__PURE__ */
|
|
4491
|
+
return /* @__PURE__ */ jsx20(
|
|
3553
4492
|
ChatSurface,
|
|
3554
4493
|
{
|
|
3555
4494
|
theme,
|
|
@@ -3574,7 +4513,7 @@ ${text}` : text),
|
|
|
3574
4513
|
setStopRequested(true);
|
|
3575
4514
|
stop();
|
|
3576
4515
|
},
|
|
3577
|
-
beforeInput: advanced ? /* @__PURE__ */
|
|
4516
|
+
beforeInput: advanced ? /* @__PURE__ */ jsx20(
|
|
3578
4517
|
LlmAdvancedSettingsBar,
|
|
3579
4518
|
{
|
|
3580
4519
|
settings: advanced,
|
|
@@ -3592,14 +4531,14 @@ ${text}` : text),
|
|
|
3592
4531
|
}
|
|
3593
4532
|
|
|
3594
4533
|
// src/components/ChatView.tsx
|
|
3595
|
-
import { jsx as
|
|
4534
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
3596
4535
|
function ChatView(props) {
|
|
3597
4536
|
const { mode, llm, onLlmReady, ...rest } = props;
|
|
3598
4537
|
if (mode === "llm") {
|
|
3599
4538
|
if (!llm) {
|
|
3600
4539
|
throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
|
|
3601
4540
|
}
|
|
3602
|
-
return /* @__PURE__ */
|
|
4541
|
+
return /* @__PURE__ */ jsx21(
|
|
3603
4542
|
LlmChat,
|
|
3604
4543
|
{
|
|
3605
4544
|
...llm,
|
|
@@ -3612,7 +4551,7 @@ function ChatView(props) {
|
|
|
3612
4551
|
}
|
|
3613
4552
|
);
|
|
3614
4553
|
}
|
|
3615
|
-
return /* @__PURE__ */
|
|
4554
|
+
return /* @__PURE__ */ jsx21(AgentChat, { ...rest });
|
|
3616
4555
|
}
|
|
3617
4556
|
|
|
3618
4557
|
// src/index.ts
|
|
@@ -3621,6 +4560,7 @@ export {
|
|
|
3621
4560
|
AgentChat,
|
|
3622
4561
|
BladeProvider,
|
|
3623
4562
|
ChatView,
|
|
4563
|
+
ContextCard,
|
|
3624
4564
|
LlmChat,
|
|
3625
4565
|
MarkdownContent,
|
|
3626
4566
|
ReplayBar,
|
|
@@ -3628,6 +4568,7 @@ export {
|
|
|
3628
4568
|
useAgentSession,
|
|
3629
4569
|
useBladeClient,
|
|
3630
4570
|
useLlmChat,
|
|
4571
|
+
useMessagePin,
|
|
3631
4572
|
useReplay
|
|
3632
4573
|
};
|
|
3633
4574
|
/*! Bundled license information:
|
|
@@ -3639,17 +4580,16 @@ lucide-react/dist/esm/createLucideIcon.js:
|
|
|
3639
4580
|
lucide-react/dist/esm/icons/arrow-right.js:
|
|
3640
4581
|
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
3641
4582
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
4583
|
+
lucide-react/dist/esm/icons/book-open.js:
|
|
3642
4584
|
lucide-react/dist/esm/icons/bot.js:
|
|
3643
|
-
lucide-react/dist/esm/icons/brain.js:
|
|
3644
4585
|
lucide-react/dist/esm/icons/check.js:
|
|
3645
4586
|
lucide-react/dist/esm/icons/chevron-down.js:
|
|
3646
4587
|
lucide-react/dist/esm/icons/chevron-right.js:
|
|
3647
4588
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3648
4589
|
lucide-react/dist/esm/icons/copy.js:
|
|
3649
|
-
lucide-react/dist/esm/icons/
|
|
4590
|
+
lucide-react/dist/esm/icons/earth.js:
|
|
4591
|
+
lucide-react/dist/esm/icons/file-pen-line.js:
|
|
3650
4592
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3651
|
-
lucide-react/dist/esm/icons/file.js:
|
|
3652
|
-
lucide-react/dist/esm/icons/film.js:
|
|
3653
4593
|
lucide-react/dist/esm/icons/globe.js:
|
|
3654
4594
|
lucide-react/dist/esm/icons/layers.js:
|
|
3655
4595
|
lucide-react/dist/esm/icons/lightbulb.js:
|
|
@@ -3658,10 +4598,13 @@ lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
|
3658
4598
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
3659
4599
|
lucide-react/dist/esm/icons/message-square.js:
|
|
3660
4600
|
lucide-react/dist/esm/icons/play.js:
|
|
4601
|
+
lucide-react/dist/esm/icons/search.js:
|
|
3661
4602
|
lucide-react/dist/esm/icons/settings-2.js:
|
|
3662
4603
|
lucide-react/dist/esm/icons/sparkles.js:
|
|
3663
4604
|
lucide-react/dist/esm/icons/square.js:
|
|
4605
|
+
lucide-react/dist/esm/icons/terminal.js:
|
|
3664
4606
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
4607
|
+
lucide-react/dist/esm/icons/wrench.js:
|
|
3665
4608
|
lucide-react/dist/esm/icons/x.js:
|
|
3666
4609
|
lucide-react/dist/esm/lucide-react.js:
|
|
3667
4610
|
(**
|