@blade-hq/agent-react 2610.0.0-beta.14 → 2610.0.0-beta.16
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 +1 -0
- package/dist/embed/entry.d.ts +3 -0
- package/dist/index.js +92 -86
- package/dist/index.js.map +1 -1
- package/dist/style.css +2 -1
- package/dist/style.full.css +3 -2
- package/package.json +2 -2
- package/public-api.md +39 -1
package/README.md
CHANGED
|
@@ -151,6 +151,7 @@ await replay.exitToAutonomous() // 退出回放,之后真的运
|
|
|
151
151
|
全部 props 见 `ChatViewProps`。样式说明:
|
|
152
152
|
|
|
153
153
|
`onFollowupInteraction` 使用 `FollowupInteractionEvent`,覆盖下一步建议展示/采纳、成果展示/打开/下载以及结果评分。SDK 不内置 PostHog 等分析厂商;宿主回调抛错也不会中断用户点击或下载。
|
|
154
|
+
文件成果以文件名为文本的标准下载链接展示;Vue / 纯 HTML 使用的 `<blade-chat>` 与 `ChatView` 行为一致。
|
|
154
155
|
|
|
155
156
|
- 必须引入一份样式,按宿主有没有 Tailwind 二选一:
|
|
156
157
|
- **`style.full.css`**(默认选它):布局兜底 + 编译好的 Tailwind 产物,自包含,宿主没装 Tailwind 也是完整视觉。
|
package/dist/embed/entry.d.ts
CHANGED
|
@@ -18,6 +18,9 @@ declare function getChat(element: Element | null): Promise<AgentSession>;
|
|
|
18
18
|
declare const BladeAgent: {
|
|
19
19
|
BladeClient: typeof agentClient.BladeClient;
|
|
20
20
|
BladeApiError: typeof agentClient.BladeApiError;
|
|
21
|
+
EMPTY_PLATFORM_ENDPOINTS: agentClient.PlatformEndpoints;
|
|
22
|
+
loadPlatformEndpoints: typeof agentClient.loadPlatformEndpoints;
|
|
23
|
+
resolveServiceUrl: typeof agentClient.resolveServiceUrl;
|
|
21
24
|
SDK_NAME: "agent-client";
|
|
22
25
|
SDK_VERSION: string;
|
|
23
26
|
AgentSession: typeof AgentSession;
|
package/dist/index.js
CHANGED
|
@@ -697,13 +697,6 @@ var Copy = createLucideIcon("Copy", [
|
|
|
697
697
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
698
698
|
]);
|
|
699
699
|
|
|
700
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/download.js
|
|
701
|
-
var Download = createLucideIcon("Download", [
|
|
702
|
-
["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
|
|
703
|
-
["polyline", { points: "7 10 12 15 17 10", key: "2ggqvy" }],
|
|
704
|
-
["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
|
|
705
|
-
]);
|
|
706
|
-
|
|
707
700
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
708
701
|
var FileText = createLucideIcon("FileText", [
|
|
709
702
|
["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,24 +706,6 @@ var FileText = createLucideIcon("FileText", [
|
|
|
713
706
|
["path", { d: "M16 17H8", key: "z1uh3a" }]
|
|
714
707
|
]);
|
|
715
708
|
|
|
716
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file.js
|
|
717
|
-
var File = createLucideIcon("File", [
|
|
718
|
-
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
719
|
-
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
720
|
-
]);
|
|
721
|
-
|
|
722
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/film.js
|
|
723
|
-
var Film = createLucideIcon("Film", [
|
|
724
|
-
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
|
|
725
|
-
["path", { d: "M7 3v18", key: "bbkbws" }],
|
|
726
|
-
["path", { d: "M3 7.5h4", key: "zfgn84" }],
|
|
727
|
-
["path", { d: "M3 12h18", key: "1i2n21" }],
|
|
728
|
-
["path", { d: "M3 16.5h4", key: "1230mu" }],
|
|
729
|
-
["path", { d: "M17 3v18", key: "in4fa5" }],
|
|
730
|
-
["path", { d: "M17 7.5h4", key: "myr1c1" }],
|
|
731
|
-
["path", { d: "M17 16.5h4", key: "go4c1d" }]
|
|
732
|
-
]);
|
|
733
|
-
|
|
734
709
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
|
|
735
710
|
var Globe = createLucideIcon("Globe", [
|
|
736
711
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
@@ -1128,7 +1103,7 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1128
1103
|
|
|
1129
1104
|
// src/components/MessageList.tsx
|
|
1130
1105
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1131
|
-
import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo7, useRef as
|
|
1106
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef9, useState as useState12 } from "react";
|
|
1132
1107
|
|
|
1133
1108
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1134
1109
|
import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
@@ -1841,8 +1816,37 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
1841
1816
|
import { useState as useState9 } from "react";
|
|
1842
1817
|
|
|
1843
1818
|
// src/components/AskUserQuestionBlock.tsx
|
|
1844
|
-
import { useEffect as useEffect6, useMemo as useMemo6, useState as useState8 } from "react";
|
|
1819
|
+
import { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
|
|
1845
1820
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1821
|
+
var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
|
|
1822
|
+
function resizeCustomTextarea(textarea) {
|
|
1823
|
+
textarea.style.height = "auto";
|
|
1824
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, CUSTOM_TEXTAREA_MAX_HEIGHT)}px`;
|
|
1825
|
+
textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
|
|
1826
|
+
}
|
|
1827
|
+
function useAutoResizeTextarea(value) {
|
|
1828
|
+
const textareaRef = useRef7(null);
|
|
1829
|
+
useEffect6(() => {
|
|
1830
|
+
const textarea = textareaRef.current;
|
|
1831
|
+
if (textarea?.value === value) resizeCustomTextarea(textarea);
|
|
1832
|
+
}, [value]);
|
|
1833
|
+
useEffect6(() => {
|
|
1834
|
+
const textarea = textareaRef.current;
|
|
1835
|
+
if (!textarea || typeof ResizeObserver === "undefined") return;
|
|
1836
|
+
let previousWidth = textarea.clientWidth;
|
|
1837
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
1838
|
+
if (!entry || entry.contentRect.width === previousWidth) return;
|
|
1839
|
+
previousWidth = entry.contentRect.width;
|
|
1840
|
+
resizeCustomTextarea(textarea);
|
|
1841
|
+
});
|
|
1842
|
+
observer.observe(textarea);
|
|
1843
|
+
return () => observer.disconnect();
|
|
1844
|
+
}, []);
|
|
1845
|
+
return textareaRef;
|
|
1846
|
+
}
|
|
1847
|
+
function indentAnswerContinuationLines(answer) {
|
|
1848
|
+
return answer.replaceAll("\n", "\n ");
|
|
1849
|
+
}
|
|
1846
1850
|
function AskUserQuestionBlock({
|
|
1847
1851
|
data,
|
|
1848
1852
|
answered,
|
|
@@ -1939,7 +1943,9 @@ function AskUserQuestionBlock({
|
|
|
1939
1943
|
Array.from(usingCustom).map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? "").trim()]).filter(([, text2]) => text2.length > 0)
|
|
1940
1944
|
)
|
|
1941
1945
|
};
|
|
1942
|
-
const parts = data.questions.map(
|
|
1946
|
+
const parts = data.questions.map(
|
|
1947
|
+
(q, i) => `- ${q.question} -> ${indentAnswerContinuationLines(getAnswer(i) ?? "")}`
|
|
1948
|
+
);
|
|
1943
1949
|
const text = `\u5173\u4E8E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\uFF0C\u7528\u6237\u7684\u56DE\u7B54\u5982\u4E0B\uFF1A
|
|
1944
1950
|
${parts.join("\n")}`;
|
|
1945
1951
|
setSubmitted(true);
|
|
@@ -2011,6 +2017,7 @@ function QuestionCard({
|
|
|
2011
2017
|
onCustomChange
|
|
2012
2018
|
}) {
|
|
2013
2019
|
const multi = question.multiSelect ?? false;
|
|
2020
|
+
const customTextareaRef = useAutoResizeTextarea(customText);
|
|
2014
2021
|
return /* @__PURE__ */ jsxs7("div", { children: [
|
|
2015
2022
|
/* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
2016
2023
|
/* @__PURE__ */ jsx9(
|
|
@@ -2087,25 +2094,26 @@ function QuestionCard({
|
|
|
2087
2094
|
"div",
|
|
2088
2095
|
{
|
|
2089
2096
|
className: cn(
|
|
2090
|
-
"flex items-
|
|
2097
|
+
"flex items-start gap-2 rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]",
|
|
2091
2098
|
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2092
2099
|
isCustom ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
|
|
2093
2100
|
answered && "cursor-default opacity-70"
|
|
2094
2101
|
),
|
|
2095
2102
|
children: [
|
|
2096
|
-
/* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2103
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2097
2104
|
/* @__PURE__ */ jsx9(
|
|
2098
|
-
"
|
|
2105
|
+
"textarea",
|
|
2099
2106
|
{
|
|
2100
|
-
|
|
2107
|
+
ref: customTextareaRef,
|
|
2108
|
+
rows: 2,
|
|
2101
2109
|
value: customText,
|
|
2102
|
-
|
|
2110
|
+
readOnly: answered,
|
|
2103
2111
|
onChange: (e) => onCustomChange(qIdx, e.target.value),
|
|
2104
2112
|
onFocus: () => onCustomFocus(qIdx),
|
|
2105
2113
|
"aria-label": "\u81EA\u5B9A\u4E49\u56DE\u7B54",
|
|
2106
2114
|
placeholder: "\u8F93\u5165\u4F60\u7684\u7B54\u6848...",
|
|
2107
2115
|
className: cn(
|
|
2108
|
-
"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2116
|
+
"min-h-10 min-w-0 flex-1 resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2109
2117
|
answered ? "text-xs" : "text-sm"
|
|
2110
2118
|
)
|
|
2111
2119
|
}
|
|
@@ -2436,7 +2444,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2436
2444
|
};
|
|
2437
2445
|
|
|
2438
2446
|
// src/components/PostChatFollowupBlock.tsx
|
|
2439
|
-
import { useCallback as useCallback5, useEffect as useEffect7, useRef as
|
|
2447
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef8, useState as useState11 } from "react";
|
|
2440
2448
|
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2441
2449
|
function emitInteraction(callback, event) {
|
|
2442
2450
|
try {
|
|
@@ -2447,9 +2455,6 @@ function emitInteraction(callback, event) {
|
|
|
2447
2455
|
function basename(path) {
|
|
2448
2456
|
return path.split(/[\\/]/).filter(Boolean).pop() || path;
|
|
2449
2457
|
}
|
|
2450
|
-
function isVideo(path) {
|
|
2451
|
-
return /\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path);
|
|
2452
|
-
}
|
|
2453
2458
|
function ArtifactCard({
|
|
2454
2459
|
artifact,
|
|
2455
2460
|
sessionId,
|
|
@@ -2480,44 +2485,48 @@ ${artifact.target}`,
|
|
|
2480
2485
|
}
|
|
2481
2486
|
);
|
|
2482
2487
|
}
|
|
2483
|
-
const
|
|
2484
|
-
|
|
2485
|
-
|
|
2488
|
+
const fileName = basename(artifact.target);
|
|
2489
|
+
const downloadUrl = sessionId ? client.buildAuthedUrl(
|
|
2490
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`
|
|
2491
|
+
) : void 0;
|
|
2492
|
+
const handleDownload = async (event) => {
|
|
2493
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
2494
|
+
event.preventDefault();
|
|
2495
|
+
if (!sessionId || downloading) return;
|
|
2496
|
+
setDownloading(true);
|
|
2497
|
+
emitInteraction(onInteraction, {
|
|
2498
|
+
type: "artifact_download_started",
|
|
2499
|
+
sessionId,
|
|
2500
|
+
assistantEntryId,
|
|
2501
|
+
artifactIndex,
|
|
2502
|
+
artifactKind: "file"
|
|
2503
|
+
});
|
|
2504
|
+
try {
|
|
2505
|
+
await client.sessions.downloadFile(sessionId, artifact.target, fileName);
|
|
2506
|
+
emitInteraction(onInteraction, {
|
|
2507
|
+
type: "artifact_download_succeeded",
|
|
2508
|
+
sessionId,
|
|
2509
|
+
assistantEntryId,
|
|
2510
|
+
artifactIndex,
|
|
2511
|
+
artifactKind: "file"
|
|
2512
|
+
});
|
|
2513
|
+
} catch {
|
|
2514
|
+
} finally {
|
|
2515
|
+
setDownloading(false);
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
return /* @__PURE__ */ jsx13(
|
|
2519
|
+
"a",
|
|
2486
2520
|
{
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
onClick:
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
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
|
+
href: downloadUrl,
|
|
2522
|
+
download: fileName,
|
|
2523
|
+
onClick: handleDownload,
|
|
2524
|
+
title: fileName,
|
|
2525
|
+
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${fileName}`,
|
|
2526
|
+
"aria-disabled": !sessionId || void 0,
|
|
2527
|
+
"aria-busy": downloading || void 0,
|
|
2528
|
+
className: "min-w-0 cursor-pointer break-all text-xs text-[hsl(var(--primary))] underline aria-disabled:cursor-not-allowed aria-disabled:opacity-60 aria-busy:cursor-wait",
|
|
2529
|
+
children: fileName
|
|
2521
2530
|
}
|
|
2522
2531
|
);
|
|
2523
2532
|
}
|
|
@@ -2566,8 +2575,8 @@ function ResultFeedback({
|
|
|
2566
2575
|
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
2567
2576
|
const [saving, setSaving] = useState11(false);
|
|
2568
2577
|
const [saveError, setSaveError] = useState11(false);
|
|
2569
|
-
const reportedShown =
|
|
2570
|
-
const latestChoice =
|
|
2578
|
+
const reportedShown = useRef8(false);
|
|
2579
|
+
const latestChoice = useRef8(null);
|
|
2571
2580
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2572
2581
|
useEffect7(() => {
|
|
2573
2582
|
if (!eligible || reportedShown.current) return;
|
|
@@ -2691,10 +2700,10 @@ function PostChatFollowupBlock({
|
|
|
2691
2700
|
onFeedbackSaved
|
|
2692
2701
|
}) {
|
|
2693
2702
|
const [expanded, setExpanded] = useState11(false);
|
|
2694
|
-
const adopted =
|
|
2695
|
-
const reportedSuggestions =
|
|
2696
|
-
const reportedArtifacts =
|
|
2697
|
-
const openedArtifacts =
|
|
2703
|
+
const adopted = useRef8(/* @__PURE__ */ new Set());
|
|
2704
|
+
const reportedSuggestions = useRef8(false);
|
|
2705
|
+
const reportedArtifacts = useRef8(/* @__PURE__ */ new Set());
|
|
2706
|
+
const openedArtifacts = useRef8(/* @__PURE__ */ new Set());
|
|
2698
2707
|
const artifacts = followup.final_artifacts ?? [];
|
|
2699
2708
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2700
2709
|
useEffect7(() => {
|
|
@@ -3071,7 +3080,7 @@ function MessageList({
|
|
|
3071
3080
|
}
|
|
3072
3081
|
function AutoScrollOnUserSend({ userMessageCount }) {
|
|
3073
3082
|
const { scrollToBottom } = useStickToBottomContext();
|
|
3074
|
-
const previousCountRef =
|
|
3083
|
+
const previousCountRef = useRef9(userMessageCount);
|
|
3075
3084
|
useEffect8(() => {
|
|
3076
3085
|
if (userMessageCount > previousCountRef.current) {
|
|
3077
3086
|
scrollToBottom("instant");
|
|
@@ -3083,7 +3092,7 @@ function AutoScrollOnUserSend({ userMessageCount }) {
|
|
|
3083
3092
|
function ScrollToBottomButton() {
|
|
3084
3093
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3085
3094
|
const [visible, setVisible] = useState12(false);
|
|
3086
|
-
const hideTimerRef =
|
|
3095
|
+
const hideTimerRef = useRef9(null);
|
|
3087
3096
|
useEffect8(() => {
|
|
3088
3097
|
if (isAtBottom) {
|
|
3089
3098
|
if (!hideTimerRef.current) {
|
|
@@ -3696,10 +3705,7 @@ lucide-react/dist/esm/icons/chevron-down.js:
|
|
|
3696
3705
|
lucide-react/dist/esm/icons/chevron-right.js:
|
|
3697
3706
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3698
3707
|
lucide-react/dist/esm/icons/copy.js:
|
|
3699
|
-
lucide-react/dist/esm/icons/download.js:
|
|
3700
3708
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3701
|
-
lucide-react/dist/esm/icons/file.js:
|
|
3702
|
-
lucide-react/dist/esm/icons/film.js:
|
|
3703
3709
|
lucide-react/dist/esm/icons/globe.js:
|
|
3704
3710
|
lucide-react/dist/esm/icons/layers.js:
|
|
3705
3711
|
lucide-react/dist/esm/icons/lightbulb.js:
|