@blade-hq/agent-react 2610.0.0-beta.5 → 2610.0.0-beta.7
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/dist/components/ConnectionBanner.d.ts +1 -1
- package/dist/embed/entry.d.ts +1 -0
- package/dist/index.js +130 -80
- package/dist/index.js.map +1 -1
- package/dist/style.css +14 -0
- package/dist/style.full.css +15 -1
- package/package.json +2 -2
- package/public-api.md +9 -1
|
@@ -3,6 +3,6 @@ interface Props {
|
|
|
3
3
|
connection: ConnectionStatus;
|
|
4
4
|
className?: string;
|
|
5
5
|
}
|
|
6
|
-
/**
|
|
6
|
+
/** 连接状态提示条:短暂断线静默,超过容错时间后再逐级提示。 */
|
|
7
7
|
export declare function ConnectionBanner({ connection, className }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
8
8
|
export {};
|
package/dist/embed/entry.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ declare const BladeAgent: {
|
|
|
24
24
|
SessionHub: typeof agentClient.SessionHub;
|
|
25
25
|
SessionSetupError: typeof agentClient.SessionSetupError;
|
|
26
26
|
createInitialSessionState: typeof agentClient.createInitialSessionState;
|
|
27
|
+
reconcileOptimisticUserTurns: typeof agentClient.reconcileOptimisticUserTurns;
|
|
27
28
|
toReplaySnapshot: typeof agentClient.toReplaySnapshot;
|
|
28
29
|
connectEmbedded: typeof agentClient.connectEmbedded;
|
|
29
30
|
isCommandEnvelope: typeof agentClient.isCommandEnvelope;
|
package/dist/index.js
CHANGED
|
@@ -854,7 +854,7 @@ var X = createLucideIcon("X", [
|
|
|
854
854
|
]);
|
|
855
855
|
|
|
856
856
|
// src/components/AgentChat.tsx
|
|
857
|
-
import { useCallback as useCallback7, useEffect as
|
|
857
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo8, useState as useState13 } from "react";
|
|
858
858
|
|
|
859
859
|
// src/lib/utils.ts
|
|
860
860
|
function cn(...inputs) {
|
|
@@ -1052,24 +1052,74 @@ function ChatInput({
|
|
|
1052
1052
|
}
|
|
1053
1053
|
|
|
1054
1054
|
// src/components/ConnectionBanner.tsx
|
|
1055
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
|
|
1055
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
|
+
}
|
|
1056
1103
|
function ConnectionBanner({ connection, className }) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
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;
|
|
1061
1111
|
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
1062
1112
|
"div",
|
|
1063
1113
|
{
|
|
1064
1114
|
className: cn(
|
|
1065
1115
|
"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
|
|
1066
|
-
|
|
1116
|
+
recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
1067
1117
|
),
|
|
1068
1118
|
children: [
|
|
1069
|
-
/* @__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 }) }),
|
|
1070
1120
|
/* @__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: "\
|
|
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" })
|
|
1073
1123
|
] })
|
|
1074
1124
|
]
|
|
1075
1125
|
}
|
|
@@ -1078,10 +1128,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1078
1128
|
|
|
1079
1129
|
// src/components/MessageList.tsx
|
|
1080
1130
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1081
|
-
import { useCallback as useCallback6, useEffect as
|
|
1131
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState12 } from "react";
|
|
1082
1132
|
|
|
1083
1133
|
// ../../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 useCallback3, useMemo as useMemo3, useRef as
|
|
1134
|
+
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
1085
1135
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1086
1136
|
/**
|
|
1087
1137
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1118,10 +1168,10 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1118
1168
|
mouseDown = false;
|
|
1119
1169
|
});
|
|
1120
1170
|
var useStickToBottom = (options = {}) => {
|
|
1121
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1122
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1123
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1124
|
-
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);
|
|
1125
1175
|
optionsRef.current = options;
|
|
1126
1176
|
const isSelecting = useCallback3(() => {
|
|
1127
1177
|
if (!mouseDown) {
|
|
@@ -1425,11 +1475,11 @@ function mergeAnimations(...animations) {
|
|
|
1425
1475
|
|
|
1426
1476
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1427
1477
|
import * as React from "react";
|
|
1428
|
-
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";
|
|
1429
1479
|
var StickToBottomContext = createContext2(null);
|
|
1430
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1480
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect4;
|
|
1431
1481
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1432
|
-
const customTargetScrollTop =
|
|
1482
|
+
const customTargetScrollTop = useRef5(null);
|
|
1433
1483
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1434
1484
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1435
1485
|
return get?.(target, elements) ?? target;
|
|
@@ -1506,10 +1556,10 @@ function useStickToBottomContext() {
|
|
|
1506
1556
|
|
|
1507
1557
|
// src/components/AssistantTurnBlock.tsx
|
|
1508
1558
|
import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
|
|
1509
|
-
import { useState as
|
|
1559
|
+
import { useState as useState10 } from "react";
|
|
1510
1560
|
|
|
1511
1561
|
// src/components/AgentLoopBlock.tsx
|
|
1512
|
-
import { useState as
|
|
1562
|
+
import { useState as useState6 } from "react";
|
|
1513
1563
|
|
|
1514
1564
|
// src/components/display-utils.ts
|
|
1515
1565
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1641,7 +1691,7 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1641
1691
|
}
|
|
1642
1692
|
}
|
|
1643
1693
|
function AgentLoopBlock({ toolCall }) {
|
|
1644
|
-
const [expanded, setExpanded] =
|
|
1694
|
+
const [expanded, setExpanded] = useState6(false);
|
|
1645
1695
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1646
1696
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1647
1697
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
@@ -1706,18 +1756,18 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1706
1756
|
|
|
1707
1757
|
// src/components/MarkdownContent.tsx
|
|
1708
1758
|
import {
|
|
1709
|
-
useEffect as
|
|
1759
|
+
useEffect as useEffect5,
|
|
1710
1760
|
useMemo as useMemo5,
|
|
1711
|
-
useRef as
|
|
1712
|
-
useState as
|
|
1761
|
+
useRef as useRef6,
|
|
1762
|
+
useState as useState7
|
|
1713
1763
|
} from "react";
|
|
1714
1764
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1715
1765
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1716
1766
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1717
|
-
const preRef =
|
|
1718
|
-
const [copied, setCopied] =
|
|
1719
|
-
const [language, setLanguage] =
|
|
1720
|
-
|
|
1767
|
+
const preRef = useRef6(null);
|
|
1768
|
+
const [copied, setCopied] = useState7(false);
|
|
1769
|
+
const [language, setLanguage] = useState7("");
|
|
1770
|
+
useEffect5(() => {
|
|
1721
1771
|
const codeEl = preRef.current?.querySelector("code");
|
|
1722
1772
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1723
1773
|
}, []);
|
|
@@ -1788,10 +1838,10 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
1788
1838
|
}
|
|
1789
1839
|
|
|
1790
1840
|
// src/components/ToolCallBlock.tsx
|
|
1791
|
-
import { useState as
|
|
1841
|
+
import { useState as useState9 } from "react";
|
|
1792
1842
|
|
|
1793
1843
|
// src/components/AskUserQuestionBlock.tsx
|
|
1794
|
-
import { useEffect as
|
|
1844
|
+
import { useEffect as useEffect6, useMemo as useMemo6, useState as useState8 } from "react";
|
|
1795
1845
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1796
1846
|
function AskUserQuestionBlock({
|
|
1797
1847
|
data,
|
|
@@ -1801,11 +1851,11 @@ function AskUserQuestionBlock({
|
|
|
1801
1851
|
answerData,
|
|
1802
1852
|
onAnswer
|
|
1803
1853
|
}) {
|
|
1804
|
-
const [selections, setSelections] =
|
|
1805
|
-
const [customTexts, setCustomTexts] =
|
|
1806
|
-
const [usingCustom, setUsingCustom] =
|
|
1807
|
-
const [submitted, setSubmitted] =
|
|
1808
|
-
|
|
1854
|
+
const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
|
|
1855
|
+
const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
|
|
1856
|
+
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
1857
|
+
const [submitted, setSubmitted] = useState8(false);
|
|
1858
|
+
useEffect6(() => {
|
|
1809
1859
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1810
1860
|
setSubmitted(false);
|
|
1811
1861
|
}
|
|
@@ -2132,7 +2182,7 @@ function ToolCallBlock({
|
|
|
2132
2182
|
sessionStatus,
|
|
2133
2183
|
renderer
|
|
2134
2184
|
}) {
|
|
2135
|
-
const [expanded, setExpanded] =
|
|
2185
|
+
const [expanded, setExpanded] = useState9(false);
|
|
2136
2186
|
const normalizedName = formatToolName(toolCall.name);
|
|
2137
2187
|
if (renderer) {
|
|
2138
2188
|
const custom = renderer(toolCall);
|
|
@@ -2234,7 +2284,7 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2234
2284
|
// src/components/AssistantTurnBlock.tsx
|
|
2235
2285
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2236
2286
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2237
|
-
const [open, setOpen] =
|
|
2287
|
+
const [open, setOpen] = useState10(false);
|
|
2238
2288
|
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
|
|
2239
2289
|
/* @__PURE__ */ jsxs9(
|
|
2240
2290
|
"button",
|
|
@@ -2386,7 +2436,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2386
2436
|
};
|
|
2387
2437
|
|
|
2388
2438
|
// src/components/PostChatFollowupBlock.tsx
|
|
2389
|
-
import { useCallback as useCallback5, useEffect as
|
|
2439
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
|
|
2390
2440
|
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2391
2441
|
function emitInteraction(callback, event) {
|
|
2392
2442
|
try {
|
|
@@ -2409,7 +2459,7 @@ function ArtifactCard({
|
|
|
2409
2459
|
onArtifactOpened
|
|
2410
2460
|
}) {
|
|
2411
2461
|
const client = useBladeClient();
|
|
2412
|
-
const [downloading, setDownloading] =
|
|
2462
|
+
const [downloading, setDownloading] = useState11(false);
|
|
2413
2463
|
const name = artifact.label || basename(artifact.target);
|
|
2414
2464
|
if (artifact.kind === "link") {
|
|
2415
2465
|
return /* @__PURE__ */ jsxs11(
|
|
@@ -2511,15 +2561,15 @@ function ResultFeedback({
|
|
|
2511
2561
|
onFeedbackSaved
|
|
2512
2562
|
}) {
|
|
2513
2563
|
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 =
|
|
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);
|
|
2521
2571
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2522
|
-
|
|
2572
|
+
useEffect7(() => {
|
|
2523
2573
|
if (!eligible || reportedShown.current) return;
|
|
2524
2574
|
reportedShown.current = true;
|
|
2525
2575
|
emitInteraction(onInteraction, {
|
|
@@ -2528,7 +2578,7 @@ function ResultFeedback({
|
|
|
2528
2578
|
assistantEntryId: followup.assistant_entry_id
|
|
2529
2579
|
});
|
|
2530
2580
|
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2531
|
-
|
|
2581
|
+
useEffect7(() => {
|
|
2532
2582
|
if (!savedFeedback || latestChoice.current) return;
|
|
2533
2583
|
setSaved(savedFeedback);
|
|
2534
2584
|
setHelpful(savedFeedback.helpful);
|
|
@@ -2640,14 +2690,14 @@ function PostChatFollowupBlock({
|
|
|
2640
2690
|
savedFeedback,
|
|
2641
2691
|
onFeedbackSaved
|
|
2642
2692
|
}) {
|
|
2643
|
-
const [expanded, setExpanded] =
|
|
2644
|
-
const adopted =
|
|
2645
|
-
const reportedSuggestions =
|
|
2646
|
-
const reportedArtifacts =
|
|
2647
|
-
const openedArtifacts =
|
|
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());
|
|
2648
2698
|
const artifacts = followup.final_artifacts ?? [];
|
|
2649
2699
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2650
|
-
|
|
2700
|
+
useEffect7(() => {
|
|
2651
2701
|
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2652
2702
|
reportedSuggestions.current = true;
|
|
2653
2703
|
emitInteraction(onInteraction, {
|
|
@@ -2916,7 +2966,7 @@ function MessageList({
|
|
|
2916
2966
|
blocks.push({
|
|
2917
2967
|
type: "message",
|
|
2918
2968
|
message,
|
|
2919
|
-
key: message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2969
|
+
key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2920
2970
|
});
|
|
2921
2971
|
}
|
|
2922
2972
|
flushAssistant();
|
|
@@ -3021,8 +3071,8 @@ function MessageList({
|
|
|
3021
3071
|
}
|
|
3022
3072
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
3023
3073
|
const { scrollToBottom } = useStickToBottomContext();
|
|
3024
|
-
const previousCountRef =
|
|
3025
|
-
|
|
3074
|
+
const previousCountRef = useRef8(userMessageCount);
|
|
3075
|
+
useEffect8(() => {
|
|
3026
3076
|
if (userMessageCount > previousCountRef.current) {
|
|
3027
3077
|
scrollToBottom("instant");
|
|
3028
3078
|
}
|
|
@@ -3032,9 +3082,9 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
3032
3082
|
}
|
|
3033
3083
|
function ScrollToBottomButton() {
|
|
3034
3084
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3035
|
-
const [visible, setVisible] =
|
|
3036
|
-
const hideTimerRef =
|
|
3037
|
-
|
|
3085
|
+
const [visible, setVisible] = useState12(false);
|
|
3086
|
+
const hideTimerRef = useRef8(null);
|
|
3087
|
+
useEffect8(() => {
|
|
3038
3088
|
if (isAtBottom) {
|
|
3039
3089
|
if (!hideTimerRef.current) {
|
|
3040
3090
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3185,8 +3235,8 @@ function isUnauthorizedError(error) {
|
|
|
3185
3235
|
return error instanceof BladeApiError && error.status === 401;
|
|
3186
3236
|
}
|
|
3187
3237
|
function LoginCard({ client, onLoggedIn }) {
|
|
3188
|
-
const [loggingIn, setLoggingIn] =
|
|
3189
|
-
const [loginError, setLoginError] =
|
|
3238
|
+
const [loggingIn, setLoggingIn] = useState13(false);
|
|
3239
|
+
const [loginError, setLoginError] = useState13(null);
|
|
3190
3240
|
const handleLogin = async () => {
|
|
3191
3241
|
setLoggingIn(true);
|
|
3192
3242
|
setLoginError(null);
|
|
@@ -3218,8 +3268,8 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3218
3268
|
}
|
|
3219
3269
|
function AgentChat(props) {
|
|
3220
3270
|
const client = useBladeClient();
|
|
3221
|
-
const [attempt, setAttempt] =
|
|
3222
|
-
const [needLogin, setNeedLogin] =
|
|
3271
|
+
const [attempt, setAttempt] = useState13(0);
|
|
3272
|
+
const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
|
|
3223
3273
|
if (needLogin) {
|
|
3224
3274
|
return /* @__PURE__ */ jsx17(
|
|
3225
3275
|
"div",
|
|
@@ -3261,12 +3311,12 @@ function ChatSessionView({
|
|
|
3261
3311
|
onSessionCreated
|
|
3262
3312
|
});
|
|
3263
3313
|
const replay = useReplay(session);
|
|
3264
|
-
const [stopRequested, setStopRequested] =
|
|
3265
|
-
const [inputText, setInputText] =
|
|
3266
|
-
const [resultFeedback, setResultFeedback] =
|
|
3314
|
+
const [stopRequested, setStopRequested] = useState13(false);
|
|
3315
|
+
const [inputText, setInputText] = useState13("");
|
|
3316
|
+
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3267
3317
|
const resolvedSessionId = session?.sessionId;
|
|
3268
3318
|
const isViewer = state?.viewerRole === "viewer";
|
|
3269
|
-
|
|
3319
|
+
useEffect9(() => {
|
|
3270
3320
|
setResultFeedback([]);
|
|
3271
3321
|
if (!resolvedSessionId || isViewer) return;
|
|
3272
3322
|
let cancelled = false;
|
|
@@ -3301,12 +3351,12 @@ function ChatSessionView({
|
|
|
3301
3351
|
saved
|
|
3302
3352
|
]);
|
|
3303
3353
|
}, []);
|
|
3304
|
-
|
|
3354
|
+
useEffect9(() => {
|
|
3305
3355
|
if (session) {
|
|
3306
3356
|
onSessionReady?.(session);
|
|
3307
3357
|
}
|
|
3308
3358
|
}, [session, onSessionReady]);
|
|
3309
|
-
|
|
3359
|
+
useEffect9(() => {
|
|
3310
3360
|
if (!session) return;
|
|
3311
3361
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
3312
3362
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3322,12 +3372,12 @@ ${content}`);
|
|
|
3322
3372
|
offInsert();
|
|
3323
3373
|
};
|
|
3324
3374
|
}, [session]);
|
|
3325
|
-
|
|
3375
|
+
useEffect9(() => {
|
|
3326
3376
|
if (isUnauthorizedError(error)) {
|
|
3327
3377
|
onUnauthorized();
|
|
3328
3378
|
}
|
|
3329
3379
|
}, [error, onUnauthorized]);
|
|
3330
|
-
|
|
3380
|
+
useEffect9(() => {
|
|
3331
3381
|
if (!session || !commands) return;
|
|
3332
3382
|
const unsubscribes = Object.entries(commands).map(
|
|
3333
3383
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3398,10 +3448,10 @@ ${content}`);
|
|
|
3398
3448
|
}
|
|
3399
3449
|
|
|
3400
3450
|
// src/components/LlmChat.tsx
|
|
3401
|
-
import { useEffect as
|
|
3451
|
+
import { useEffect as useEffect10, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3402
3452
|
|
|
3403
3453
|
// src/components/LlmAdvancedSettings.tsx
|
|
3404
|
-
import { useState as
|
|
3454
|
+
import { useState as useState14 } from "react";
|
|
3405
3455
|
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3406
3456
|
var FIELDS = [
|
|
3407
3457
|
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
@@ -3448,8 +3498,8 @@ function writeOverride(settings, baseURL, override) {
|
|
|
3448
3498
|
}
|
|
3449
3499
|
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3450
3500
|
const normalized = normalizeAdvanced(settings);
|
|
3451
|
-
const [open, setOpen] =
|
|
3452
|
-
const [draft, setDraft] =
|
|
3501
|
+
const [open, setOpen] = useState14(false);
|
|
3502
|
+
const [draft, setDraft] = useState14(override);
|
|
3453
3503
|
if (!normalized) return null;
|
|
3454
3504
|
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3455
3505
|
const dirty = Object.keys(override).length > 0;
|
|
@@ -3532,11 +3582,11 @@ function LlmChat({
|
|
|
3532
3582
|
onOverrideChange,
|
|
3533
3583
|
...options
|
|
3534
3584
|
}) {
|
|
3535
|
-
const [override, setOverride] =
|
|
3585
|
+
const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
|
|
3536
3586
|
const effective = { ...options, ...override };
|
|
3537
3587
|
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3538
|
-
const [inputText, setInputText] =
|
|
3539
|
-
const [stopRequested, setStopRequested] =
|
|
3588
|
+
const [inputText, setInputText] = useState15("");
|
|
3589
|
+
const [stopRequested, setStopRequested] = useState15(false);
|
|
3540
3590
|
const handle = useMemo9(
|
|
3541
3591
|
() => ({
|
|
3542
3592
|
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
@@ -3546,7 +3596,7 @@ ${text}` : text),
|
|
|
3546
3596
|
}),
|
|
3547
3597
|
[send, reset]
|
|
3548
3598
|
);
|
|
3549
|
-
|
|
3599
|
+
useEffect10(() => {
|
|
3550
3600
|
onReady?.(handle);
|
|
3551
3601
|
}, [handle, onReady]);
|
|
3552
3602
|
return /* @__PURE__ */ jsx19(
|