@blade-hq/agent-react 2610.0.0-beta.0 → 2610.0.0-beta.10
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 +5 -3
- package/dist/components/AgentChat.d.ts +3 -0
- package/dist/components/AssistantTurnBlock.d.ts +1 -1
- package/dist/components/ChatSurface.d.ts +11 -2
- package/dist/components/ChatView.d.ts +1 -0
- package/dist/components/ConnectionBanner.d.ts +1 -1
- package/dist/components/MarkdownContent.d.ts +3 -6
- package/dist/components/MessageList.d.ts +10 -3
- package/dist/components/PostChatFollowupBlock.d.ts +59 -0
- package/dist/embed/entry.d.ts +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +712 -338
- package/dist/index.js.map +1 -1
- package/dist/style.css +14 -0
- package/dist/style.full.css +15 -1
- package/package.json +5 -3
- package/public-api.md +153 -1
- package/dist/components/FileCard.d.ts +0 -16
- package/dist/lib/media-tags.d.ts +0 -24
package/dist/index.js
CHANGED
|
@@ -545,7 +545,7 @@ function toToolCallInfo(call, results, failedToolIds) {
|
|
|
545
545
|
}
|
|
546
546
|
|
|
547
547
|
// src/components/AgentChat.tsx
|
|
548
|
-
import { BladeApiError } from "@blade-hq/agent-client";
|
|
548
|
+
import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
|
|
549
549
|
|
|
550
550
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
551
551
|
import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
|
|
@@ -618,11 +618,16 @@ var createLucideIcon = (iconName, iconNode) => {
|
|
|
618
618
|
return Component2;
|
|
619
619
|
};
|
|
620
620
|
|
|
621
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/
|
|
622
|
-
var
|
|
623
|
-
["
|
|
624
|
-
["path", { d: "
|
|
625
|
-
|
|
621
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-right.js
|
|
622
|
+
var ArrowRight = createLucideIcon("ArrowRight", [
|
|
623
|
+
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
624
|
+
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
625
|
+
]);
|
|
626
|
+
|
|
627
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up-right.js
|
|
628
|
+
var ArrowUpRight = createLucideIcon("ArrowUpRight", [
|
|
629
|
+
["path", { d: "M7 7h10v10", key: "1tivn9" }],
|
|
630
|
+
["path", { d: "M7 17 17 7", key: "1vkiza" }]
|
|
626
631
|
]);
|
|
627
632
|
|
|
628
633
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
|
|
@@ -699,24 +704,6 @@ var Download = createLucideIcon("Download", [
|
|
|
699
704
|
["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
|
|
700
705
|
]);
|
|
701
706
|
|
|
702
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-code-2.js
|
|
703
|
-
var FileCode2 = createLucideIcon("FileCode2", [
|
|
704
|
-
["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }],
|
|
705
|
-
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
|
|
706
|
-
["path", { d: "m5 12-3 3 3 3", key: "oke12k" }],
|
|
707
|
-
["path", { d: "m9 18 3-3-3-3", key: "112psh" }]
|
|
708
|
-
]);
|
|
709
|
-
|
|
710
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js
|
|
711
|
-
var FileSpreadsheet = createLucideIcon("FileSpreadsheet", [
|
|
712
|
-
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
713
|
-
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
|
|
714
|
-
["path", { d: "M8 13h2", key: "yr2amv" }],
|
|
715
|
-
["path", { d: "M14 13h2", key: "un5t4a" }],
|
|
716
|
-
["path", { d: "M8 17h2", key: "2yhykz" }],
|
|
717
|
-
["path", { d: "M14 17h2", key: "10kma7" }]
|
|
718
|
-
]);
|
|
719
|
-
|
|
720
707
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
721
708
|
var FileText = createLucideIcon("FileText", [
|
|
722
709
|
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
@@ -744,11 +731,11 @@ var Film = createLucideIcon("Film", [
|
|
|
744
731
|
["path", { d: "M17 16.5h4", key: "go4c1d" }]
|
|
745
732
|
]);
|
|
746
733
|
|
|
747
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/
|
|
748
|
-
var
|
|
749
|
-
["
|
|
750
|
-
["
|
|
751
|
-
["path", { d: "
|
|
734
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
|
|
735
|
+
var Globe = createLucideIcon("Globe", [
|
|
736
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
737
|
+
["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
|
|
738
|
+
["path", { d: "M2 12h20", key: "9i4pu4" }]
|
|
752
739
|
]);
|
|
753
740
|
|
|
754
741
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
|
|
@@ -814,13 +801,6 @@ var MessageSquare = createLucideIcon("MessageSquare", [
|
|
|
814
801
|
["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
|
|
815
802
|
]);
|
|
816
803
|
|
|
817
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/music.js
|
|
818
|
-
var Music = createLucideIcon("Music", [
|
|
819
|
-
["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
|
|
820
|
-
["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
|
|
821
|
-
["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
|
|
822
|
-
]);
|
|
823
|
-
|
|
824
804
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
|
|
825
805
|
var Play = createLucideIcon("Play", [
|
|
826
806
|
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
@@ -834,6 +814,21 @@ var Settings2 = createLucideIcon("Settings2", [
|
|
|
834
814
|
["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
|
|
835
815
|
]);
|
|
836
816
|
|
|
817
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/sparkles.js
|
|
818
|
+
var Sparkles = createLucideIcon("Sparkles", [
|
|
819
|
+
[
|
|
820
|
+
"path",
|
|
821
|
+
{
|
|
822
|
+
d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
|
|
823
|
+
key: "4pj2yx"
|
|
824
|
+
}
|
|
825
|
+
],
|
|
826
|
+
["path", { d: "M20 3v4", key: "1olli1" }],
|
|
827
|
+
["path", { d: "M22 5h-4", key: "1gvqau" }],
|
|
828
|
+
["path", { d: "M4 17v2", key: "vumght" }],
|
|
829
|
+
["path", { d: "M5 18H3", key: "zchphs" }]
|
|
830
|
+
]);
|
|
831
|
+
|
|
837
832
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
|
|
838
833
|
var Square = createLucideIcon("Square", [
|
|
839
834
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
@@ -859,7 +854,7 @@ var X = createLucideIcon("X", [
|
|
|
859
854
|
]);
|
|
860
855
|
|
|
861
856
|
// src/components/AgentChat.tsx
|
|
862
|
-
import { useEffect as
|
|
857
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo8, useState as useState13 } from "react";
|
|
863
858
|
|
|
864
859
|
// src/lib/utils.ts
|
|
865
860
|
function cn(...inputs) {
|
|
@@ -1057,24 +1052,74 @@ function ChatInput({
|
|
|
1057
1052
|
}
|
|
1058
1053
|
|
|
1059
1054
|
// src/components/ConnectionBanner.tsx
|
|
1055
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
|
|
1060
1056
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1057
|
+
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1058
|
+
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1059
|
+
function useConnectionNoticePhase(connected) {
|
|
1060
|
+
const [phase, setPhase] = useState4("hidden");
|
|
1061
|
+
const connectedRef = useRef3(connected);
|
|
1062
|
+
const timersRef = useRef3([]);
|
|
1063
|
+
connectedRef.current = connected;
|
|
1064
|
+
useEffect3(() => {
|
|
1065
|
+
const clearTimers = () => {
|
|
1066
|
+
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1067
|
+
timersRef.current = [];
|
|
1068
|
+
};
|
|
1069
|
+
const startGracePeriod = () => {
|
|
1070
|
+
clearTimers();
|
|
1071
|
+
setPhase("hidden");
|
|
1072
|
+
timersRef.current = [
|
|
1073
|
+
setTimeout(() => setPhase("recovering"), CONNECTION_NOTICE_DELAY_MS),
|
|
1074
|
+
setTimeout(() => setPhase("failed"), CONNECTION_ERROR_DELAY_MS)
|
|
1075
|
+
];
|
|
1076
|
+
};
|
|
1077
|
+
if (connected) {
|
|
1078
|
+
clearTimers();
|
|
1079
|
+
setPhase("hidden");
|
|
1080
|
+
} else {
|
|
1081
|
+
startGracePeriod();
|
|
1082
|
+
}
|
|
1083
|
+
const handleForeground = () => {
|
|
1084
|
+
if (!connectedRef.current) startGracePeriod();
|
|
1085
|
+
};
|
|
1086
|
+
const handleVisibilityChange = () => {
|
|
1087
|
+
if (document.visibilityState === "visible") handleForeground();
|
|
1088
|
+
};
|
|
1089
|
+
window.addEventListener("blade:app-active", handleForeground);
|
|
1090
|
+
window.addEventListener("focus", handleForeground);
|
|
1091
|
+
window.addEventListener("pageshow", handleForeground);
|
|
1092
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1093
|
+
return () => {
|
|
1094
|
+
clearTimers();
|
|
1095
|
+
window.removeEventListener("blade:app-active", handleForeground);
|
|
1096
|
+
window.removeEventListener("focus", handleForeground);
|
|
1097
|
+
window.removeEventListener("pageshow", handleForeground);
|
|
1098
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1099
|
+
};
|
|
1100
|
+
}, [connected]);
|
|
1101
|
+
return phase;
|
|
1102
|
+
}
|
|
1061
1103
|
function ConnectionBanner({ connection, className }) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const
|
|
1104
|
+
const hasConnectedRef = useRef3(connection === "connected" || connection === "reconnecting");
|
|
1105
|
+
if (connection === "connected") hasConnectedRef.current = true;
|
|
1106
|
+
const connected = connection === "connected";
|
|
1107
|
+
const phase = useConnectionNoticePhase(connected);
|
|
1108
|
+
if (connected || phase === "hidden") return null;
|
|
1109
|
+
const recovering = phase === "recovering";
|
|
1110
|
+
const firstConnection = !hasConnectedRef.current;
|
|
1066
1111
|
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
1067
1112
|
"div",
|
|
1068
1113
|
{
|
|
1069
1114
|
className: cn(
|
|
1070
1115
|
"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
|
|
1071
|
-
|
|
1116
|
+
recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
1072
1117
|
),
|
|
1073
1118
|
children: [
|
|
1074
|
-
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children:
|
|
1119
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
|
|
1075
1120
|
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
1076
|
-
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children:
|
|
1077
|
-
/* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: "\
|
|
1121
|
+
/* @__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" }),
|
|
1122
|
+
/* @__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" })
|
|
1078
1123
|
] })
|
|
1079
1124
|
]
|
|
1080
1125
|
}
|
|
@@ -1083,10 +1128,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1083
1128
|
|
|
1084
1129
|
// src/components/MessageList.tsx
|
|
1085
1130
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1086
|
-
import { useCallback as
|
|
1131
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState12 } from "react";
|
|
1087
1132
|
|
|
1088
1133
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1089
|
-
import { useCallback as useCallback3, useMemo as useMemo3, useRef as
|
|
1134
|
+
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
1090
1135
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1091
1136
|
/**
|
|
1092
1137
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1123,10 +1168,10 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1123
1168
|
mouseDown = false;
|
|
1124
1169
|
});
|
|
1125
1170
|
var useStickToBottom = (options = {}) => {
|
|
1126
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1127
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1128
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1129
|
-
const optionsRef =
|
|
1171
|
+
const [escapedFromLock, updateEscapedFromLock] = useState5(false);
|
|
1172
|
+
const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
|
|
1173
|
+
const [isNearBottom, setIsNearBottom] = useState5(false);
|
|
1174
|
+
const optionsRef = useRef4(null);
|
|
1130
1175
|
optionsRef.current = options;
|
|
1131
1176
|
const isSelecting = useCallback3(() => {
|
|
1132
1177
|
if (!mouseDown) {
|
|
@@ -1430,11 +1475,11 @@ function mergeAnimations(...animations) {
|
|
|
1430
1475
|
|
|
1431
1476
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1432
1477
|
import * as React from "react";
|
|
1433
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
1478
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect4, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef5 } from "react";
|
|
1434
1479
|
var StickToBottomContext = createContext2(null);
|
|
1435
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1480
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect4;
|
|
1436
1481
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1437
|
-
const customTargetScrollTop =
|
|
1482
|
+
const customTargetScrollTop = useRef5(null);
|
|
1438
1483
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1439
1484
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1440
1485
|
return get?.(target, elements) ?? target;
|
|
@@ -1514,7 +1559,7 @@ import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client"
|
|
|
1514
1559
|
import { useState as useState10 } from "react";
|
|
1515
1560
|
|
|
1516
1561
|
// src/components/AgentLoopBlock.tsx
|
|
1517
|
-
import { useState as
|
|
1562
|
+
import { useState as useState6 } from "react";
|
|
1518
1563
|
|
|
1519
1564
|
// src/components/display-utils.ts
|
|
1520
1565
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1646,7 +1691,7 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1646
1691
|
}
|
|
1647
1692
|
}
|
|
1648
1693
|
function AgentLoopBlock({ toolCall }) {
|
|
1649
|
-
const [expanded, setExpanded] =
|
|
1694
|
+
const [expanded, setExpanded] = useState6(false);
|
|
1650
1695
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1651
1696
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1652
1697
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
@@ -1711,146 +1756,18 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1711
1756
|
|
|
1712
1757
|
// src/components/MarkdownContent.tsx
|
|
1713
1758
|
import {
|
|
1714
|
-
useEffect as
|
|
1759
|
+
useEffect as useEffect5,
|
|
1715
1760
|
useMemo as useMemo5,
|
|
1716
|
-
useRef as
|
|
1761
|
+
useRef as useRef6,
|
|
1717
1762
|
useState as useState7
|
|
1718
1763
|
} from "react";
|
|
1719
|
-
|
|
1720
|
-
// src/lib/media-tags.ts
|
|
1721
|
-
var FILE_CARD_TAG = "blade-file-card";
|
|
1722
|
-
var MEDIA_LINE_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?(?<wrap>[`"'*_]*)MEDIA:[ \t]*(?<path>.+?)\k<wrap>[ \t]*[。,、;;,]?[ \t]*\r?$/gm;
|
|
1723
|
-
var MEDIA_LINE_START_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?[`"'*_]*MEDIA:/;
|
|
1724
|
-
function basename(path) {
|
|
1725
|
-
return path.split(/[/\\]/).filter(Boolean).pop() || path;
|
|
1726
|
-
}
|
|
1727
|
-
function unwrapMediaPath(path) {
|
|
1728
|
-
if (!path.startsWith("<")) return path;
|
|
1729
|
-
const closingBracket = path.indexOf(">");
|
|
1730
|
-
if (closingBracket < 0) return null;
|
|
1731
|
-
return `${path.slice(1, closingBracket)}${path.slice(closingBracket + 1)}`;
|
|
1732
|
-
}
|
|
1733
|
-
function isSafeMediaPath(path) {
|
|
1734
|
-
if (!path) return false;
|
|
1735
|
-
return !path.split(/[/\\]/).includes("..");
|
|
1736
|
-
}
|
|
1737
|
-
function escapeAttribute(value) {
|
|
1738
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1739
|
-
}
|
|
1740
|
-
function foldMediaLines(text) {
|
|
1741
|
-
return text.replace(MEDIA_LINE_RE, (match, _wrap, rawPath) => {
|
|
1742
|
-
const path = unwrapMediaPath(rawPath);
|
|
1743
|
-
if (path === null) return match;
|
|
1744
|
-
const name = basename(path);
|
|
1745
|
-
if (!isSafeMediaPath(path)) {
|
|
1746
|
-
return `\`${name}\``;
|
|
1747
|
-
}
|
|
1748
|
-
return `<${FILE_CARD_TAG} data-path="${escapeAttribute(path)}" data-name="${escapeAttribute(name)}">${escapeAttribute(name)}</${FILE_CARD_TAG}>`;
|
|
1749
|
-
});
|
|
1750
|
-
}
|
|
1751
|
-
function collapseMediaTags(text, options) {
|
|
1752
|
-
if (!text.includes("MEDIA:")) return text;
|
|
1753
|
-
if (!options?.streaming) return foldMediaLines(text);
|
|
1754
|
-
const newlineIndex = text.lastIndexOf("\n");
|
|
1755
|
-
const head = newlineIndex < 0 ? "" : text.slice(0, newlineIndex + 1);
|
|
1756
|
-
const lastLine = newlineIndex < 0 ? text : text.slice(newlineIndex + 1);
|
|
1757
|
-
if (MEDIA_LINE_START_RE.test(lastLine)) {
|
|
1758
|
-
return foldMediaLines(head);
|
|
1759
|
-
}
|
|
1760
|
-
return foldMediaLines(head) + lastLine;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
// src/components/FileCard.tsx
|
|
1764
|
-
import { useState as useState6 } from "react";
|
|
1765
1764
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1766
|
-
var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
|
|
1767
|
-
var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
|
|
1768
|
-
var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
|
|
1769
|
-
var CODE_EXTS = /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx", "py", "go", "json", "yaml", "yml", "html", "css"]);
|
|
1770
|
-
var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "mkv", "avi"]);
|
|
1771
|
-
var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "ogg", "m4a", "flac"]);
|
|
1772
|
-
var ARCHIVE_EXTS = /* @__PURE__ */ new Set(["zip", "tar", "gz", "tgz", "rar", "7z"]);
|
|
1773
|
-
function getFileIcon(name) {
|
|
1774
|
-
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
1775
|
-
if (IMAGE_EXTS.has(ext)) return Image;
|
|
1776
|
-
if (SHEET_EXTS.has(ext)) return FileSpreadsheet;
|
|
1777
|
-
if (TEXT_EXTS.has(ext)) return FileText;
|
|
1778
|
-
if (CODE_EXTS.has(ext)) return FileCode2;
|
|
1779
|
-
if (VIDEO_EXTS.has(ext)) return Film;
|
|
1780
|
-
if (AUDIO_EXTS.has(ext)) return Music;
|
|
1781
|
-
if (ARCHIVE_EXTS.has(ext)) return Archive;
|
|
1782
|
-
return File;
|
|
1783
|
-
}
|
|
1784
|
-
function stringValue(value) {
|
|
1785
|
-
return typeof value === "string" ? value : void 0;
|
|
1786
|
-
}
|
|
1787
|
-
function extractChildrenText(children) {
|
|
1788
|
-
if (typeof children === "string") return children;
|
|
1789
|
-
if (Array.isArray(children)) return children.map(extractChildrenText).join("");
|
|
1790
|
-
if (children && typeof children === "object" && "props" in children) {
|
|
1791
|
-
return extractChildrenText(children.props?.children);
|
|
1792
|
-
}
|
|
1793
|
-
return "";
|
|
1794
|
-
}
|
|
1795
|
-
function FileCard({
|
|
1796
|
-
node,
|
|
1797
|
-
"data-path": pathAttribute,
|
|
1798
|
-
"data-name": nameAttribute,
|
|
1799
|
-
dataPath,
|
|
1800
|
-
dataName,
|
|
1801
|
-
sessionId,
|
|
1802
|
-
children,
|
|
1803
|
-
className,
|
|
1804
|
-
...props
|
|
1805
|
-
}) {
|
|
1806
|
-
const client = useBladeClient();
|
|
1807
|
-
const [downloading, setDownloading] = useState6(false);
|
|
1808
|
-
const [failed, setFailed] = useState6(false);
|
|
1809
|
-
const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
|
|
1810
|
-
const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
|
|
1811
|
-
const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
|
|
1812
|
-
const disabled = !sessionId || !isSafeMediaPath(path);
|
|
1813
|
-
const Icon2 = getFileIcon(name);
|
|
1814
|
-
const handleDownload = async () => {
|
|
1815
|
-
if (disabled || downloading || !sessionId) return;
|
|
1816
|
-
setDownloading(true);
|
|
1817
|
-
setFailed(false);
|
|
1818
|
-
try {
|
|
1819
|
-
await client.sessions.downloadFile(sessionId, path, name);
|
|
1820
|
-
} catch {
|
|
1821
|
-
setFailed(true);
|
|
1822
|
-
} finally {
|
|
1823
|
-
setDownloading(false);
|
|
1824
|
-
}
|
|
1825
|
-
};
|
|
1826
|
-
return /* @__PURE__ */ jsxs6("span", { ...props, className: cn("blade-chat-file-card", className), children: [
|
|
1827
|
-
/* @__PURE__ */ jsxs6(
|
|
1828
|
-
"button",
|
|
1829
|
-
{
|
|
1830
|
-
type: "button",
|
|
1831
|
-
onClick: handleDownload,
|
|
1832
|
-
disabled: disabled || downloading,
|
|
1833
|
-
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
|
|
1834
|
-
title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : `\u4E0B\u8F7D ${name}`,
|
|
1835
|
-
children: [
|
|
1836
|
-
/* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-icon", children: downloading ? /* @__PURE__ */ jsx7(LoaderCircle, { size: 15, className: "animate-spin" }) : /* @__PURE__ */ jsx7(Icon2, { size: 15 }) }),
|
|
1837
|
-
/* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-name", children: name }),
|
|
1838
|
-
/* @__PURE__ */ jsx7(Download, { size: 13, className: "blade-chat-file-card-action" })
|
|
1839
|
-
]
|
|
1840
|
-
}
|
|
1841
|
-
),
|
|
1842
|
-
failed && /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
|
|
1843
|
-
] });
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
// src/components/MarkdownContent.tsx
|
|
1847
|
-
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1848
1765
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1849
1766
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1850
|
-
const preRef =
|
|
1767
|
+
const preRef = useRef6(null);
|
|
1851
1768
|
const [copied, setCopied] = useState7(false);
|
|
1852
1769
|
const [language, setLanguage] = useState7("");
|
|
1853
|
-
|
|
1770
|
+
useEffect5(() => {
|
|
1854
1771
|
const codeEl = preRef.current?.querySelector("code");
|
|
1855
1772
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1856
1773
|
}, []);
|
|
@@ -1861,10 +1778,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1861
1778
|
setTimeout(() => setCopied(false), 2e3);
|
|
1862
1779
|
}
|
|
1863
1780
|
};
|
|
1864
|
-
return /* @__PURE__ */
|
|
1865
|
-
/* @__PURE__ */
|
|
1866
|
-
/* @__PURE__ */
|
|
1867
|
-
/* @__PURE__ */
|
|
1781
|
+
return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
|
|
1782
|
+
/* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
|
|
1783
|
+
/* @__PURE__ */ jsx7("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
|
|
1784
|
+
/* @__PURE__ */ jsxs6(
|
|
1868
1785
|
"button",
|
|
1869
1786
|
{
|
|
1870
1787
|
type: "button",
|
|
@@ -1874,13 +1791,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1874
1791
|
copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
|
|
1875
1792
|
),
|
|
1876
1793
|
children: [
|
|
1877
|
-
copied ? /* @__PURE__ */
|
|
1878
|
-
/* @__PURE__ */
|
|
1794
|
+
copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
|
|
1795
|
+
/* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
|
|
1879
1796
|
]
|
|
1880
1797
|
}
|
|
1881
1798
|
)
|
|
1882
1799
|
] }),
|
|
1883
|
-
/* @__PURE__ */
|
|
1800
|
+
/* @__PURE__ */ jsx7(
|
|
1884
1801
|
"pre",
|
|
1885
1802
|
{
|
|
1886
1803
|
ref: preRef,
|
|
@@ -1892,36 +1809,22 @@ function CodeBlockPre({ children, node: _node, ...props }) {
|
|
|
1892
1809
|
] });
|
|
1893
1810
|
}
|
|
1894
1811
|
function ExternalAnchor({ node: _node, children, ...props }) {
|
|
1895
|
-
return /* @__PURE__ */
|
|
1812
|
+
return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
|
|
1896
1813
|
}
|
|
1897
1814
|
var MARKDOWN_COMPONENTS = {
|
|
1898
1815
|
pre: CodeBlockPre,
|
|
1899
1816
|
a: ExternalAnchor
|
|
1900
1817
|
};
|
|
1901
|
-
var CUSTOM_ALLOWED_TAGS = {
|
|
1902
|
-
[FILE_CARD_TAG]: ["dataPath", "dataName", "data-path", "data-name"]
|
|
1903
|
-
};
|
|
1904
1818
|
function MarkdownContent({ children, className, mode, sessionId }) {
|
|
1905
|
-
const streaming = mode === "streaming";
|
|
1906
1819
|
const resolvedChildren = useMemo5(() => {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
}, [children, sessionId, streaming]);
|
|
1911
|
-
const components = useMemo5(
|
|
1912
|
-
() => ({
|
|
1913
|
-
...MARKDOWN_COMPONENTS,
|
|
1914
|
-
[FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx8(FileCard, { ...fileCardProps, sessionId })
|
|
1915
|
-
}),
|
|
1916
|
-
[sessionId]
|
|
1917
|
-
);
|
|
1918
|
-
return /* @__PURE__ */ jsx8(
|
|
1820
|
+
return children.replace(SYSTEM_REMINDER_RE, "");
|
|
1821
|
+
}, [children]);
|
|
1822
|
+
return /* @__PURE__ */ jsx7(
|
|
1919
1823
|
_r,
|
|
1920
1824
|
{
|
|
1921
1825
|
className: cn("blade-chat-markdown break-words", className),
|
|
1922
1826
|
mode: mode ?? "static",
|
|
1923
|
-
components,
|
|
1924
|
-
allowedTags: CUSTOM_ALLOWED_TAGS,
|
|
1827
|
+
components: MARKDOWN_COMPONENTS,
|
|
1925
1828
|
children: resolvedChildren
|
|
1926
1829
|
},
|
|
1927
1830
|
sessionId ?? "no-session"
|
|
@@ -1929,17 +1832,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
|
|
|
1929
1832
|
}
|
|
1930
1833
|
|
|
1931
1834
|
// src/components/Shimmer.tsx
|
|
1932
|
-
import { jsx as
|
|
1835
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1933
1836
|
function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
1934
|
-
return /* @__PURE__ */
|
|
1837
|
+
return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
|
|
1935
1838
|
}
|
|
1936
1839
|
|
|
1937
1840
|
// src/components/ToolCallBlock.tsx
|
|
1938
1841
|
import { useState as useState9 } from "react";
|
|
1939
1842
|
|
|
1940
1843
|
// src/components/AskUserQuestionBlock.tsx
|
|
1941
|
-
import { useEffect as
|
|
1942
|
-
import { jsx as
|
|
1844
|
+
import { useEffect as useEffect6, useMemo as useMemo6, useState as useState8 } from "react";
|
|
1845
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1943
1846
|
function AskUserQuestionBlock({
|
|
1944
1847
|
data,
|
|
1945
1848
|
answered,
|
|
@@ -1952,7 +1855,7 @@ function AskUserQuestionBlock({
|
|
|
1952
1855
|
const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
|
|
1953
1856
|
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
1954
1857
|
const [submitted, setSubmitted] = useState8(false);
|
|
1955
|
-
|
|
1858
|
+
useEffect6(() => {
|
|
1956
1859
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1957
1860
|
setSubmitted(false);
|
|
1958
1861
|
}
|
|
@@ -2042,7 +1945,7 @@ ${parts.join("\n")}`;
|
|
|
2042
1945
|
setSubmitted(true);
|
|
2043
1946
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
2044
1947
|
};
|
|
2045
|
-
return /* @__PURE__ */
|
|
1948
|
+
return /* @__PURE__ */ jsxs7(
|
|
2046
1949
|
"div",
|
|
2047
1950
|
{
|
|
2048
1951
|
className: cn(
|
|
@@ -2050,12 +1953,12 @@ ${parts.join("\n")}`;
|
|
|
2050
1953
|
answered ? "max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80" : "max-w-lg space-y-5 p-4 text-sm"
|
|
2051
1954
|
),
|
|
2052
1955
|
children: [
|
|
2053
|
-
data.source_loop?.description && /* @__PURE__ */
|
|
1956
|
+
data.source_loop?.description && /* @__PURE__ */ jsxs7("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
2054
1957
|
"\u5B50\u667A\u80FD\u4F53\u300C",
|
|
2055
1958
|
data.source_loop.description,
|
|
2056
1959
|
"\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
|
|
2057
1960
|
] }),
|
|
2058
|
-
data.questions.map((q, qIdx) => /* @__PURE__ */
|
|
1961
|
+
data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
|
|
2059
1962
|
QuestionCard,
|
|
2060
1963
|
{
|
|
2061
1964
|
question: q,
|
|
@@ -2070,7 +1973,7 @@ ${parts.join("\n")}`;
|
|
|
2070
1973
|
},
|
|
2071
1974
|
q.question
|
|
2072
1975
|
)),
|
|
2073
|
-
!answered && !submitted && onAnswer && /* @__PURE__ */
|
|
1976
|
+
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
|
|
2074
1977
|
"button",
|
|
2075
1978
|
{
|
|
2076
1979
|
type: "button",
|
|
@@ -2080,14 +1983,14 @@ ${parts.join("\n")}`;
|
|
|
2080
1983
|
children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
|
|
2081
1984
|
}
|
|
2082
1985
|
),
|
|
2083
|
-
submitted && !answered && /* @__PURE__ */
|
|
1986
|
+
submitted && !answered && /* @__PURE__ */ jsxs7(
|
|
2084
1987
|
"button",
|
|
2085
1988
|
{
|
|
2086
1989
|
type: "button",
|
|
2087
1990
|
disabled: true,
|
|
2088
1991
|
className: "flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80",
|
|
2089
1992
|
children: [
|
|
2090
|
-
/* @__PURE__ */
|
|
1993
|
+
/* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
2091
1994
|
"\u786E\u8BA4\u4E2D"
|
|
2092
1995
|
]
|
|
2093
1996
|
}
|
|
@@ -2108,30 +2011,30 @@ function QuestionCard({
|
|
|
2108
2011
|
onCustomChange
|
|
2109
2012
|
}) {
|
|
2110
2013
|
const multi = question.multiSelect ?? false;
|
|
2111
|
-
return /* @__PURE__ */
|
|
2112
|
-
/* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2014
|
+
return /* @__PURE__ */ jsxs7("div", { children: [
|
|
2015
|
+
/* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
2016
|
+
/* @__PURE__ */ jsx9(
|
|
2114
2017
|
MessageSquareMore,
|
|
2115
2018
|
{
|
|
2116
2019
|
size: answered ? 12 : 13,
|
|
2117
2020
|
className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
|
|
2118
2021
|
}
|
|
2119
2022
|
),
|
|
2120
|
-
/* @__PURE__ */
|
|
2023
|
+
/* @__PURE__ */ jsx9(
|
|
2121
2024
|
"div",
|
|
2122
2025
|
{
|
|
2123
2026
|
className: cn(
|
|
2124
2027
|
"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
|
|
2125
2028
|
answered ? "text-xs" : "text-sm"
|
|
2126
2029
|
),
|
|
2127
|
-
children: /* @__PURE__ */
|
|
2030
|
+
children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
|
|
2128
2031
|
}
|
|
2129
2032
|
)
|
|
2130
2033
|
] }),
|
|
2131
|
-
/* @__PURE__ */
|
|
2034
|
+
/* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
|
|
2132
2035
|
question.options.map((opt, optIdx) => {
|
|
2133
2036
|
const isSel = selected.has(optIdx);
|
|
2134
|
-
return /* @__PURE__ */
|
|
2037
|
+
return /* @__PURE__ */ jsxs7(
|
|
2135
2038
|
"button",
|
|
2136
2039
|
{
|
|
2137
2040
|
type: "button",
|
|
@@ -2145,14 +2048,14 @@ function QuestionCard({
|
|
|
2145
2048
|
answered && "cursor-default opacity-70"
|
|
2146
2049
|
),
|
|
2147
2050
|
children: [
|
|
2148
|
-
multi && /* @__PURE__ */
|
|
2051
|
+
multi && /* @__PURE__ */ jsx9(
|
|
2149
2052
|
"div",
|
|
2150
2053
|
{
|
|
2151
2054
|
className: cn(
|
|
2152
2055
|
"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
|
|
2153
2056
|
isSel && !answered ? "border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]" : isSel ? "border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]" : "border-[hsl(var(--border))]"
|
|
2154
2057
|
),
|
|
2155
|
-
children: isSel && /* @__PURE__ */
|
|
2058
|
+
children: isSel && /* @__PURE__ */ jsx9(
|
|
2156
2059
|
Check,
|
|
2157
2060
|
{
|
|
2158
2061
|
size: 9,
|
|
@@ -2161,9 +2064,9 @@ function QuestionCard({
|
|
|
2161
2064
|
)
|
|
2162
2065
|
}
|
|
2163
2066
|
),
|
|
2164
|
-
/* @__PURE__ */
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
opt.description && /* @__PURE__ */
|
|
2067
|
+
/* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx9("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
|
|
2069
|
+
opt.description && /* @__PURE__ */ jsx9(
|
|
2167
2070
|
"div",
|
|
2168
2071
|
{
|
|
2169
2072
|
className: cn(
|
|
@@ -2180,7 +2083,7 @@ function QuestionCard({
|
|
|
2180
2083
|
opt.label
|
|
2181
2084
|
);
|
|
2182
2085
|
}),
|
|
2183
|
-
answered && !isCustom ? null : /* @__PURE__ */
|
|
2086
|
+
answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
|
|
2184
2087
|
"div",
|
|
2185
2088
|
{
|
|
2186
2089
|
className: cn(
|
|
@@ -2190,8 +2093,8 @@ function QuestionCard({
|
|
|
2190
2093
|
answered && "cursor-default opacity-70"
|
|
2191
2094
|
),
|
|
2192
2095
|
children: [
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2096
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2097
|
+
/* @__PURE__ */ jsx9(
|
|
2195
2098
|
"input",
|
|
2196
2099
|
{
|
|
2197
2100
|
type: "text",
|
|
@@ -2259,7 +2162,7 @@ function normalizeOptionItem(value) {
|
|
|
2259
2162
|
}
|
|
2260
2163
|
|
|
2261
2164
|
// src/components/ToolCallBlock.tsx
|
|
2262
|
-
import { Fragment, jsx as
|
|
2165
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2263
2166
|
function resolveAskQuestionState({
|
|
2264
2167
|
toolStatus,
|
|
2265
2168
|
hasAnswerData,
|
|
@@ -2284,7 +2187,7 @@ function ToolCallBlock({
|
|
|
2284
2187
|
if (renderer) {
|
|
2285
2188
|
const custom = renderer(toolCall);
|
|
2286
2189
|
if (custom !== null && custom !== void 0) {
|
|
2287
|
-
return /* @__PURE__ */
|
|
2190
|
+
return /* @__PURE__ */ jsx10(Fragment, { children: custom });
|
|
2288
2191
|
}
|
|
2289
2192
|
}
|
|
2290
2193
|
if (normalizedName === "AskUserQuestion") {
|
|
@@ -2296,7 +2199,7 @@ function ToolCallBlock({
|
|
|
2296
2199
|
});
|
|
2297
2200
|
const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
|
|
2298
2201
|
if (askData) {
|
|
2299
|
-
return /* @__PURE__ */
|
|
2202
|
+
return /* @__PURE__ */ jsx10(
|
|
2300
2203
|
AskUserQuestionBlock,
|
|
2301
2204
|
{
|
|
2302
2205
|
data: askData,
|
|
@@ -2309,24 +2212,24 @@ function ToolCallBlock({
|
|
|
2309
2212
|
);
|
|
2310
2213
|
}
|
|
2311
2214
|
if (toolCall.status === "pending") {
|
|
2312
|
-
return /* @__PURE__ */
|
|
2313
|
-
/* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
2215
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
|
|
2216
|
+
/* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
|
|
2217
|
+
/* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
|
|
2315
2218
|
] });
|
|
2316
2219
|
}
|
|
2317
|
-
return /* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
/* @__PURE__ */
|
|
2220
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
|
|
2221
|
+
/* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
|
|
2222
|
+
/* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
|
|
2320
2223
|
] });
|
|
2321
2224
|
}
|
|
2322
2225
|
const tone = getToolTone(toolCall.status);
|
|
2323
2226
|
const displayName = getToolDisplayLabel(toolCall);
|
|
2324
2227
|
const toneClass = tone === "red" ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : tone === "amber" ? "border-l-amber-400" : tone === "blue" ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]";
|
|
2325
|
-
const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */
|
|
2228
|
+
const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx10(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx10(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx10(X, { size: 11 }) : /* @__PURE__ */ jsx10(Check, { size: 11 });
|
|
2326
2229
|
const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
|
|
2327
|
-
return /* @__PURE__ */
|
|
2328
|
-
/* @__PURE__ */
|
|
2329
|
-
/* @__PURE__ */
|
|
2230
|
+
return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool ml-4 text-xs", children: [
|
|
2231
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
|
|
2232
|
+
/* @__PURE__ */ jsxs8(
|
|
2330
2233
|
"button",
|
|
2331
2234
|
{
|
|
2332
2235
|
type: "button",
|
|
@@ -2334,7 +2237,7 @@ function ToolCallBlock({
|
|
|
2334
2237
|
className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
|
|
2335
2238
|
"aria-expanded": expanded,
|
|
2336
2239
|
children: [
|
|
2337
|
-
/* @__PURE__ */
|
|
2240
|
+
/* @__PURE__ */ jsx10(
|
|
2338
2241
|
ChevronRight,
|
|
2339
2242
|
{
|
|
2340
2243
|
size: 11,
|
|
@@ -2344,24 +2247,24 @@ function ToolCallBlock({
|
|
|
2344
2247
|
)
|
|
2345
2248
|
}
|
|
2346
2249
|
),
|
|
2347
|
-
/* @__PURE__ */
|
|
2250
|
+
/* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
|
|
2348
2251
|
statusIcon,
|
|
2349
|
-
/* @__PURE__ */
|
|
2252
|
+
/* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
|
|
2350
2253
|
] }),
|
|
2351
|
-
/* @__PURE__ */
|
|
2254
|
+
/* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
|
|
2352
2255
|
]
|
|
2353
2256
|
}
|
|
2354
2257
|
),
|
|
2355
|
-
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */
|
|
2258
|
+
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
|
|
2356
2259
|
] }),
|
|
2357
|
-
expanded && /* @__PURE__ */
|
|
2358
|
-
/* @__PURE__ */
|
|
2359
|
-
/* @__PURE__ */
|
|
2360
|
-
/* @__PURE__ */
|
|
2361
|
-
/* @__PURE__ */
|
|
2362
|
-
toolCall.result != null && /* @__PURE__ */
|
|
2363
|
-
/* @__PURE__ */
|
|
2364
|
-
/* @__PURE__ */
|
|
2260
|
+
expanded && /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
|
|
2261
|
+
/* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
|
|
2262
|
+
/* @__PURE__ */ jsx10("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
|
|
2263
|
+
/* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
|
|
2264
|
+
/* @__PURE__ */ jsx10("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolArgs(toolCall.arguments) }),
|
|
2265
|
+
toolCall.result != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
2266
|
+
/* @__PURE__ */ jsx10("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
2267
|
+
/* @__PURE__ */ jsx10("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
|
|
2365
2268
|
] })
|
|
2366
2269
|
] })
|
|
2367
2270
|
] });
|
|
@@ -2379,11 +2282,11 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2379
2282
|
}
|
|
2380
2283
|
|
|
2381
2284
|
// src/components/AssistantTurnBlock.tsx
|
|
2382
|
-
import { jsx as
|
|
2285
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2383
2286
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2384
2287
|
const [open, setOpen] = useState10(false);
|
|
2385
|
-
return /* @__PURE__ */
|
|
2386
|
-
/* @__PURE__ */
|
|
2288
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
|
|
2289
|
+
/* @__PURE__ */ jsxs9(
|
|
2387
2290
|
"button",
|
|
2388
2291
|
{
|
|
2389
2292
|
type: "button",
|
|
@@ -2391,14 +2294,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
2391
2294
|
"aria-expanded": open,
|
|
2392
2295
|
className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2393
2296
|
children: [
|
|
2394
|
-
/* @__PURE__ */
|
|
2395
|
-
isStreaming ? /* @__PURE__ */
|
|
2396
|
-
/* @__PURE__ */
|
|
2297
|
+
/* @__PURE__ */ jsx11(Brain, { size: 12, className: "shrink-0" }),
|
|
2298
|
+
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
|
|
2299
|
+
/* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
|
|
2397
2300
|
"\xB7 ",
|
|
2398
2301
|
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
2399
2302
|
" \u5B57"
|
|
2400
2303
|
] }),
|
|
2401
|
-
/* @__PURE__ */
|
|
2304
|
+
/* @__PURE__ */ jsx11(
|
|
2402
2305
|
ChevronDown,
|
|
2403
2306
|
{
|
|
2404
2307
|
size: 12,
|
|
@@ -2408,7 +2311,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
|
|
|
2408
2311
|
]
|
|
2409
2312
|
}
|
|
2410
2313
|
),
|
|
2411
|
-
open && /* @__PURE__ */
|
|
2314
|
+
open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
|
|
2412
2315
|
] });
|
|
2413
2316
|
}
|
|
2414
2317
|
function getMessageText(message) {
|
|
@@ -2434,21 +2337,21 @@ function AssistantTurnBlock({
|
|
|
2434
2337
|
(message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
2435
2338
|
);
|
|
2436
2339
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
2437
|
-
return /* @__PURE__ */
|
|
2438
|
-
hasInterrupted && /* @__PURE__ */
|
|
2340
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2341
|
+
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" }),
|
|
2439
2342
|
messages.map((message, index) => {
|
|
2440
2343
|
const isLast = index === messages.length - 1;
|
|
2441
2344
|
const streamingThis = isStreaming && isLast;
|
|
2442
2345
|
const text = getMessageText(message);
|
|
2443
2346
|
const toolCalls = message.tool_calls ?? [];
|
|
2444
2347
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
2445
|
-
return /* @__PURE__ */
|
|
2348
|
+
return /* @__PURE__ */ jsxs9(
|
|
2446
2349
|
"div",
|
|
2447
2350
|
{
|
|
2448
2351
|
className: "flex flex-col gap-3",
|
|
2449
2352
|
children: [
|
|
2450
|
-
showReasoning && message.reasoning && /* @__PURE__ */
|
|
2451
|
-
text && /* @__PURE__ */
|
|
2353
|
+
showReasoning && message.reasoning && /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
|
|
2354
|
+
text && /* @__PURE__ */ jsx11("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx11(
|
|
2452
2355
|
MarkdownContent,
|
|
2453
2356
|
{
|
|
2454
2357
|
mode: streamingThis ? "streaming" : "static",
|
|
@@ -2457,8 +2360,8 @@ function AssistantTurnBlock({
|
|
|
2457
2360
|
children: text
|
|
2458
2361
|
}
|
|
2459
2362
|
) }),
|
|
2460
|
-
toolCalls.length > 0 && /* @__PURE__ */
|
|
2461
|
-
(toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */
|
|
2363
|
+
toolCalls.length > 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-2", children: toolCalls.map(
|
|
2364
|
+
(toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(
|
|
2462
2365
|
ToolCallBlock,
|
|
2463
2366
|
{
|
|
2464
2367
|
toolCall,
|
|
@@ -2476,13 +2379,13 @@ function AssistantTurnBlock({
|
|
|
2476
2379
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2477
2380
|
);
|
|
2478
2381
|
}),
|
|
2479
|
-
isStreaming && !hasAnyContent && /* @__PURE__ */
|
|
2382
|
+
isStreaming && !hasAnyContent && /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
|
|
2480
2383
|
] });
|
|
2481
2384
|
}
|
|
2482
2385
|
|
|
2483
2386
|
// src/components/RenderErrorBoundary.tsx
|
|
2484
2387
|
import { Component } from "react";
|
|
2485
|
-
import { jsx as
|
|
2388
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2486
2389
|
function getFirstComponentName(componentStack) {
|
|
2487
2390
|
const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
|
|
2488
2391
|
return match?.[1] ?? null;
|
|
@@ -2515,23 +2418,408 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2515
2418
|
return children;
|
|
2516
2419
|
}
|
|
2517
2420
|
const componentName = getFirstComponentName(componentStack);
|
|
2518
|
-
return /* @__PURE__ */
|
|
2519
|
-
/* @__PURE__ */
|
|
2520
|
-
/* @__PURE__ */
|
|
2521
|
-
/* @__PURE__ */
|
|
2421
|
+
return /* @__PURE__ */ jsx12("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__ */ jsxs10("div", { className: "flex items-start gap-2", children: [
|
|
2422
|
+
/* @__PURE__ */ jsx12(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
|
|
2423
|
+
/* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
|
|
2424
|
+
/* @__PURE__ */ jsxs10("div", { className: "font-medium", children: [
|
|
2522
2425
|
label,
|
|
2523
2426
|
"\u6E32\u67D3\u5931\u8D25"
|
|
2524
2427
|
] }),
|
|
2525
|
-
/* @__PURE__ */
|
|
2428
|
+
/* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
|
|
2526
2429
|
componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
|
|
2527
2430
|
error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
|
|
2528
2431
|
] }),
|
|
2529
|
-
details ? /* @__PURE__ */
|
|
2432
|
+
details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
|
|
2530
2433
|
] })
|
|
2531
2434
|
] }) });
|
|
2532
2435
|
}
|
|
2533
2436
|
};
|
|
2534
2437
|
|
|
2438
|
+
// src/components/PostChatFollowupBlock.tsx
|
|
2439
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
|
|
2440
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2441
|
+
function emitInteraction(callback, event) {
|
|
2442
|
+
try {
|
|
2443
|
+
callback?.(event);
|
|
2444
|
+
} catch {
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
function basename(path) {
|
|
2448
|
+
return path.split(/[\\/]/).filter(Boolean).pop() || path;
|
|
2449
|
+
}
|
|
2450
|
+
function isVideo(path) {
|
|
2451
|
+
return /\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path);
|
|
2452
|
+
}
|
|
2453
|
+
function ArtifactCard({
|
|
2454
|
+
artifact,
|
|
2455
|
+
sessionId,
|
|
2456
|
+
assistantEntryId,
|
|
2457
|
+
artifactIndex,
|
|
2458
|
+
onInteraction,
|
|
2459
|
+
onArtifactOpened
|
|
2460
|
+
}) {
|
|
2461
|
+
const client = useBladeClient();
|
|
2462
|
+
const [downloading, setDownloading] = useState11(false);
|
|
2463
|
+
const name = artifact.label || basename(artifact.target);
|
|
2464
|
+
if (artifact.kind === "link") {
|
|
2465
|
+
return /* @__PURE__ */ jsxs11(
|
|
2466
|
+
"a",
|
|
2467
|
+
{
|
|
2468
|
+
href: artifact.target,
|
|
2469
|
+
target: "_blank",
|
|
2470
|
+
rel: "noopener noreferrer",
|
|
2471
|
+
onClick: () => onArtifactOpened(artifactIndex, "link"),
|
|
2472
|
+
title: `${name}
|
|
2473
|
+
${artifact.target}`,
|
|
2474
|
+
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))]",
|
|
2475
|
+
children: [
|
|
2476
|
+
/* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
2477
|
+
/* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
2478
|
+
/* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2479
|
+
]
|
|
2480
|
+
}
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
const Icon2 = isVideo(artifact.target) ? Film : File;
|
|
2484
|
+
return /* @__PURE__ */ jsxs11(
|
|
2485
|
+
"button",
|
|
2486
|
+
{
|
|
2487
|
+
type: "button",
|
|
2488
|
+
disabled: !sessionId || downloading,
|
|
2489
|
+
onClick: async () => {
|
|
2490
|
+
if (!sessionId || downloading) return;
|
|
2491
|
+
setDownloading(true);
|
|
2492
|
+
emitInteraction(onInteraction, {
|
|
2493
|
+
type: "artifact_download_started",
|
|
2494
|
+
sessionId,
|
|
2495
|
+
assistantEntryId,
|
|
2496
|
+
artifactIndex,
|
|
2497
|
+
artifactKind: "file"
|
|
2498
|
+
});
|
|
2499
|
+
try {
|
|
2500
|
+
await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target));
|
|
2501
|
+
emitInteraction(onInteraction, {
|
|
2502
|
+
type: "artifact_download_succeeded",
|
|
2503
|
+
sessionId,
|
|
2504
|
+
assistantEntryId,
|
|
2505
|
+
artifactIndex,
|
|
2506
|
+
artifactKind: "file"
|
|
2507
|
+
});
|
|
2508
|
+
} catch {
|
|
2509
|
+
} finally {
|
|
2510
|
+
setDownloading(false);
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
title: name,
|
|
2514
|
+
"aria-label": `\u4E0B\u8F7D ${name}`,
|
|
2515
|
+
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",
|
|
2516
|
+
children: [
|
|
2517
|
+
/* @__PURE__ */ jsx13(Icon2, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
2518
|
+
/* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
2519
|
+
/* @__PURE__ */ jsx13(Download, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2520
|
+
]
|
|
2521
|
+
}
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
var FEEDBACK_REASONS = [
|
|
2525
|
+
{ value: "not_solved", label: "\u6CA1\u89E3\u51B3\u95EE\u9898" },
|
|
2526
|
+
{ value: "inaccurate", label: "\u5185\u5BB9\u4E0D\u51C6\u786E" },
|
|
2527
|
+
{ value: "incomplete", label: "\u5185\u5BB9\u4E0D\u5B8C\u6574" },
|
|
2528
|
+
{ value: "needs_major_changes", label: "\u9700\u8981\u5927\u91CF\u4FEE\u6539" },
|
|
2529
|
+
{ value: "too_slow", label: "\u592A\u6162" },
|
|
2530
|
+
{ value: "other", label: "\u5176\u4ED6" }
|
|
2531
|
+
];
|
|
2532
|
+
function feedbackReasonLabel(reason) {
|
|
2533
|
+
return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null;
|
|
2534
|
+
}
|
|
2535
|
+
function HistoricalResultFeedback({ feedback }) {
|
|
2536
|
+
const label = feedbackReasonLabel(feedback.reason);
|
|
2537
|
+
return /* @__PURE__ */ jsxs11(
|
|
2538
|
+
"section",
|
|
2539
|
+
{
|
|
2540
|
+
"aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
|
|
2541
|
+
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))]",
|
|
2542
|
+
children: [
|
|
2543
|
+
/* @__PURE__ */ jsxs11("span", { children: [
|
|
2544
|
+
"\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
|
|
2545
|
+
feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
|
|
2546
|
+
] }),
|
|
2547
|
+
label ? /* @__PURE__ */ jsxs11("span", { children: [
|
|
2548
|
+
" \xB7 ",
|
|
2549
|
+
label
|
|
2550
|
+
] }) : null
|
|
2551
|
+
]
|
|
2552
|
+
}
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
function ResultFeedback({
|
|
2556
|
+
followup,
|
|
2557
|
+
sessionId,
|
|
2558
|
+
isViewer,
|
|
2559
|
+
onInteraction,
|
|
2560
|
+
savedFeedback,
|
|
2561
|
+
onFeedbackSaved
|
|
2562
|
+
}) {
|
|
2563
|
+
const client = useBladeClient();
|
|
2564
|
+
const [saved, setSaved] = useState11(savedFeedback ?? null);
|
|
2565
|
+
const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
|
|
2566
|
+
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
2567
|
+
const [saving, setSaving] = useState11(false);
|
|
2568
|
+
const [saveError, setSaveError] = useState11(false);
|
|
2569
|
+
const reportedShown = useRef7(false);
|
|
2570
|
+
const latestChoice = useRef7(null);
|
|
2571
|
+
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2572
|
+
useEffect7(() => {
|
|
2573
|
+
if (!eligible || reportedShown.current) return;
|
|
2574
|
+
reportedShown.current = true;
|
|
2575
|
+
emitInteraction(onInteraction, {
|
|
2576
|
+
type: "result_feedback_shown",
|
|
2577
|
+
sessionId,
|
|
2578
|
+
assistantEntryId: followup.assistant_entry_id
|
|
2579
|
+
});
|
|
2580
|
+
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2581
|
+
useEffect7(() => {
|
|
2582
|
+
if (!savedFeedback || latestChoice.current) return;
|
|
2583
|
+
setSaved(savedFeedback);
|
|
2584
|
+
setHelpful(savedFeedback.helpful);
|
|
2585
|
+
setReason(savedFeedback.reason);
|
|
2586
|
+
}, [savedFeedback]);
|
|
2587
|
+
const submit = useCallback5(
|
|
2588
|
+
async (nextHelpful, nextReason) => {
|
|
2589
|
+
if (!sessionId) return;
|
|
2590
|
+
const choice = { helpful: nextHelpful, reason: nextReason };
|
|
2591
|
+
latestChoice.current = choice;
|
|
2592
|
+
setHelpful(nextHelpful);
|
|
2593
|
+
setReason(nextReason);
|
|
2594
|
+
setSaving(true);
|
|
2595
|
+
setSaveError(false);
|
|
2596
|
+
try {
|
|
2597
|
+
const result = await client.sessions.putResultFeedback(
|
|
2598
|
+
sessionId,
|
|
2599
|
+
followup.assistant_entry_id,
|
|
2600
|
+
choice
|
|
2601
|
+
);
|
|
2602
|
+
if (latestChoice.current !== choice) return;
|
|
2603
|
+
setSaved(result);
|
|
2604
|
+
onFeedbackSaved?.(result);
|
|
2605
|
+
emitInteraction(onInteraction, {
|
|
2606
|
+
type: "result_feedback_submitted",
|
|
2607
|
+
sessionId,
|
|
2608
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2609
|
+
helpful: result.helpful,
|
|
2610
|
+
reason: result.reason,
|
|
2611
|
+
updatedAt: result.updated_at
|
|
2612
|
+
});
|
|
2613
|
+
} catch {
|
|
2614
|
+
if (latestChoice.current === choice) setSaveError(true);
|
|
2615
|
+
} finally {
|
|
2616
|
+
if (latestChoice.current === choice) setSaving(false);
|
|
2617
|
+
}
|
|
2618
|
+
},
|
|
2619
|
+
[client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
|
|
2620
|
+
);
|
|
2621
|
+
if (!eligible) return null;
|
|
2622
|
+
return /* @__PURE__ */ jsxs11(
|
|
2623
|
+
"section",
|
|
2624
|
+
{
|
|
2625
|
+
"aria-label": "\u7ED3\u679C\u53CD\u9988",
|
|
2626
|
+
className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
|
|
2627
|
+
children: [
|
|
2628
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
|
|
2629
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
2630
|
+
/* @__PURE__ */ jsx13(
|
|
2631
|
+
"button",
|
|
2632
|
+
{
|
|
2633
|
+
type: "button",
|
|
2634
|
+
"aria-pressed": helpful === true,
|
|
2635
|
+
disabled: saving,
|
|
2636
|
+
onClick: () => void submit(true, null),
|
|
2637
|
+
className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
|
|
2638
|
+
children: "\u6709\u5E2E\u52A9"
|
|
2639
|
+
}
|
|
2640
|
+
),
|
|
2641
|
+
/* @__PURE__ */ jsx13(
|
|
2642
|
+
"button",
|
|
2643
|
+
{
|
|
2644
|
+
type: "button",
|
|
2645
|
+
"aria-pressed": helpful === false,
|
|
2646
|
+
disabled: saving,
|
|
2647
|
+
onClick: () => void submit(false, reason),
|
|
2648
|
+
className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
|
|
2649
|
+
children: "\u6CA1\u5E2E\u52A9"
|
|
2650
|
+
}
|
|
2651
|
+
)
|
|
2652
|
+
] }),
|
|
2653
|
+
helpful === false ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx13(
|
|
2654
|
+
"button",
|
|
2655
|
+
{
|
|
2656
|
+
type: "button",
|
|
2657
|
+
"aria-pressed": reason === item.value,
|
|
2658
|
+
disabled: saving,
|
|
2659
|
+
onClick: () => void submit(false, item.value),
|
|
2660
|
+
className: "rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60",
|
|
2661
|
+
children: item.label
|
|
2662
|
+
},
|
|
2663
|
+
item.value
|
|
2664
|
+
)) }) : null,
|
|
2665
|
+
saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
|
|
2666
|
+
/* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
|
|
2667
|
+
/* @__PURE__ */ jsx13(
|
|
2668
|
+
"button",
|
|
2669
|
+
{
|
|
2670
|
+
type: "button",
|
|
2671
|
+
className: "underline",
|
|
2672
|
+
onClick: () => {
|
|
2673
|
+
const choice = latestChoice.current;
|
|
2674
|
+
if (choice) void submit(choice.helpful, choice.reason);
|
|
2675
|
+
},
|
|
2676
|
+
children: "\u91CD\u8BD5"
|
|
2677
|
+
}
|
|
2678
|
+
)
|
|
2679
|
+
] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
|
|
2680
|
+
]
|
|
2681
|
+
}
|
|
2682
|
+
);
|
|
2683
|
+
}
|
|
2684
|
+
function PostChatFollowupBlock({
|
|
2685
|
+
followup,
|
|
2686
|
+
sessionId,
|
|
2687
|
+
onSuggestion,
|
|
2688
|
+
isViewer = false,
|
|
2689
|
+
onInteraction,
|
|
2690
|
+
savedFeedback,
|
|
2691
|
+
onFeedbackSaved
|
|
2692
|
+
}) {
|
|
2693
|
+
const [expanded, setExpanded] = useState11(false);
|
|
2694
|
+
const adopted = useRef7(/* @__PURE__ */ new Set());
|
|
2695
|
+
const reportedSuggestions = useRef7(false);
|
|
2696
|
+
const reportedArtifacts = useRef7(/* @__PURE__ */ new Set());
|
|
2697
|
+
const openedArtifacts = useRef7(/* @__PURE__ */ new Set());
|
|
2698
|
+
const artifacts = followup.final_artifacts ?? [];
|
|
2699
|
+
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2700
|
+
useEffect7(() => {
|
|
2701
|
+
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2702
|
+
reportedSuggestions.current = true;
|
|
2703
|
+
emitInteraction(onInteraction, {
|
|
2704
|
+
type: "suggestions_shown",
|
|
2705
|
+
sessionId,
|
|
2706
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2707
|
+
count: followup.suggestions.length
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {
|
|
2711
|
+
if (reportedArtifacts.current.has(artifactIndex)) continue;
|
|
2712
|
+
const artifact = visibleArtifacts[artifactIndex];
|
|
2713
|
+
if (!artifact) continue;
|
|
2714
|
+
reportedArtifacts.current.add(artifactIndex);
|
|
2715
|
+
emitInteraction(onInteraction, {
|
|
2716
|
+
type: "artifact_shown",
|
|
2717
|
+
sessionId,
|
|
2718
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2719
|
+
artifactIndex,
|
|
2720
|
+
artifactKind: artifact.kind
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
}, [
|
|
2724
|
+
followup.assistant_entry_id,
|
|
2725
|
+
followup.suggestions.length,
|
|
2726
|
+
onInteraction,
|
|
2727
|
+
sessionId,
|
|
2728
|
+
visibleArtifacts
|
|
2729
|
+
]);
|
|
2730
|
+
const reportArtifactOpened = useCallback5(
|
|
2731
|
+
(artifactIndex, artifactKind) => {
|
|
2732
|
+
if (openedArtifacts.current.has(artifactIndex)) return;
|
|
2733
|
+
openedArtifacts.current.add(artifactIndex);
|
|
2734
|
+
emitInteraction(onInteraction, {
|
|
2735
|
+
type: "artifact_opened",
|
|
2736
|
+
sessionId,
|
|
2737
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2738
|
+
artifactIndex,
|
|
2739
|
+
artifactKind
|
|
2740
|
+
});
|
|
2741
|
+
},
|
|
2742
|
+
[followup.assistant_entry_id, onInteraction, sessionId]
|
|
2743
|
+
);
|
|
2744
|
+
if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
|
|
2745
|
+
return null;
|
|
2746
|
+
return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
|
|
2747
|
+
followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
|
|
2748
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
|
|
2749
|
+
/* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
|
|
2750
|
+
"\u672C\u8F6E\u5C0F\u7ED3"
|
|
2751
|
+
] }),
|
|
2752
|
+
followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
|
|
2753
|
+
artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2754
|
+
/* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
|
|
2755
|
+
ArtifactCard,
|
|
2756
|
+
{
|
|
2757
|
+
artifact,
|
|
2758
|
+
sessionId,
|
|
2759
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2760
|
+
artifactIndex,
|
|
2761
|
+
onInteraction,
|
|
2762
|
+
onArtifactOpened: reportArtifactOpened
|
|
2763
|
+
},
|
|
2764
|
+
`${artifact.kind}:${artifactIndex}`
|
|
2765
|
+
)) }),
|
|
2766
|
+
artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
|
|
2767
|
+
"button",
|
|
2768
|
+
{
|
|
2769
|
+
type: "button",
|
|
2770
|
+
onClick: () => setExpanded((value) => !value),
|
|
2771
|
+
"aria-expanded": expanded,
|
|
2772
|
+
className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
|
|
2773
|
+
children: [
|
|
2774
|
+
expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
|
|
2775
|
+
/* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
|
|
2776
|
+
]
|
|
2777
|
+
}
|
|
2778
|
+
) : null
|
|
2779
|
+
] }) : null
|
|
2780
|
+
] }) : null,
|
|
2781
|
+
followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
|
|
2782
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
|
|
2783
|
+
followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
|
|
2784
|
+
"button",
|
|
2785
|
+
{
|
|
2786
|
+
type: "button",
|
|
2787
|
+
disabled: isViewer,
|
|
2788
|
+
onClick: () => {
|
|
2789
|
+
if (!adopted.current.has(suggestionIndex)) {
|
|
2790
|
+
adopted.current.add(suggestionIndex);
|
|
2791
|
+
emitInteraction(onInteraction, {
|
|
2792
|
+
type: "suggestion_adopted",
|
|
2793
|
+
sessionId,
|
|
2794
|
+
assistantEntryId: followup.assistant_entry_id,
|
|
2795
|
+
suggestionIndex
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
onSuggestion?.(suggestion);
|
|
2799
|
+
},
|
|
2800
|
+
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",
|
|
2801
|
+
children: [
|
|
2802
|
+
/* @__PURE__ */ jsx13("span", { children: suggestion }),
|
|
2803
|
+
/* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
|
|
2804
|
+
]
|
|
2805
|
+
},
|
|
2806
|
+
suggestion
|
|
2807
|
+
))
|
|
2808
|
+
] }) : null,
|
|
2809
|
+
/* @__PURE__ */ jsx13(
|
|
2810
|
+
ResultFeedback,
|
|
2811
|
+
{
|
|
2812
|
+
followup,
|
|
2813
|
+
sessionId,
|
|
2814
|
+
isViewer,
|
|
2815
|
+
onInteraction,
|
|
2816
|
+
savedFeedback,
|
|
2817
|
+
onFeedbackSaved
|
|
2818
|
+
}
|
|
2819
|
+
)
|
|
2820
|
+
] });
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2535
2823
|
// src/components/UserMessageBubble.tsx
|
|
2536
2824
|
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
2537
2825
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
@@ -2617,6 +2905,8 @@ function getMessageResetSignature(messages) {
|
|
|
2617
2905
|
}
|
|
2618
2906
|
function MessageList({
|
|
2619
2907
|
messages,
|
|
2908
|
+
postChatFollowup,
|
|
2909
|
+
onSuggestion,
|
|
2620
2910
|
isStreaming,
|
|
2621
2911
|
sessionStatus,
|
|
2622
2912
|
askAnswers,
|
|
@@ -2624,7 +2914,11 @@ function MessageList({
|
|
|
2624
2914
|
toolCallRenderer,
|
|
2625
2915
|
emptyState,
|
|
2626
2916
|
className,
|
|
2627
|
-
sessionId
|
|
2917
|
+
sessionId,
|
|
2918
|
+
isViewer = false,
|
|
2919
|
+
onFollowupInteraction,
|
|
2920
|
+
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
2921
|
+
onResultFeedbackSaved
|
|
2628
2922
|
}) {
|
|
2629
2923
|
const renderBlocks = useMemo7(() => {
|
|
2630
2924
|
const visible = messages.filter((message) => {
|
|
@@ -2672,7 +2966,7 @@ function MessageList({
|
|
|
2672
2966
|
blocks.push({
|
|
2673
2967
|
type: "message",
|
|
2674
2968
|
message,
|
|
2675
|
-
key: message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2969
|
+
key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2676
2970
|
});
|
|
2677
2971
|
}
|
|
2678
2972
|
flushAssistant();
|
|
@@ -2710,24 +3004,47 @@ function MessageList({
|
|
|
2710
3004
|
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
2711
3005
|
}
|
|
2712
3006
|
if (block.type === "assistant_turn") {
|
|
2713
|
-
|
|
3007
|
+
const blockFeedback = block.messages.map(
|
|
3008
|
+
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
3009
|
+
).find((feedback) => feedback != null);
|
|
3010
|
+
const hasActiveFollowup = Boolean(
|
|
3011
|
+
postChatFollowup && block.messages.some(
|
|
3012
|
+
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
3013
|
+
)
|
|
3014
|
+
);
|
|
3015
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
|
|
2714
3016
|
RenderErrorBoundary,
|
|
2715
3017
|
{
|
|
2716
3018
|
label: "\u52A9\u624B\u6D88\u606F",
|
|
2717
3019
|
details: block.key,
|
|
2718
3020
|
resetKey: getMessageResetSignature(block.messages),
|
|
2719
|
-
children:
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
3021
|
+
children: [
|
|
3022
|
+
/* @__PURE__ */ jsx15(
|
|
3023
|
+
AssistantTurnBlock,
|
|
3024
|
+
{
|
|
3025
|
+
messages: block.messages,
|
|
3026
|
+
isStreaming: block.isStreaming,
|
|
3027
|
+
askAnswers,
|
|
3028
|
+
onAnswer,
|
|
3029
|
+
sessionStatus,
|
|
3030
|
+
toolCallRenderer,
|
|
3031
|
+
sessionId
|
|
3032
|
+
}
|
|
3033
|
+
),
|
|
3034
|
+
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3035
|
+
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
|
|
3036
|
+
PostChatFollowupBlock,
|
|
3037
|
+
{
|
|
3038
|
+
followup: postChatFollowup,
|
|
3039
|
+
sessionId,
|
|
3040
|
+
onSuggestion,
|
|
3041
|
+
isViewer,
|
|
3042
|
+
onInteraction: onFollowupInteraction,
|
|
3043
|
+
savedFeedback: blockFeedback,
|
|
3044
|
+
onFeedbackSaved: onResultFeedbackSaved
|
|
3045
|
+
}
|
|
3046
|
+
) : null
|
|
3047
|
+
]
|
|
2731
3048
|
}
|
|
2732
3049
|
) }, block.key);
|
|
2733
3050
|
}
|
|
@@ -2754,8 +3071,8 @@ function MessageList({
|
|
|
2754
3071
|
}
|
|
2755
3072
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
2756
3073
|
const { scrollToBottom } = useStickToBottomContext();
|
|
2757
|
-
const previousCountRef =
|
|
2758
|
-
|
|
3074
|
+
const previousCountRef = useRef8(userMessageCount);
|
|
3075
|
+
useEffect8(() => {
|
|
2759
3076
|
if (userMessageCount > previousCountRef.current) {
|
|
2760
3077
|
scrollToBottom("instant");
|
|
2761
3078
|
}
|
|
@@ -2765,9 +3082,9 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
2765
3082
|
}
|
|
2766
3083
|
function ScrollToBottomButton() {
|
|
2767
3084
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
2768
|
-
const [visible, setVisible] =
|
|
2769
|
-
const hideTimerRef =
|
|
2770
|
-
|
|
3085
|
+
const [visible, setVisible] = useState12(false);
|
|
3086
|
+
const hideTimerRef = useRef8(null);
|
|
3087
|
+
useEffect8(() => {
|
|
2771
3088
|
if (isAtBottom) {
|
|
2772
3089
|
if (!hideTimerRef.current) {
|
|
2773
3090
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -2789,7 +3106,7 @@ function ScrollToBottomButton() {
|
|
|
2789
3106
|
}
|
|
2790
3107
|
};
|
|
2791
3108
|
}, [isAtBottom]);
|
|
2792
|
-
const handleClick =
|
|
3109
|
+
const handleClick = useCallback6(() => {
|
|
2793
3110
|
if (hideTimerRef.current) {
|
|
2794
3111
|
clearTimeout(hideTimerRef.current);
|
|
2795
3112
|
hideTimerRef.current = null;
|
|
@@ -2837,16 +3154,22 @@ function ChatSurface({
|
|
|
2837
3154
|
connection,
|
|
2838
3155
|
errorMessage,
|
|
2839
3156
|
messages,
|
|
3157
|
+
postChatFollowup,
|
|
2840
3158
|
isStreaming,
|
|
2841
3159
|
isStopping,
|
|
2842
3160
|
inputText,
|
|
2843
3161
|
onInputChange,
|
|
3162
|
+
onSuggestion,
|
|
2844
3163
|
onSend,
|
|
2845
3164
|
onStop,
|
|
2846
3165
|
sessionStatus,
|
|
2847
3166
|
askAnswers,
|
|
2848
3167
|
onAnswer,
|
|
2849
3168
|
sessionId,
|
|
3169
|
+
isViewer,
|
|
3170
|
+
resultFeedbackByEntry,
|
|
3171
|
+
onResultFeedbackSaved,
|
|
3172
|
+
onFollowupInteraction,
|
|
2850
3173
|
beforeInput,
|
|
2851
3174
|
banner
|
|
2852
3175
|
}) {
|
|
@@ -2870,6 +3193,8 @@ function ChatSurface({
|
|
|
2870
3193
|
MessageList,
|
|
2871
3194
|
{
|
|
2872
3195
|
messages,
|
|
3196
|
+
postChatFollowup,
|
|
3197
|
+
onSuggestion,
|
|
2873
3198
|
isStreaming,
|
|
2874
3199
|
sessionStatus,
|
|
2875
3200
|
askAnswers,
|
|
@@ -2877,7 +3202,11 @@ function ChatSurface({
|
|
|
2877
3202
|
toolCallRenderer: renderers?.toolCall,
|
|
2878
3203
|
emptyState: slots?.emptyState,
|
|
2879
3204
|
className: classNames?.messageList,
|
|
2880
|
-
sessionId
|
|
3205
|
+
sessionId,
|
|
3206
|
+
isViewer,
|
|
3207
|
+
resultFeedbackByEntry,
|
|
3208
|
+
onResultFeedbackSaved,
|
|
3209
|
+
onFollowupInteraction
|
|
2881
3210
|
}
|
|
2882
3211
|
),
|
|
2883
3212
|
beforeInput,
|
|
@@ -2901,13 +3230,13 @@ function ChatSurface({
|
|
|
2901
3230
|
}
|
|
2902
3231
|
|
|
2903
3232
|
// src/components/AgentChat.tsx
|
|
2904
|
-
import { Fragment as
|
|
3233
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2905
3234
|
function isUnauthorizedError(error) {
|
|
2906
3235
|
return error instanceof BladeApiError && error.status === 401;
|
|
2907
3236
|
}
|
|
2908
3237
|
function LoginCard({ client, onLoggedIn }) {
|
|
2909
|
-
const [loggingIn, setLoggingIn] =
|
|
2910
|
-
const [loginError, setLoginError] =
|
|
3238
|
+
const [loggingIn, setLoggingIn] = useState13(false);
|
|
3239
|
+
const [loginError, setLoginError] = useState13(null);
|
|
2911
3240
|
const handleLogin = async () => {
|
|
2912
3241
|
setLoggingIn(true);
|
|
2913
3242
|
setLoginError(null);
|
|
@@ -2939,8 +3268,8 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
2939
3268
|
}
|
|
2940
3269
|
function AgentChat(props) {
|
|
2941
3270
|
const client = useBladeClient();
|
|
2942
|
-
const [attempt, setAttempt] =
|
|
2943
|
-
const [needLogin, setNeedLogin] =
|
|
3271
|
+
const [attempt, setAttempt] = useState13(0);
|
|
3272
|
+
const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
|
|
2944
3273
|
if (needLogin) {
|
|
2945
3274
|
return /* @__PURE__ */ jsx17(
|
|
2946
3275
|
"div",
|
|
@@ -2973,21 +3302,61 @@ function ChatSessionView({
|
|
|
2973
3302
|
slots,
|
|
2974
3303
|
placeholder,
|
|
2975
3304
|
theme,
|
|
3305
|
+
onFollowupInteraction,
|
|
2976
3306
|
onUnauthorized
|
|
2977
3307
|
}) {
|
|
3308
|
+
const client = useBladeClient();
|
|
2978
3309
|
const { session, state, error } = useAgentSession(sessionId, {
|
|
2979
3310
|
createOptions,
|
|
2980
3311
|
onSessionCreated
|
|
2981
3312
|
});
|
|
2982
3313
|
const replay = useReplay(session);
|
|
2983
|
-
const [stopRequested, setStopRequested] =
|
|
2984
|
-
const [inputText, setInputText] =
|
|
2985
|
-
|
|
3314
|
+
const [stopRequested, setStopRequested] = useState13(false);
|
|
3315
|
+
const [inputText, setInputText] = useState13("");
|
|
3316
|
+
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3317
|
+
const resolvedSessionId = session?.sessionId;
|
|
3318
|
+
const isViewer = state?.viewerRole === "viewer";
|
|
3319
|
+
useEffect9(() => {
|
|
3320
|
+
setResultFeedback([]);
|
|
3321
|
+
if (!resolvedSessionId || isViewer) return;
|
|
3322
|
+
let cancelled = false;
|
|
3323
|
+
void client.sessions.listResultFeedback(resolvedSessionId).then(
|
|
3324
|
+
(items) => {
|
|
3325
|
+
if (cancelled) return;
|
|
3326
|
+
setResultFeedback((current) => {
|
|
3327
|
+
const merged = new Map(items.map((item) => [item.assistant_entry_id, item]));
|
|
3328
|
+
for (const item of current) {
|
|
3329
|
+
const fetched = merged.get(item.assistant_entry_id);
|
|
3330
|
+
if (!fetched || fetched.updated_at < item.updated_at) {
|
|
3331
|
+
merged.set(item.assistant_entry_id, item);
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
return [...merged.values()];
|
|
3335
|
+
});
|
|
3336
|
+
},
|
|
3337
|
+
() => {
|
|
3338
|
+
}
|
|
3339
|
+
);
|
|
3340
|
+
return () => {
|
|
3341
|
+
cancelled = true;
|
|
3342
|
+
};
|
|
3343
|
+
}, [client, isViewer, resolvedSessionId]);
|
|
3344
|
+
const resultFeedbackByEntry = useMemo8(
|
|
3345
|
+
() => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
|
|
3346
|
+
[resultFeedback]
|
|
3347
|
+
);
|
|
3348
|
+
const handleResultFeedbackSaved = useCallback7((saved) => {
|
|
3349
|
+
setResultFeedback((current) => [
|
|
3350
|
+
...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
|
|
3351
|
+
saved
|
|
3352
|
+
]);
|
|
3353
|
+
}, []);
|
|
3354
|
+
useEffect9(() => {
|
|
2986
3355
|
if (session) {
|
|
2987
3356
|
onSessionReady?.(session);
|
|
2988
3357
|
}
|
|
2989
3358
|
}, [session, onSessionReady]);
|
|
2990
|
-
|
|
3359
|
+
useEffect9(() => {
|
|
2991
3360
|
if (!session) return;
|
|
2992
3361
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
2993
3362
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3003,12 +3372,12 @@ ${content}`);
|
|
|
3003
3372
|
offInsert();
|
|
3004
3373
|
};
|
|
3005
3374
|
}, [session]);
|
|
3006
|
-
|
|
3375
|
+
useEffect9(() => {
|
|
3007
3376
|
if (isUnauthorizedError(error)) {
|
|
3008
3377
|
onUnauthorized();
|
|
3009
3378
|
}
|
|
3010
3379
|
}, [error, onUnauthorized]);
|
|
3011
|
-
|
|
3380
|
+
useEffect9(() => {
|
|
3012
3381
|
if (!session || !commands) return;
|
|
3013
3382
|
const unsubscribes = Object.entries(commands).map(
|
|
3014
3383
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3033,12 +3402,13 @@ ${content}`);
|
|
|
3033
3402
|
ChatSurface,
|
|
3034
3403
|
{
|
|
3035
3404
|
theme,
|
|
3405
|
+
onFollowupInteraction,
|
|
3036
3406
|
classNames,
|
|
3037
3407
|
renderers,
|
|
3038
3408
|
slots,
|
|
3039
3409
|
placeholder,
|
|
3040
3410
|
connection: state?.connection ?? "connecting",
|
|
3041
|
-
banner: /* @__PURE__ */ jsxs15(
|
|
3411
|
+
banner: /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
3042
3412
|
/* @__PURE__ */ jsx17(
|
|
3043
3413
|
ReplayBar,
|
|
3044
3414
|
{
|
|
@@ -3053,10 +3423,14 @@ ${content}`);
|
|
|
3053
3423
|
] }),
|
|
3054
3424
|
errorMessage,
|
|
3055
3425
|
messages: state?.messages ?? [],
|
|
3426
|
+
postChatFollowup: latestPostChatFollowup(state?.turns ?? []),
|
|
3427
|
+
resultFeedbackByEntry,
|
|
3428
|
+
onResultFeedbackSaved: handleResultFeedbackSaved,
|
|
3056
3429
|
isStreaming,
|
|
3057
3430
|
isStopping,
|
|
3058
3431
|
inputText,
|
|
3059
3432
|
onInputChange: setInputText,
|
|
3433
|
+
onSuggestion: setInputText,
|
|
3060
3434
|
onSend: handleSend,
|
|
3061
3435
|
onStop: handleStop,
|
|
3062
3436
|
sessionStatus: state?.status ?? void 0,
|
|
@@ -3067,16 +3441,17 @@ ${content}`);
|
|
|
3067
3441
|
askUserAnswer: { tool_call_id: toolCallId, ...answerData }
|
|
3068
3442
|
});
|
|
3069
3443
|
},
|
|
3070
|
-
sessionId:
|
|
3444
|
+
sessionId: resolvedSessionId,
|
|
3445
|
+
isViewer
|
|
3071
3446
|
}
|
|
3072
3447
|
);
|
|
3073
3448
|
}
|
|
3074
3449
|
|
|
3075
3450
|
// src/components/LlmChat.tsx
|
|
3076
|
-
import { useEffect as
|
|
3451
|
+
import { useEffect as useEffect10, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3077
3452
|
|
|
3078
3453
|
// src/components/LlmAdvancedSettings.tsx
|
|
3079
|
-
import { useState as
|
|
3454
|
+
import { useState as useState14 } from "react";
|
|
3080
3455
|
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3081
3456
|
var FIELDS = [
|
|
3082
3457
|
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
@@ -3123,8 +3498,8 @@ function writeOverride(settings, baseURL, override) {
|
|
|
3123
3498
|
}
|
|
3124
3499
|
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3125
3500
|
const normalized = normalizeAdvanced(settings);
|
|
3126
|
-
const [open, setOpen] =
|
|
3127
|
-
const [draft, setDraft] =
|
|
3501
|
+
const [open, setOpen] = useState14(false);
|
|
3502
|
+
const [draft, setDraft] = useState14(override);
|
|
3128
3503
|
if (!normalized) return null;
|
|
3129
3504
|
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3130
3505
|
const dirty = Object.keys(override).length > 0;
|
|
@@ -3207,12 +3582,12 @@ function LlmChat({
|
|
|
3207
3582
|
onOverrideChange,
|
|
3208
3583
|
...options
|
|
3209
3584
|
}) {
|
|
3210
|
-
const [override, setOverride] =
|
|
3585
|
+
const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
|
|
3211
3586
|
const effective = { ...options, ...override };
|
|
3212
3587
|
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3213
|
-
const [inputText, setInputText] =
|
|
3214
|
-
const [stopRequested, setStopRequested] =
|
|
3215
|
-
const handle =
|
|
3588
|
+
const [inputText, setInputText] = useState15("");
|
|
3589
|
+
const [stopRequested, setStopRequested] = useState15(false);
|
|
3590
|
+
const handle = useMemo9(
|
|
3216
3591
|
() => ({
|
|
3217
3592
|
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
3218
3593
|
${text}` : text),
|
|
@@ -3221,7 +3596,7 @@ ${text}` : text),
|
|
|
3221
3596
|
}),
|
|
3222
3597
|
[send, reset]
|
|
3223
3598
|
);
|
|
3224
|
-
|
|
3599
|
+
useEffect10(() => {
|
|
3225
3600
|
onReady?.(handle);
|
|
3226
3601
|
}, [handle, onReady]);
|
|
3227
3602
|
return /* @__PURE__ */ jsx19(
|
|
@@ -3311,7 +3686,8 @@ lucide-react/dist/esm/shared/src/utils.js:
|
|
|
3311
3686
|
lucide-react/dist/esm/defaultAttributes.js:
|
|
3312
3687
|
lucide-react/dist/esm/Icon.js:
|
|
3313
3688
|
lucide-react/dist/esm/createLucideIcon.js:
|
|
3314
|
-
lucide-react/dist/esm/icons/
|
|
3689
|
+
lucide-react/dist/esm/icons/arrow-right.js:
|
|
3690
|
+
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
3315
3691
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
3316
3692
|
lucide-react/dist/esm/icons/bot.js:
|
|
3317
3693
|
lucide-react/dist/esm/icons/brain.js:
|
|
@@ -3321,21 +3697,19 @@ lucide-react/dist/esm/icons/chevron-right.js:
|
|
|
3321
3697
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3322
3698
|
lucide-react/dist/esm/icons/copy.js:
|
|
3323
3699
|
lucide-react/dist/esm/icons/download.js:
|
|
3324
|
-
lucide-react/dist/esm/icons/file-code-2.js:
|
|
3325
|
-
lucide-react/dist/esm/icons/file-spreadsheet.js:
|
|
3326
3700
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3327
3701
|
lucide-react/dist/esm/icons/file.js:
|
|
3328
3702
|
lucide-react/dist/esm/icons/film.js:
|
|
3329
|
-
lucide-react/dist/esm/icons/
|
|
3703
|
+
lucide-react/dist/esm/icons/globe.js:
|
|
3330
3704
|
lucide-react/dist/esm/icons/layers.js:
|
|
3331
3705
|
lucide-react/dist/esm/icons/lightbulb.js:
|
|
3332
3706
|
lucide-react/dist/esm/icons/loader-circle.js:
|
|
3333
3707
|
lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
3334
3708
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
3335
3709
|
lucide-react/dist/esm/icons/message-square.js:
|
|
3336
|
-
lucide-react/dist/esm/icons/music.js:
|
|
3337
3710
|
lucide-react/dist/esm/icons/play.js:
|
|
3338
3711
|
lucide-react/dist/esm/icons/settings-2.js:
|
|
3712
|
+
lucide-react/dist/esm/icons/sparkles.js:
|
|
3339
3713
|
lucide-react/dist/esm/icons/square.js:
|
|
3340
3714
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
3341
3715
|
lucide-react/dist/esm/icons/x.js:
|