@blade-hq/agent-react 2610.0.0-beta.32 → 2610.0.0-beta.34
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 +20 -0
- package/dist/components/ChatInput.d.ts +4 -1
- package/dist/components/ChatSurface.d.ts +2 -1
- package/dist/components/SessionMemoryToggle.d.ts +19 -0
- package/dist/components/display-utils.d.ts +2 -0
- package/dist/context.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +406 -216
- package/dist/index.js.map +1 -1
- package/dist/style.css +4 -1
- package/dist/style.full.css +5 -2
- package/package.json +2 -2
- package/public-api.md +22 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,9 @@ function useBladeClient() {
|
|
|
17
17
|
}
|
|
18
18
|
return client;
|
|
19
19
|
}
|
|
20
|
+
function useOptionalBladeClient() {
|
|
21
|
+
return useContext(BladeClientContext);
|
|
22
|
+
}
|
|
20
23
|
|
|
21
24
|
// src/hooks/use-agent-session.ts
|
|
22
25
|
import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
@@ -1141,7 +1144,7 @@ var X = createLucideIcon("X", [
|
|
|
1141
1144
|
]);
|
|
1142
1145
|
|
|
1143
1146
|
// src/components/AgentChat.tsx
|
|
1144
|
-
import { useCallback as useCallback8, useEffect as
|
|
1147
|
+
import { useCallback as useCallback8, useEffect as useEffect13, useMemo as useMemo8, useState as useState15 } from "react";
|
|
1145
1148
|
|
|
1146
1149
|
// src/lib/utils.ts
|
|
1147
1150
|
function cn(...inputs) {
|
|
@@ -1610,7 +1613,8 @@ function CurrentPlanPanel({
|
|
|
1610
1613
|
import { chatErrorForDisplay as chatErrorForDisplay2 } from "@blade-hq/agent-client";
|
|
1611
1614
|
|
|
1612
1615
|
// src/components/ChatInput.tsx
|
|
1613
|
-
import {
|
|
1616
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
|
|
1617
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1614
1618
|
function isImeCompositionKey(event) {
|
|
1615
1619
|
return event.isComposing || event.keyCode === 229;
|
|
1616
1620
|
}
|
|
@@ -1621,16 +1625,55 @@ function ChatInput({
|
|
|
1621
1625
|
value,
|
|
1622
1626
|
onValueChange,
|
|
1623
1627
|
onSend,
|
|
1628
|
+
onAppend,
|
|
1624
1629
|
onStop,
|
|
1625
1630
|
isStreaming,
|
|
1626
1631
|
isStopping = false,
|
|
1627
1632
|
placeholder = "\u8F93\u5165\u6D88\u606F\u2026",
|
|
1628
|
-
className
|
|
1633
|
+
className,
|
|
1634
|
+
queueKey
|
|
1629
1635
|
}) {
|
|
1630
1636
|
const trimmed = value.trim();
|
|
1631
|
-
const
|
|
1637
|
+
const [sendMode, setSendMode] = useState5("direct");
|
|
1638
|
+
const [promptQueue, setPromptQueue] = useState5([]);
|
|
1639
|
+
const queueSendingRef = useRef5(false);
|
|
1640
|
+
const queueBlockedRef = useRef5(false);
|
|
1641
|
+
const previousQueueKeyRef = useRef5(queueKey);
|
|
1642
|
+
useEffect5(() => {
|
|
1643
|
+
if (previousQueueKeyRef.current === queueKey) return;
|
|
1644
|
+
previousQueueKeyRef.current = queueKey;
|
|
1645
|
+
setPromptQueue([]);
|
|
1646
|
+
queueBlockedRef.current = false;
|
|
1647
|
+
}, [queueKey]);
|
|
1648
|
+
const canSend = trimmed.length > 0 && (!isStreaming || sendMode === "queue");
|
|
1649
|
+
useEffect5(() => {
|
|
1650
|
+
if (isStreaming) {
|
|
1651
|
+
queueBlockedRef.current = false;
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
if (queueBlockedRef.current || promptQueue.length === 0 || queueSendingRef.current) return;
|
|
1655
|
+
const next = promptQueue[0];
|
|
1656
|
+
queueSendingRef.current = true;
|
|
1657
|
+
Promise.resolve().then(() => onSend(next)).then((accepted) => {
|
|
1658
|
+
if (accepted) setPromptQueue((current) => current.slice(1));
|
|
1659
|
+
else queueBlockedRef.current = true;
|
|
1660
|
+
}).finally(() => {
|
|
1661
|
+
queueSendingRef.current = false;
|
|
1662
|
+
});
|
|
1663
|
+
}, [isStreaming, onSend, promptQueue]);
|
|
1632
1664
|
const handleSend = async () => {
|
|
1633
1665
|
if (!canSend) return;
|
|
1666
|
+
if (isStreaming && sendMode === "queue") {
|
|
1667
|
+
setPromptQueue((current) => [...current, trimmed]);
|
|
1668
|
+
onValueChange("");
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
if (isStreaming && sendMode === "direct") {
|
|
1672
|
+
if (!onAppend) return;
|
|
1673
|
+
onAppend(trimmed);
|
|
1674
|
+
onValueChange("");
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1634
1677
|
const accepted = await onSend(trimmed);
|
|
1635
1678
|
if (!accepted) return;
|
|
1636
1679
|
onValueChange("");
|
|
@@ -1646,61 +1689,73 @@ function ChatInput({
|
|
|
1646
1689
|
void handleSend();
|
|
1647
1690
|
}
|
|
1648
1691
|
};
|
|
1649
|
-
return /* @__PURE__ */
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
"
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
children: /* @__PURE__ */ jsx5(ArrowUp, { size: 15 })
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1692
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("blade-chat-input border-t border-[hsl(var(--border))] py-3", className), children: [
|
|
1693
|
+
/* @__PURE__ */ jsxs4("div", { className: "mx-auto mb-2 flex max-w-[748px] items-center justify-between px-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
1694
|
+
/* @__PURE__ */ jsxs4("fieldset", { className: "flex items-center gap-1 rounded-md border border-[hsl(var(--border))] p-0.5", children: [
|
|
1695
|
+
/* @__PURE__ */ jsx5("legend", { className: "sr-only", children: "\u53D1\u9001\u65B9\u5F0F" }),
|
|
1696
|
+
/* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setSendMode("direct"), "aria-pressed": sendMode === "direct", disabled: isStreaming && !onAppend, className: `rounded px-2 py-1 ${sendMode === "direct" ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : ""}`, children: "\u76F4\u63A5\u63D2\u5165" }),
|
|
1697
|
+
/* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setSendMode("queue"), "aria-pressed": sendMode === "queue", className: `rounded px-2 py-1 ${sendMode === "queue" ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : ""}`, children: "\u6392\u961F\u6267\u884C" })
|
|
1698
|
+
] }),
|
|
1699
|
+
promptQueue.length > 0 ? /* @__PURE__ */ jsxs4("details", { className: "relative", children: [
|
|
1700
|
+
/* @__PURE__ */ jsxs4("summary", { className: "cursor-pointer list-none rounded px-2 py-1 hover:bg-[hsl(var(--accent))]", children: [
|
|
1701
|
+
"\u5F85\u6267\u884C ",
|
|
1702
|
+
promptQueue.length,
|
|
1703
|
+
" \u6761"
|
|
1704
|
+
] }),
|
|
1705
|
+
/* @__PURE__ */ jsx5("div", { className: "absolute bottom-full right-0 z-20 mb-2 w-64 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-2 shadow-lg", children: promptQueue.map((item, index) => /* @__PURE__ */ jsxs4("div", { className: "flex gap-2 border-b border-[hsl(var(--border))] py-2 last:border-0", children: [
|
|
1706
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate", children: item }),
|
|
1707
|
+
/* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setPromptQueue((current) => current.filter((_, i) => i !== index)), children: "\u53D6\u6D88" })
|
|
1708
|
+
] }, `${index}-${item}`)) })
|
|
1709
|
+
] }) : null
|
|
1710
|
+
] }),
|
|
1711
|
+
/* @__PURE__ */ jsxs4("div", { className: "blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx5(
|
|
1713
|
+
"textarea",
|
|
1714
|
+
{
|
|
1715
|
+
value,
|
|
1716
|
+
onChange: (event) => onValueChange(event.target.value),
|
|
1717
|
+
onKeyDown: handleKeyDown,
|
|
1718
|
+
onInput: (event) => {
|
|
1719
|
+
const el = event.currentTarget;
|
|
1720
|
+
el.style.height = "auto";
|
|
1721
|
+
el.style.height = `${Math.min(el.scrollHeight, 192)}px`;
|
|
1722
|
+
},
|
|
1723
|
+
rows: 1,
|
|
1724
|
+
placeholder,
|
|
1725
|
+
"aria-label": "\u804A\u5929\u8F93\u5165",
|
|
1726
|
+
className: "blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]"
|
|
1727
|
+
}
|
|
1728
|
+
),
|
|
1729
|
+
isStreaming ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1730
|
+
sendMode === "queue" ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: handleSend, disabled: !canSend, "aria-label": "\u52A0\u5165\u6392\u961F", title: "\u52A0\u5165\u6392\u961F", className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] disabled:opacity-40", children: /* @__PURE__ */ jsx5(ArrowUp, { size: 15 }) }) : null,
|
|
1731
|
+
/* @__PURE__ */ jsx5("button", { type: "button", onClick: onStop, disabled: isStopping, "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D", title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D", className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60", children: isStopping ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(Square, { size: 12, fill: "currentColor" }) })
|
|
1732
|
+
] }) : /* @__PURE__ */ jsx5(
|
|
1733
|
+
"button",
|
|
1734
|
+
{
|
|
1735
|
+
type: "button",
|
|
1736
|
+
onClick: handleSend,
|
|
1737
|
+
disabled: !canSend,
|
|
1738
|
+
"aria-label": "\u53D1\u9001\u6D88\u606F",
|
|
1739
|
+
title: "\u53D1\u9001\u6D88\u606F",
|
|
1740
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40",
|
|
1741
|
+
children: /* @__PURE__ */ jsx5(ArrowUp, { size: 15 })
|
|
1742
|
+
}
|
|
1743
|
+
)
|
|
1744
|
+
] })
|
|
1745
|
+
] });
|
|
1691
1746
|
}
|
|
1692
1747
|
|
|
1693
1748
|
// src/components/ConnectionBanner.tsx
|
|
1694
|
-
import { useEffect as
|
|
1749
|
+
import { useEffect as useEffect6, useRef as useRef6, useState as useState6 } from "react";
|
|
1695
1750
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1696
1751
|
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1697
1752
|
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1698
1753
|
function useConnectionNoticePhase(connected) {
|
|
1699
|
-
const [phase, setPhase] =
|
|
1700
|
-
const connectedRef =
|
|
1701
|
-
const timersRef =
|
|
1754
|
+
const [phase, setPhase] = useState6("hidden");
|
|
1755
|
+
const connectedRef = useRef6(connected);
|
|
1756
|
+
const timersRef = useRef6([]);
|
|
1702
1757
|
connectedRef.current = connected;
|
|
1703
|
-
|
|
1758
|
+
useEffect6(() => {
|
|
1704
1759
|
const clearTimers = () => {
|
|
1705
1760
|
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1706
1761
|
timersRef.current = [];
|
|
@@ -1740,7 +1795,7 @@ function useConnectionNoticePhase(connected) {
|
|
|
1740
1795
|
return phase;
|
|
1741
1796
|
}
|
|
1742
1797
|
function ConnectionBanner({ connection, className }) {
|
|
1743
|
-
const hasConnectedRef =
|
|
1798
|
+
const hasConnectedRef = useRef6(connection === "connected" || connection === "reconnecting");
|
|
1744
1799
|
if (connection === "connected") hasConnectedRef.current = true;
|
|
1745
1800
|
const connected = connection === "connected";
|
|
1746
1801
|
const phase = useConnectionNoticePhase(connected);
|
|
@@ -1767,10 +1822,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1767
1822
|
|
|
1768
1823
|
// src/components/MessageList.tsx
|
|
1769
1824
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1770
|
-
import { useCallback as useCallback7, useEffect as
|
|
1825
|
+
import { useCallback as useCallback7, useEffect as useEffect12, useMemo as useMemo7, useRef as useRef13, useState as useState14 } from "react";
|
|
1771
1826
|
|
|
1772
1827
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1773
|
-
import { useCallback as useCallback4, useMemo as useMemo3, useRef as
|
|
1828
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef7, useState as useState7 } from "react";
|
|
1774
1829
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1775
1830
|
/**
|
|
1776
1831
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1807,10 +1862,10 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1807
1862
|
mouseDown = false;
|
|
1808
1863
|
});
|
|
1809
1864
|
var useStickToBottom = (options = {}) => {
|
|
1810
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1811
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1812
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1813
|
-
const optionsRef =
|
|
1865
|
+
const [escapedFromLock, updateEscapedFromLock] = useState7(false);
|
|
1866
|
+
const [isAtBottom, updateIsAtBottom] = useState7(options.initial !== false);
|
|
1867
|
+
const [isNearBottom, setIsNearBottom] = useState7(false);
|
|
1868
|
+
const optionsRef = useRef7(null);
|
|
1814
1869
|
optionsRef.current = options;
|
|
1815
1870
|
const isSelecting = useCallback4(() => {
|
|
1816
1871
|
if (!mouseDown) {
|
|
@@ -2114,11 +2169,11 @@ function mergeAnimations(...animations) {
|
|
|
2114
2169
|
|
|
2115
2170
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
2116
2171
|
import * as React from "react";
|
|
2117
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
2172
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect7, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef8 } from "react";
|
|
2118
2173
|
var StickToBottomContext = createContext2(null);
|
|
2119
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
2174
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect7;
|
|
2120
2175
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
2121
|
-
const customTargetScrollTop =
|
|
2176
|
+
const customTargetScrollTop = useRef8(null);
|
|
2122
2177
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
2123
2178
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
2124
2179
|
return get?.(target, elements) ?? target;
|
|
@@ -2200,10 +2255,10 @@ import {
|
|
|
2200
2255
|
getTextContent,
|
|
2201
2256
|
normalizeMessageContent
|
|
2202
2257
|
} from "@blade-hq/agent-client";
|
|
2203
|
-
import { useEffect as
|
|
2258
|
+
import { useEffect as useEffect10, useRef as useRef11, useState as useState12 } from "react";
|
|
2204
2259
|
|
|
2205
2260
|
// src/components/AgentLoopBlock.tsx
|
|
2206
|
-
import { useState as
|
|
2261
|
+
import { useState as useState8 } from "react";
|
|
2207
2262
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2208
2263
|
function parseAgentDescription(argumentsJson) {
|
|
2209
2264
|
try {
|
|
@@ -2214,7 +2269,7 @@ function parseAgentDescription(argumentsJson) {
|
|
|
2214
2269
|
}
|
|
2215
2270
|
}
|
|
2216
2271
|
function AgentLoopBlock({ toolCall }) {
|
|
2217
|
-
const [expanded, setExpanded] =
|
|
2272
|
+
const [expanded, setExpanded] = useState8(false);
|
|
2218
2273
|
const description = parseAgentDescription(toolCall.arguments);
|
|
2219
2274
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
2220
2275
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
@@ -2248,8 +2303,9 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
2248
2303
|
ChevronRight,
|
|
2249
2304
|
{
|
|
2250
2305
|
size: 14,
|
|
2306
|
+
style: { transitionDuration: "260ms", transitionTimingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" },
|
|
2251
2307
|
className: cn(
|
|
2252
|
-
"shrink-0 transition-transform
|
|
2308
|
+
"shrink-0 transition-transform",
|
|
2253
2309
|
expanded && "rotate-90"
|
|
2254
2310
|
),
|
|
2255
2311
|
"aria-hidden": "true"
|
|
@@ -2264,10 +2320,10 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
2264
2320
|
|
|
2265
2321
|
// src/components/MarkdownContent.tsx
|
|
2266
2322
|
import {
|
|
2267
|
-
useEffect as
|
|
2323
|
+
useEffect as useEffect8,
|
|
2268
2324
|
useMemo as useMemo5,
|
|
2269
|
-
useRef as
|
|
2270
|
-
useState as
|
|
2325
|
+
useRef as useRef9,
|
|
2326
|
+
useState as useState9
|
|
2271
2327
|
} from "react";
|
|
2272
2328
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2273
2329
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
@@ -2284,10 +2340,10 @@ function normalizeAdjacentUrlFormatting(value) {
|
|
|
2284
2340
|
return normalized.replace(/blade-url-protected-(\d+)-marker/g, (_, index) => protectedSegments[Number(index)]);
|
|
2285
2341
|
}
|
|
2286
2342
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
2287
|
-
const preRef =
|
|
2288
|
-
const [copied, setCopied] =
|
|
2289
|
-
const [language, setLanguage] =
|
|
2290
|
-
|
|
2343
|
+
const preRef = useRef9(null);
|
|
2344
|
+
const [copied, setCopied] = useState9(false);
|
|
2345
|
+
const [language, setLanguage] = useState9("");
|
|
2346
|
+
useEffect8(() => {
|
|
2291
2347
|
const codeEl = preRef.current?.querySelector("code");
|
|
2292
2348
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
2293
2349
|
}, []);
|
|
@@ -2358,10 +2414,10 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
2358
2414
|
}
|
|
2359
2415
|
|
|
2360
2416
|
// src/components/ToolCallBlock.tsx
|
|
2361
|
-
import { useState as
|
|
2417
|
+
import { useState as useState11 } from "react";
|
|
2362
2418
|
|
|
2363
2419
|
// src/components/AskUserQuestionBlock.tsx
|
|
2364
|
-
import { useEffect as
|
|
2420
|
+
import { useEffect as useEffect9, useMemo as useMemo6, useRef as useRef10, useState as useState10 } from "react";
|
|
2365
2421
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2366
2422
|
var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
|
|
2367
2423
|
function resizeCustomTextarea(textarea) {
|
|
@@ -2370,12 +2426,12 @@ function resizeCustomTextarea(textarea) {
|
|
|
2370
2426
|
textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
|
|
2371
2427
|
}
|
|
2372
2428
|
function useAutoResizeTextarea(value) {
|
|
2373
|
-
const textareaRef =
|
|
2374
|
-
|
|
2429
|
+
const textareaRef = useRef10(null);
|
|
2430
|
+
useEffect9(() => {
|
|
2375
2431
|
const textarea = textareaRef.current;
|
|
2376
2432
|
if (textarea?.value === value) resizeCustomTextarea(textarea);
|
|
2377
2433
|
}, [value]);
|
|
2378
|
-
|
|
2434
|
+
useEffect9(() => {
|
|
2379
2435
|
const textarea = textareaRef.current;
|
|
2380
2436
|
if (!textarea || typeof ResizeObserver === "undefined") return;
|
|
2381
2437
|
let previousWidth = textarea.clientWidth;
|
|
@@ -2400,12 +2456,12 @@ function AskUserQuestionBlock({
|
|
|
2400
2456
|
answerData,
|
|
2401
2457
|
onAnswer
|
|
2402
2458
|
}) {
|
|
2403
|
-
const [selections, setSelections] =
|
|
2404
|
-
const [customTexts, setCustomTexts] =
|
|
2405
|
-
const [usingCustom, setUsingCustom] =
|
|
2406
|
-
const [note, setNote] =
|
|
2407
|
-
const [submitted, setSubmitted] =
|
|
2408
|
-
|
|
2459
|
+
const [selections, setSelections] = useState10(/* @__PURE__ */ new Map());
|
|
2460
|
+
const [customTexts, setCustomTexts] = useState10(/* @__PURE__ */ new Map());
|
|
2461
|
+
const [usingCustom, setUsingCustom] = useState10(/* @__PURE__ */ new Set());
|
|
2462
|
+
const [note, setNote] = useState10("");
|
|
2463
|
+
const [submitted, setSubmitted] = useState10(false);
|
|
2464
|
+
useEffect9(() => {
|
|
2409
2465
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
2410
2466
|
setSubmitted(false);
|
|
2411
2467
|
}
|
|
@@ -2794,7 +2850,7 @@ function normalizeOptionItem(value) {
|
|
|
2794
2850
|
}
|
|
2795
2851
|
|
|
2796
2852
|
// src/components/ToolCallBlock.tsx
|
|
2797
|
-
import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2853
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2798
2854
|
function resolveAskQuestionState({
|
|
2799
2855
|
toolStatus,
|
|
2800
2856
|
hasAnswerData,
|
|
@@ -2816,12 +2872,12 @@ function ToolCallBlock({
|
|
|
2816
2872
|
isActiveQuestion,
|
|
2817
2873
|
renderer
|
|
2818
2874
|
}) {
|
|
2819
|
-
const [expanded, setExpanded] =
|
|
2875
|
+
const [expanded, setExpanded] = useState11(false);
|
|
2820
2876
|
const normalizedName = formatToolName(toolCall.name);
|
|
2821
2877
|
if (renderer) {
|
|
2822
2878
|
const custom = renderer(toolCall);
|
|
2823
2879
|
if (custom !== null && custom !== void 0) {
|
|
2824
|
-
return /* @__PURE__ */ jsx11(
|
|
2880
|
+
return /* @__PURE__ */ jsx11(Fragment2, { children: custom });
|
|
2825
2881
|
}
|
|
2826
2882
|
}
|
|
2827
2883
|
if (normalizedName === "AskUserQuestion") {
|
|
@@ -2904,7 +2960,7 @@ function ToolCallBlock({
|
|
|
2904
2960
|
/* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
|
|
2905
2961
|
/* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
|
|
2906
2962
|
/* @__PURE__ */ jsx11("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) }),
|
|
2907
|
-
toolCall.result != null && /* @__PURE__ */ jsxs9(
|
|
2963
|
+
toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
2908
2964
|
/* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
2909
2965
|
/* @__PURE__ */ jsx11("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) })
|
|
2910
2966
|
] })
|
|
@@ -2926,7 +2982,7 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2926
2982
|
// src/components/AssistantTurnBlock.tsx
|
|
2927
2983
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2928
2984
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2929
|
-
const [open, setOpen] =
|
|
2985
|
+
const [open, setOpen] = useState12(false);
|
|
2930
2986
|
if (!isStreaming) return null;
|
|
2931
2987
|
return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking text-xs", children: [
|
|
2932
2988
|
/* @__PURE__ */ jsxs10(
|
|
@@ -3196,12 +3252,12 @@ function AssistantTurnBlock({
|
|
|
3196
3252
|
)
|
|
3197
3253
|
);
|
|
3198
3254
|
const activeQuestionId = questionToolCalls.filter((toolCall) => toolCall.status === "pending").at(-1)?.id;
|
|
3199
|
-
const [displayMode, setDisplayMode] =
|
|
3255
|
+
const [displayMode, setDisplayMode] = useState12(
|
|
3200
3256
|
() => isStreaming || hasActionableToolCall ? "detail" : "compact"
|
|
3201
3257
|
);
|
|
3202
|
-
const userSelectedDisplayModeRef =
|
|
3203
|
-
const wasStreamingRef =
|
|
3204
|
-
|
|
3258
|
+
const userSelectedDisplayModeRef = useRef11(false);
|
|
3259
|
+
const wasStreamingRef = useRef11(isStreaming);
|
|
3260
|
+
useEffect10(() => {
|
|
3205
3261
|
if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
|
|
3206
3262
|
setDisplayMode(hasActionableToolCall ? "detail" : "compact");
|
|
3207
3263
|
}
|
|
@@ -3209,11 +3265,11 @@ function AssistantTurnBlock({
|
|
|
3209
3265
|
}, [hasActionableToolCall, isStreaming]);
|
|
3210
3266
|
const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
|
|
3211
3267
|
const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
|
|
3212
|
-
const [clock, setClock] =
|
|
3268
|
+
const [clock, setClock] = useState12(() => Date.now());
|
|
3213
3269
|
const hasLiveStartTime = messages.some(
|
|
3214
3270
|
(message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
|
|
3215
3271
|
);
|
|
3216
|
-
|
|
3272
|
+
useEffect10(() => {
|
|
3217
3273
|
if (!isStreaming || !hasLiveStartTime) return;
|
|
3218
3274
|
const timer = window.setInterval(() => setClock(Date.now()), 1e3);
|
|
3219
3275
|
return () => window.clearInterval(timer);
|
|
@@ -3294,8 +3350,9 @@ function AssistantTurnBlock({
|
|
|
3294
3350
|
ChevronRight,
|
|
3295
3351
|
{
|
|
3296
3352
|
size: 14,
|
|
3353
|
+
style: { transitionDuration: "260ms", transitionTimingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" },
|
|
3297
3354
|
className: cn(
|
|
3298
|
-
"shrink-0 transition-transform
|
|
3355
|
+
"shrink-0 transition-transform",
|
|
3299
3356
|
effectiveMode === "detail" && "rotate-90"
|
|
3300
3357
|
),
|
|
3301
3358
|
"aria-hidden": "true"
|
|
@@ -3387,7 +3444,7 @@ function collectMemoryRefs(messages) {
|
|
|
3387
3444
|
return [...refs.values()];
|
|
3388
3445
|
}
|
|
3389
3446
|
function MemoryRefsHint({ refs }) {
|
|
3390
|
-
const [expanded, setExpanded] =
|
|
3447
|
+
const [expanded, setExpanded] = useState12(false);
|
|
3391
3448
|
const label = refs.some((ref) => ref.skill_name) ? "\u53C2\u8003\u4E86\u8BE5\u6280\u80FD\u7684\u5386\u53F2\u7ECF\u9A8C" : "\u53C2\u8003\u4E86\u5386\u53F2\u7ECF\u9A8C";
|
|
3392
3449
|
return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
|
|
3393
3450
|
/* @__PURE__ */ jsxs10("button", { type: "button", onClick: () => setExpanded((value) => !value), className: "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.22)] bg-[hsl(var(--primary)/0.07)] px-3 text-xs font-medium text-[hsl(var(--primary))]", children: [
|
|
@@ -3522,8 +3579,8 @@ var RenderErrorBoundary = class extends Component {
|
|
|
3522
3579
|
};
|
|
3523
3580
|
|
|
3524
3581
|
// src/components/PostChatFollowupBlock.tsx
|
|
3525
|
-
import { useCallback as useCallback6, useEffect as
|
|
3526
|
-
import { Fragment as
|
|
3582
|
+
import { useCallback as useCallback6, useEffect as useEffect11, useRef as useRef12, useState as useState13 } from "react";
|
|
3583
|
+
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3527
3584
|
function emitInteraction(callback, event) {
|
|
3528
3585
|
try {
|
|
3529
3586
|
callback?.(event);
|
|
@@ -3542,7 +3599,7 @@ function ArtifactCard({
|
|
|
3542
3599
|
onArtifactOpened
|
|
3543
3600
|
}) {
|
|
3544
3601
|
const client = useBladeClient();
|
|
3545
|
-
const [downloading, setDownloading] =
|
|
3602
|
+
const [downloading, setDownloading] = useState13(false);
|
|
3546
3603
|
const name = artifact.label || basename(artifact.target);
|
|
3547
3604
|
if (artifact.kind === "link") {
|
|
3548
3605
|
return /* @__PURE__ */ jsxs12(
|
|
@@ -3648,15 +3705,15 @@ function ResultFeedback({
|
|
|
3648
3705
|
onFeedbackSaved
|
|
3649
3706
|
}) {
|
|
3650
3707
|
const client = useBladeClient();
|
|
3651
|
-
const [saved, setSaved] =
|
|
3652
|
-
const [helpful, setHelpful] =
|
|
3653
|
-
const [reason, setReason] =
|
|
3654
|
-
const [saving, setSaving] =
|
|
3655
|
-
const [saveError, setSaveError] =
|
|
3656
|
-
const reportedShown =
|
|
3657
|
-
const latestChoice =
|
|
3708
|
+
const [saved, setSaved] = useState13(savedFeedback ?? null);
|
|
3709
|
+
const [helpful, setHelpful] = useState13(savedFeedback?.helpful ?? null);
|
|
3710
|
+
const [reason, setReason] = useState13(savedFeedback?.reason ?? null);
|
|
3711
|
+
const [saving, setSaving] = useState13(false);
|
|
3712
|
+
const [saveError, setSaveError] = useState13(false);
|
|
3713
|
+
const reportedShown = useRef12(false);
|
|
3714
|
+
const latestChoice = useRef12(null);
|
|
3658
3715
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
3659
|
-
|
|
3716
|
+
useEffect11(() => {
|
|
3660
3717
|
if (!eligible || reportedShown.current) return;
|
|
3661
3718
|
reportedShown.current = true;
|
|
3662
3719
|
emitInteraction(onInteraction, {
|
|
@@ -3665,7 +3722,7 @@ function ResultFeedback({
|
|
|
3665
3722
|
assistantEntryId: followup.assistant_entry_id
|
|
3666
3723
|
});
|
|
3667
3724
|
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
3668
|
-
|
|
3725
|
+
useEffect11(() => {
|
|
3669
3726
|
if (!savedFeedback || latestChoice.current) return;
|
|
3670
3727
|
setSaved(savedFeedback);
|
|
3671
3728
|
setHelpful(savedFeedback.helpful);
|
|
@@ -3777,14 +3834,14 @@ function PostChatFollowupBlock({
|
|
|
3777
3834
|
savedFeedback,
|
|
3778
3835
|
onFeedbackSaved
|
|
3779
3836
|
}) {
|
|
3780
|
-
const [expanded, setExpanded] =
|
|
3781
|
-
const adopted =
|
|
3782
|
-
const reportedSuggestions =
|
|
3783
|
-
const reportedArtifacts =
|
|
3784
|
-
const openedArtifacts =
|
|
3837
|
+
const [expanded, setExpanded] = useState13(false);
|
|
3838
|
+
const adopted = useRef12(/* @__PURE__ */ new Set());
|
|
3839
|
+
const reportedSuggestions = useRef12(false);
|
|
3840
|
+
const reportedArtifacts = useRef12(/* @__PURE__ */ new Set());
|
|
3841
|
+
const openedArtifacts = useRef12(/* @__PURE__ */ new Set());
|
|
3785
3842
|
const artifacts = followup.final_artifacts ?? [];
|
|
3786
3843
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
3787
|
-
|
|
3844
|
+
useEffect11(() => {
|
|
3788
3845
|
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
3789
3846
|
reportedSuggestions.current = true;
|
|
3790
3847
|
emitInteraction(onInteraction, {
|
|
@@ -3837,7 +3894,7 @@ function PostChatFollowupBlock({
|
|
|
3837
3894
|
"\u672C\u8F6E\u5C0F\u7ED3"
|
|
3838
3895
|
] }),
|
|
3839
3896
|
followup.recaption ? /* @__PURE__ */ jsx14("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
|
|
3840
|
-
artifacts.length > 0 ? /* @__PURE__ */ jsxs12(
|
|
3897
|
+
artifacts.length > 0 ? /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
3841
3898
|
/* @__PURE__ */ jsx14("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx14(
|
|
3842
3899
|
ArtifactCard,
|
|
3843
3900
|
{
|
|
@@ -4095,8 +4152,16 @@ function MessageList({
|
|
|
4095
4152
|
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
4096
4153
|
onResultFeedbackSaved
|
|
4097
4154
|
}) {
|
|
4098
|
-
const
|
|
4155
|
+
const visibleRootMessages = messages.filter((message) => {
|
|
4156
|
+
if ((message.loop_name ?? "root") !== "root") return false;
|
|
4157
|
+
if (isHiddenInternalMessage(message)) return false;
|
|
4158
|
+
if (message.kind === "context") return false;
|
|
4159
|
+
return message.role !== "tool" || getPlanningDividerKind(message) !== null;
|
|
4160
|
+
});
|
|
4161
|
+
const userMessages = visibleRootMessages.filter((message) => isUserMessage(message));
|
|
4099
4162
|
const latestUserMessage = userMessages.at(-1);
|
|
4163
|
+
const latestPromptText = latestUserMessage ? (typeof latestUserMessage.content === "string" ? latestUserMessage.content : latestUserMessage.content.filter((part) => part.type === "text").map((part) => part.text).join("")).replace(/\s+/g, " ").trim() : "";
|
|
4164
|
+
const latestPromptPreview = latestPromptText.length > 80 ? `${latestPromptText.slice(0, 80)}\u2026` : latestPromptText;
|
|
4100
4165
|
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith("local-user-"));
|
|
4101
4166
|
const renderBlocks = useMemo7(() => {
|
|
4102
4167
|
const visible = messages.filter((message) => {
|
|
@@ -4181,78 +4246,90 @@ function MessageList({
|
|
|
4181
4246
|
initial: "instant",
|
|
4182
4247
|
resize: "instant",
|
|
4183
4248
|
children: [
|
|
4184
|
-
/* @__PURE__ */ jsx17(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
if (block.type === "assistant_turn") {
|
|
4194
|
-
const blockFeedback = block.messages.map(
|
|
4195
|
-
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
4196
|
-
).find((feedback) => feedback != null);
|
|
4197
|
-
const hasActiveFollowup = Boolean(
|
|
4198
|
-
postChatFollowup && block.messages.some(
|
|
4199
|
-
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
4200
|
-
)
|
|
4201
|
-
);
|
|
4202
|
-
return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs15(
|
|
4203
|
-
RenderErrorBoundary,
|
|
4204
|
-
{
|
|
4205
|
-
label: "\u52A9\u624B\u6D88\u606F",
|
|
4206
|
-
details: block.key,
|
|
4207
|
-
resetKey: getMessageResetSignature(block.messages),
|
|
4208
|
-
children: [
|
|
4209
|
-
/* @__PURE__ */ jsx17(
|
|
4210
|
-
AssistantTurnBlock,
|
|
4211
|
-
{
|
|
4212
|
-
messages: block.messages,
|
|
4213
|
-
isStreaming: block.isStreaming,
|
|
4214
|
-
askAnswers,
|
|
4215
|
-
onAnswer,
|
|
4216
|
-
sessionStatus,
|
|
4217
|
-
toolCallRenderer,
|
|
4218
|
-
hidePlanUpdateTools,
|
|
4219
|
-
sessionId
|
|
4220
|
-
}
|
|
4221
|
-
),
|
|
4222
|
-
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx17(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
4223
|
-
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx17(
|
|
4224
|
-
PostChatFollowupBlock,
|
|
4225
|
-
{
|
|
4226
|
-
followup: postChatFollowup,
|
|
4227
|
-
sessionId,
|
|
4228
|
-
onSuggestion,
|
|
4229
|
-
isViewer,
|
|
4230
|
-
onInteraction: onFollowupInteraction,
|
|
4231
|
-
savedFeedback: blockFeedback,
|
|
4232
|
-
onFeedbackSaved: onResultFeedbackSaved
|
|
4233
|
-
}
|
|
4234
|
-
) : null
|
|
4235
|
-
]
|
|
4236
|
-
}
|
|
4237
|
-
) }, block.key);
|
|
4238
|
-
}
|
|
4239
|
-
if (block.type === "compaction") {
|
|
4240
|
-
return /* @__PURE__ */ jsxs15(
|
|
4241
|
-
"div",
|
|
4242
|
-
{
|
|
4243
|
-
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
4244
|
-
children: [
|
|
4245
|
-
/* @__PURE__ */ jsx17(Layers, { size: 12 }),
|
|
4246
|
-
/* @__PURE__ */ jsx17("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
4247
|
-
]
|
|
4248
|
-
},
|
|
4249
|
-
block.key
|
|
4250
|
-
);
|
|
4249
|
+
/* @__PURE__ */ jsx17(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsxs15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: [
|
|
4250
|
+
isStreaming && latestUserMessage && latestPromptPreview ? /* @__PURE__ */ jsx17(
|
|
4251
|
+
"button",
|
|
4252
|
+
{
|
|
4253
|
+
type: "button",
|
|
4254
|
+
className: "sticky top-0 z-20 mb-4 w-full truncate rounded-2xl border border-[hsl(var(--primary)/0.2)] bg-[hsl(var(--background)/0.92)] px-4 py-3 text-left text-sm font-medium shadow-sm backdrop-blur",
|
|
4255
|
+
onClick: () => Array.from(document.querySelectorAll("[data-entry-id]")).find((el) => el.dataset.entryId === latestUserMessage.entry_id)?.scrollIntoView({ behavior: "smooth", block: "start" }),
|
|
4256
|
+
"aria-label": "\u8DF3\u8F6C\u5230\u5F53\u524D\u63D0\u95EE",
|
|
4257
|
+
children: latestPromptPreview
|
|
4251
4258
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4259
|
+
) : null,
|
|
4260
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex min-w-0 flex-col", children: [
|
|
4261
|
+
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs15("div", { className: "blade-chat-empty", children: [
|
|
4262
|
+
/* @__PURE__ */ jsx17(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
4263
|
+
/* @__PURE__ */ jsx17("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
|
|
4264
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
4265
|
+
] }) : renderBlocks.map((block) => {
|
|
4266
|
+
if (block.type === "message") {
|
|
4267
|
+
return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx17(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx17(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
4268
|
+
}
|
|
4269
|
+
if (block.type === "assistant_turn") {
|
|
4270
|
+
const blockFeedback = block.messages.map(
|
|
4271
|
+
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
4272
|
+
).find((feedback) => feedback != null);
|
|
4273
|
+
const hasActiveFollowup = Boolean(
|
|
4274
|
+
postChatFollowup && block.messages.some(
|
|
4275
|
+
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
4276
|
+
)
|
|
4277
|
+
);
|
|
4278
|
+
return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs15(
|
|
4279
|
+
RenderErrorBoundary,
|
|
4280
|
+
{
|
|
4281
|
+
label: "\u52A9\u624B\u6D88\u606F",
|
|
4282
|
+
details: block.key,
|
|
4283
|
+
resetKey: getMessageResetSignature(block.messages),
|
|
4284
|
+
children: [
|
|
4285
|
+
/* @__PURE__ */ jsx17(
|
|
4286
|
+
AssistantTurnBlock,
|
|
4287
|
+
{
|
|
4288
|
+
messages: block.messages,
|
|
4289
|
+
isStreaming: block.isStreaming,
|
|
4290
|
+
askAnswers,
|
|
4291
|
+
onAnswer,
|
|
4292
|
+
sessionStatus,
|
|
4293
|
+
toolCallRenderer,
|
|
4294
|
+
hidePlanUpdateTools,
|
|
4295
|
+
sessionId
|
|
4296
|
+
}
|
|
4297
|
+
),
|
|
4298
|
+
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx17(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
4299
|
+
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx17(
|
|
4300
|
+
PostChatFollowupBlock,
|
|
4301
|
+
{
|
|
4302
|
+
followup: postChatFollowup,
|
|
4303
|
+
sessionId,
|
|
4304
|
+
onSuggestion,
|
|
4305
|
+
isViewer,
|
|
4306
|
+
onInteraction: onFollowupInteraction,
|
|
4307
|
+
savedFeedback: blockFeedback,
|
|
4308
|
+
onFeedbackSaved: onResultFeedbackSaved
|
|
4309
|
+
}
|
|
4310
|
+
) : null
|
|
4311
|
+
]
|
|
4312
|
+
}
|
|
4313
|
+
) }, block.key);
|
|
4314
|
+
}
|
|
4315
|
+
if (block.type === "compaction") {
|
|
4316
|
+
return /* @__PURE__ */ jsxs15(
|
|
4317
|
+
"div",
|
|
4318
|
+
{
|
|
4319
|
+
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
4320
|
+
children: [
|
|
4321
|
+
/* @__PURE__ */ jsx17(Layers, { size: 12 }),
|
|
4322
|
+
/* @__PURE__ */ jsx17("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
4323
|
+
]
|
|
4324
|
+
},
|
|
4325
|
+
block.key
|
|
4326
|
+
);
|
|
4327
|
+
}
|
|
4328
|
+
return /* @__PURE__ */ jsx17(PlanningDivider, { kind: block.kind }, block.key);
|
|
4329
|
+
}),
|
|
4330
|
+
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx17("div", { className: "flex", children: /* @__PURE__ */ jsx17("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
|
|
4331
|
+
] })
|
|
4332
|
+
] }) }),
|
|
4256
4333
|
/* @__PURE__ */ jsx17(
|
|
4257
4334
|
PinLatestUserMessage,
|
|
4258
4335
|
{
|
|
@@ -4275,8 +4352,8 @@ function PinLatestUserMessage({
|
|
|
4275
4352
|
targetKey
|
|
4276
4353
|
}) {
|
|
4277
4354
|
const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
|
|
4278
|
-
const previousCountRef =
|
|
4279
|
-
const spacerHeightRef =
|
|
4355
|
+
const previousCountRef = useRef13(userMessageCount);
|
|
4356
|
+
const spacerHeightRef = useRef13(0);
|
|
4280
4357
|
const getScrollElement = useCallback7(() => scrollRef.current, [scrollRef]);
|
|
4281
4358
|
const getContentElement = useCallback7(() => contentRef.current, [contentRef]);
|
|
4282
4359
|
const getTargetElement = useCallback7(() => {
|
|
@@ -4305,7 +4382,7 @@ function PinLatestUserMessage({
|
|
|
4305
4382
|
stopAutoScroll: stopScroll,
|
|
4306
4383
|
scrollToBottom
|
|
4307
4384
|
});
|
|
4308
|
-
|
|
4385
|
+
useEffect12(() => {
|
|
4309
4386
|
if (userMessageCount > previousCountRef.current && !shouldPinLatestUser) {
|
|
4310
4387
|
scrollToBottom("instant");
|
|
4311
4388
|
}
|
|
@@ -4315,9 +4392,9 @@ function PinLatestUserMessage({
|
|
|
4315
4392
|
}
|
|
4316
4393
|
function ScrollToBottomButton() {
|
|
4317
4394
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
4318
|
-
const [visible, setVisible] =
|
|
4319
|
-
const hideTimerRef =
|
|
4320
|
-
|
|
4395
|
+
const [visible, setVisible] = useState14(false);
|
|
4396
|
+
const hideTimerRef = useRef13(null);
|
|
4397
|
+
useEffect12(() => {
|
|
4321
4398
|
if (isAtBottom) {
|
|
4322
4399
|
if (!hideTimerRef.current) {
|
|
4323
4400
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -4394,6 +4471,7 @@ function ChatSurface({
|
|
|
4394
4471
|
onInputChange,
|
|
4395
4472
|
onSuggestion,
|
|
4396
4473
|
onSend,
|
|
4474
|
+
onAppend,
|
|
4397
4475
|
onStop,
|
|
4398
4476
|
sessionStatus,
|
|
4399
4477
|
askAnswers,
|
|
@@ -4462,10 +4540,12 @@ function ChatSurface({
|
|
|
4462
4540
|
value: inputText,
|
|
4463
4541
|
onValueChange: onInputChange,
|
|
4464
4542
|
onSend,
|
|
4543
|
+
onAppend,
|
|
4465
4544
|
onStop,
|
|
4466
4545
|
isStreaming,
|
|
4467
4546
|
isStopping,
|
|
4468
4547
|
placeholder,
|
|
4548
|
+
queueKey: sessionId,
|
|
4469
4549
|
className: classNames?.chatInput
|
|
4470
4550
|
}
|
|
4471
4551
|
),
|
|
@@ -4476,13 +4556,13 @@ function ChatSurface({
|
|
|
4476
4556
|
}
|
|
4477
4557
|
|
|
4478
4558
|
// src/components/AgentChat.tsx
|
|
4479
|
-
import { Fragment as
|
|
4559
|
+
import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4480
4560
|
function isUnauthorizedError(error) {
|
|
4481
4561
|
return error instanceof BladeApiError && error.status === 401;
|
|
4482
4562
|
}
|
|
4483
4563
|
function LoginCard({ client, onLoggedIn }) {
|
|
4484
|
-
const [loggingIn, setLoggingIn] =
|
|
4485
|
-
const [loginError, setLoginError] =
|
|
4564
|
+
const [loggingIn, setLoggingIn] = useState15(false);
|
|
4565
|
+
const [loginError, setLoginError] = useState15(null);
|
|
4486
4566
|
const handleLogin = async () => {
|
|
4487
4567
|
setLoggingIn(true);
|
|
4488
4568
|
setLoginError(null);
|
|
@@ -4514,8 +4594,8 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
4514
4594
|
}
|
|
4515
4595
|
function AgentChat(props) {
|
|
4516
4596
|
const client = useBladeClient();
|
|
4517
|
-
const [attempt, setAttempt] =
|
|
4518
|
-
const [needLogin, setNeedLogin] =
|
|
4597
|
+
const [attempt, setAttempt] = useState15(0);
|
|
4598
|
+
const [needLogin, setNeedLogin] = useState15(() => !client.hasToken());
|
|
4519
4599
|
if (needLogin) {
|
|
4520
4600
|
return /* @__PURE__ */ jsx19(
|
|
4521
4601
|
"div",
|
|
@@ -4552,7 +4632,7 @@ function ChatSessionView({
|
|
|
4552
4632
|
onUnauthorized
|
|
4553
4633
|
}) {
|
|
4554
4634
|
const client = useBladeClient();
|
|
4555
|
-
const [planRevealRevisions, setPlanRevealRevisions] =
|
|
4635
|
+
const [planRevealRevisions, setPlanRevealRevisions] = useState15(
|
|
4556
4636
|
() => /* @__PURE__ */ new Map()
|
|
4557
4637
|
);
|
|
4558
4638
|
const handleSessionConnected = useCallback8((connectedSession) => {
|
|
@@ -4573,12 +4653,12 @@ function ChatSessionView({
|
|
|
4573
4653
|
onSessionConnected: handleSessionConnected
|
|
4574
4654
|
});
|
|
4575
4655
|
const replay = useReplay(session);
|
|
4576
|
-
const [stopRequested, setStopRequested] =
|
|
4577
|
-
const [inputText, setInputText] =
|
|
4578
|
-
const [resultFeedback, setResultFeedback] =
|
|
4656
|
+
const [stopRequested, setStopRequested] = useState15(false);
|
|
4657
|
+
const [inputText, setInputText] = useState15("");
|
|
4658
|
+
const [resultFeedback, setResultFeedback] = useState15([]);
|
|
4579
4659
|
const resolvedSessionId = session?.sessionId;
|
|
4580
4660
|
const isViewer = state?.viewerRole === "viewer";
|
|
4581
|
-
|
|
4661
|
+
useEffect13(() => {
|
|
4582
4662
|
setResultFeedback([]);
|
|
4583
4663
|
if (!resolvedSessionId || isViewer) return;
|
|
4584
4664
|
let cancelled = false;
|
|
@@ -4613,12 +4693,12 @@ function ChatSessionView({
|
|
|
4613
4693
|
saved
|
|
4614
4694
|
]);
|
|
4615
4695
|
}, []);
|
|
4616
|
-
|
|
4696
|
+
useEffect13(() => {
|
|
4617
4697
|
if (session) {
|
|
4618
4698
|
onSessionReady?.(session);
|
|
4619
4699
|
}
|
|
4620
4700
|
}, [session, onSessionReady]);
|
|
4621
|
-
|
|
4701
|
+
useEffect13(() => {
|
|
4622
4702
|
if (!session) return;
|
|
4623
4703
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
4624
4704
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -4634,12 +4714,12 @@ ${content}`);
|
|
|
4634
4714
|
offInsert();
|
|
4635
4715
|
};
|
|
4636
4716
|
}, [session]);
|
|
4637
|
-
|
|
4717
|
+
useEffect13(() => {
|
|
4638
4718
|
if (isUnauthorizedError(error)) {
|
|
4639
4719
|
onUnauthorized();
|
|
4640
4720
|
}
|
|
4641
4721
|
}, [error, onUnauthorized]);
|
|
4642
|
-
|
|
4722
|
+
useEffect13(() => {
|
|
4643
4723
|
if (!session || !commands) return;
|
|
4644
4724
|
const unsubscribes = Object.entries(commands).map(
|
|
4645
4725
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -4671,7 +4751,7 @@ ${content}`);
|
|
|
4671
4751
|
slots,
|
|
4672
4752
|
placeholder,
|
|
4673
4753
|
connection: state?.connection ?? "connecting",
|
|
4674
|
-
banner: /* @__PURE__ */ jsxs17(
|
|
4754
|
+
banner: /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
4675
4755
|
/* @__PURE__ */ jsx19(
|
|
4676
4756
|
ReplayBar,
|
|
4677
4757
|
{
|
|
@@ -4697,6 +4777,9 @@ ${content}`);
|
|
|
4697
4777
|
onInputChange: setInputText,
|
|
4698
4778
|
onSuggestion: setInputText,
|
|
4699
4779
|
onSend: handleSend,
|
|
4780
|
+
onAppend: (text) => {
|
|
4781
|
+
void session?.send(text, { mode: state?.mode ?? void 0 });
|
|
4782
|
+
},
|
|
4700
4783
|
onStop: handleStop,
|
|
4701
4784
|
sessionStatus: state?.status ?? void 0,
|
|
4702
4785
|
askAnswers: state?.askAnswers,
|
|
@@ -4713,10 +4796,10 @@ ${content}`);
|
|
|
4713
4796
|
}
|
|
4714
4797
|
|
|
4715
4798
|
// src/components/LlmChat.tsx
|
|
4716
|
-
import { useEffect as
|
|
4799
|
+
import { useEffect as useEffect14, useMemo as useMemo9, useState as useState17 } from "react";
|
|
4717
4800
|
|
|
4718
4801
|
// src/components/LlmAdvancedSettings.tsx
|
|
4719
|
-
import { useState as
|
|
4802
|
+
import { useState as useState16 } from "react";
|
|
4720
4803
|
import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
4721
4804
|
var FIELDS = [
|
|
4722
4805
|
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
@@ -4763,8 +4846,8 @@ function writeOverride(settings, baseURL, override) {
|
|
|
4763
4846
|
}
|
|
4764
4847
|
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
4765
4848
|
const normalized = normalizeAdvanced(settings);
|
|
4766
|
-
const [open, setOpen] =
|
|
4767
|
-
const [draft, setDraft] =
|
|
4849
|
+
const [open, setOpen] = useState16(false);
|
|
4850
|
+
const [draft, setDraft] = useState16(override);
|
|
4768
4851
|
if (!normalized) return null;
|
|
4769
4852
|
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
4770
4853
|
const dirty = Object.keys(override).length > 0;
|
|
@@ -4847,11 +4930,11 @@ function LlmChat({
|
|
|
4847
4930
|
onOverrideChange,
|
|
4848
4931
|
...options
|
|
4849
4932
|
}) {
|
|
4850
|
-
const [override, setOverride] =
|
|
4933
|
+
const [override, setOverride] = useState17(() => readOverride(advanced, options.baseURL));
|
|
4851
4934
|
const effective = { ...options, ...override };
|
|
4852
4935
|
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
4853
|
-
const [inputText, setInputText] =
|
|
4854
|
-
const [stopRequested, setStopRequested] =
|
|
4936
|
+
const [inputText, setInputText] = useState17("");
|
|
4937
|
+
const [stopRequested, setStopRequested] = useState17(false);
|
|
4855
4938
|
const handle = useMemo9(
|
|
4856
4939
|
() => ({
|
|
4857
4940
|
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
@@ -4861,7 +4944,7 @@ ${text}` : text),
|
|
|
4861
4944
|
}),
|
|
4862
4945
|
[send, reset]
|
|
4863
4946
|
);
|
|
4864
|
-
|
|
4947
|
+
useEffect14(() => {
|
|
4865
4948
|
onReady?.(handle);
|
|
4866
4949
|
}, [handle, onReady]);
|
|
4867
4950
|
return /* @__PURE__ */ jsx21(
|
|
@@ -5007,6 +5090,112 @@ function ContextGroupCard({ contexts, className }) {
|
|
|
5007
5090
|
] });
|
|
5008
5091
|
}
|
|
5009
5092
|
|
|
5093
|
+
// src/components/SessionMemoryToggle.tsx
|
|
5094
|
+
import { useCallback as useCallback9, useEffect as useEffect15, useRef as useRef14, useState as useState18, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
5095
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
5096
|
+
var saveStates = /* @__PURE__ */ new WeakMap();
|
|
5097
|
+
function getSaveState(client, sessionId) {
|
|
5098
|
+
let clientStates = saveStates.get(client);
|
|
5099
|
+
if (!clientStates) {
|
|
5100
|
+
clientStates = /* @__PURE__ */ new Map();
|
|
5101
|
+
saveStates.set(client, clientStates);
|
|
5102
|
+
}
|
|
5103
|
+
let state = clientStates.get(sessionId);
|
|
5104
|
+
if (!state) {
|
|
5105
|
+
state = { saving: false, listeners: /* @__PURE__ */ new Set() };
|
|
5106
|
+
clientStates.set(sessionId, state);
|
|
5107
|
+
}
|
|
5108
|
+
return state;
|
|
5109
|
+
}
|
|
5110
|
+
function notify(state) {
|
|
5111
|
+
for (const listener of state.listeners) listener();
|
|
5112
|
+
}
|
|
5113
|
+
function cleanupSaveState(client, sessionId, state) {
|
|
5114
|
+
if (state.saving || state.listeners.size > 0) return;
|
|
5115
|
+
const clientStates = saveStates.get(client);
|
|
5116
|
+
if (clientStates?.get(sessionId) === state) clientStates.delete(sessionId);
|
|
5117
|
+
}
|
|
5118
|
+
function SessionMemoryToggle({
|
|
5119
|
+
sessionId,
|
|
5120
|
+
enabled,
|
|
5121
|
+
client: clientProp,
|
|
5122
|
+
disabled = false,
|
|
5123
|
+
label = "\u5F53\u524D\u4F1A\u8BDD\u4F7F\u7528\u8BB0\u5FC6",
|
|
5124
|
+
className,
|
|
5125
|
+
labelClassName,
|
|
5126
|
+
inputClassName,
|
|
5127
|
+
onSaved,
|
|
5128
|
+
onError
|
|
5129
|
+
}) {
|
|
5130
|
+
const contextClient = useOptionalBladeClient();
|
|
5131
|
+
const client = clientProp ?? contextClient;
|
|
5132
|
+
if (!client) {
|
|
5133
|
+
throw new Error("SessionMemoryToggle \u5FC5\u987B\u5728 <BladeProvider> \u5185\u4F7F\u7528\u6216\u663E\u5F0F\u4F20\u5165 client");
|
|
5134
|
+
}
|
|
5135
|
+
const saveState = getSaveState(client, sessionId);
|
|
5136
|
+
const subscribe = useCallback9(
|
|
5137
|
+
(listener) => {
|
|
5138
|
+
saveState.listeners.add(listener);
|
|
5139
|
+
return () => {
|
|
5140
|
+
saveState.listeners.delete(listener);
|
|
5141
|
+
cleanupSaveState(client, sessionId, saveState);
|
|
5142
|
+
};
|
|
5143
|
+
},
|
|
5144
|
+
[client, saveState, sessionId]
|
|
5145
|
+
);
|
|
5146
|
+
const getSaving = useCallback9(() => saveState.saving, [saveState]);
|
|
5147
|
+
const saving = useSyncExternalStore2(
|
|
5148
|
+
subscribe,
|
|
5149
|
+
getSaving,
|
|
5150
|
+
getSaving
|
|
5151
|
+
);
|
|
5152
|
+
const [draftEnabled, setDraftEnabled] = useState18(enabled);
|
|
5153
|
+
const activeSessionIdRef = useRef14(sessionId);
|
|
5154
|
+
activeSessionIdRef.current = sessionId;
|
|
5155
|
+
useEffect15(() => {
|
|
5156
|
+
setDraftEnabled(enabled);
|
|
5157
|
+
}, [enabled, sessionId]);
|
|
5158
|
+
const update = useCallback9(
|
|
5159
|
+
(nextEnabled) => {
|
|
5160
|
+
const currentSaveState = getSaveState(client, sessionId);
|
|
5161
|
+
if (currentSaveState.saving) return;
|
|
5162
|
+
currentSaveState.saving = true;
|
|
5163
|
+
notify(currentSaveState);
|
|
5164
|
+
setDraftEnabled(nextEnabled);
|
|
5165
|
+
void client.sessions.updateSessionMemory(sessionId, nextEnabled).then(
|
|
5166
|
+
(updated) => {
|
|
5167
|
+
if (activeSessionIdRef.current === sessionId) {
|
|
5168
|
+
setDraftEnabled(updated.memory_enabled);
|
|
5169
|
+
}
|
|
5170
|
+
onSaved?.(sessionId, updated.memory_enabled);
|
|
5171
|
+
},
|
|
5172
|
+
(error) => {
|
|
5173
|
+
if (activeSessionIdRef.current === sessionId) setDraftEnabled(enabled);
|
|
5174
|
+
onError?.(error);
|
|
5175
|
+
}
|
|
5176
|
+
).finally(() => {
|
|
5177
|
+
currentSaveState.saving = false;
|
|
5178
|
+
notify(currentSaveState);
|
|
5179
|
+
cleanupSaveState(client, sessionId, currentSaveState);
|
|
5180
|
+
});
|
|
5181
|
+
},
|
|
5182
|
+
[client, enabled, onError, onSaved, sessionId]
|
|
5183
|
+
);
|
|
5184
|
+
return /* @__PURE__ */ jsxs20("label", { className: cn("flex items-center justify-between", className), children: [
|
|
5185
|
+
/* @__PURE__ */ jsx24("span", { className: labelClassName, children: label }),
|
|
5186
|
+
/* @__PURE__ */ jsx24(
|
|
5187
|
+
"input",
|
|
5188
|
+
{
|
|
5189
|
+
type: "checkbox",
|
|
5190
|
+
checked: draftEnabled,
|
|
5191
|
+
onChange: (event) => update(event.target.checked),
|
|
5192
|
+
disabled: disabled || saving,
|
|
5193
|
+
className: inputClassName
|
|
5194
|
+
}
|
|
5195
|
+
)
|
|
5196
|
+
] });
|
|
5197
|
+
}
|
|
5198
|
+
|
|
5010
5199
|
// src/lib/agent-computer-command.ts
|
|
5011
5200
|
var COMPUTER_LAUNCH_COMMAND_PATTERN = /(?:^|[\n;&|(]\s*)computer\s+launch(?:\s|$)/;
|
|
5012
5201
|
function isAgentComputerCommand(command) {
|
|
@@ -5065,6 +5254,7 @@ export {
|
|
|
5065
5254
|
PlanUpdateBlock,
|
|
5066
5255
|
ReplayBar,
|
|
5067
5256
|
ReplayMismatchPrompt,
|
|
5257
|
+
SessionMemoryToggle,
|
|
5068
5258
|
WhatIfUserBubble,
|
|
5069
5259
|
classifyAgentComputerLaunchOutcome,
|
|
5070
5260
|
collectMemoryRefs,
|