@blade-hq/agent-kit 1.0.27 → 1.0.29
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/{chunk-M72OL7ZJ.js → chunk-HCQDDDOX.js} +2 -2
- package/dist/{chunk-X2NJKKXR.js → chunk-N2OE5MVV.js} +253 -52
- package/dist/chunk-N2OE5MVV.js.map +1 -0
- package/dist/{chunk-C2FKVGJU.js → chunk-PYYDXV2A.js} +2 -2
- package/dist/{chunk-JU5SL4HP.js → chunk-TVWC3PED.js} +2 -2
- package/dist/{chunk-SBASG3M2.js → chunk-UWNQ4PXK.js} +4 -1101
- package/dist/chunk-UWNQ4PXK.js.map +1 -0
- package/dist/{chunk-34GDDOWY.js → chunk-YJ6BX3FU.js} +1379 -31
- package/dist/chunk-YJ6BX3FU.js.map +1 -0
- package/dist/{chunk-TE6G6NVX.js → chunk-YXHPJGXR.js} +27 -13
- package/dist/chunk-YXHPJGXR.js.map +1 -0
- package/dist/client/blade-client.d.ts +2 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +3 -1
- package/dist/client/resources/app-dev-templates.d.ts +93 -0
- package/dist/client/resources/models.d.ts +3 -0
- package/dist/{react → client/shared}/projection/builder.d.ts +1 -1
- package/dist/{react → client/shared}/projection/helpers.d.ts +1 -1
- package/dist/client/shared/projection/history.d.ts +12 -0
- package/dist/{react → client/shared}/projection/index.d.ts +1 -0
- package/dist/{react → client/shared}/projection/state.d.ts +1 -1
- package/dist/client/types/index.d.ts +2 -2
- package/dist/client/types/rest.d.ts +1104 -66
- package/dist/client/types/socket-events.d.ts +18 -132
- package/dist/react/api/app-dev-templates.d.ts +5 -0
- package/dist/react/api/model-config.d.ts +68 -0
- package/dist/react/api/models.d.ts +2 -0
- package/dist/react/api/published-apps.js +3 -3
- package/dist/react/api/sessions.js +2 -2
- package/dist/react/components/chat/AskUserQuestionBlock.d.ts +1 -1
- package/dist/react/components/chat/ChatInput.d.ts +10 -4
- package/dist/react/components/chat/ChatView.d.ts +3 -1
- package/dist/react/components/chat/MessageList.d.ts +3 -1
- package/dist/react/components/chat/index.d.ts +1 -1
- package/dist/react/components/chat/index.js +5 -5
- package/dist/react/components/chat/scroll-anchor.d.ts +8 -0
- package/dist/react/components/plan/index.js +4 -4
- package/dist/react/components/session/index.js +3 -3
- package/dist/react/components/workspace/index.js +3 -3
- package/dist/react/hooks/use-model-preferences.d.ts +1 -0
- package/dist/react/hooks/use-resolved-model.d.ts +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +87 -7
- package/dist/react/index.js.map +1 -1
- package/dist/react/schemas/session.d.ts +1 -0
- package/package.json +1 -1
- package/dist/chunk-34GDDOWY.js.map +0 -1
- package/dist/chunk-SBASG3M2.js.map +0 -1
- package/dist/chunk-TE6G6NVX.js.map +0 -1
- package/dist/chunk-X2NJKKXR.js.map +0 -1
- /package/dist/{chunk-M72OL7ZJ.js.map → chunk-HCQDDDOX.js.map} +0 -0
- /package/dist/{chunk-C2FKVGJU.js.map → chunk-PYYDXV2A.js.map} +0 -0
- /package/dist/{chunk-JU5SL4HP.js.map → chunk-TVWC3PED.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClient
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UWNQ4PXK.js";
|
|
4
4
|
import {
|
|
5
5
|
__export
|
|
6
6
|
} from "./chunk-PZ5AY32C.js";
|
|
@@ -20,4 +20,4 @@ export {
|
|
|
20
20
|
unpublishApp,
|
|
21
21
|
published_apps_exports
|
|
22
22
|
};
|
|
23
|
-
//# sourceMappingURL=chunk-
|
|
23
|
+
//# sourceMappingURL=chunk-HCQDDDOX.js.map
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
getCodeLanguageFromFilename,
|
|
14
14
|
parseAskUserQuestion,
|
|
15
15
|
useHighlightedCodeHtml
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YXHPJGXR.js";
|
|
17
17
|
import {
|
|
18
18
|
resolveSessionFilePreviewTarget
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-PYYDXV2A.js";
|
|
20
20
|
import {
|
|
21
21
|
apiFetchResponse,
|
|
22
22
|
buildMessageContent,
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
useUiBridgeStore,
|
|
61
61
|
useUiStore,
|
|
62
62
|
writeFile
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-UWNQ4PXK.js";
|
|
64
64
|
import {
|
|
65
65
|
registerBridgeIframe,
|
|
66
66
|
tapBridgeEvent
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
ModelOption,
|
|
70
70
|
ModelsConfig,
|
|
71
71
|
ModelsResource
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-YJ6BX3FU.js";
|
|
73
73
|
import {
|
|
74
74
|
cn,
|
|
75
75
|
copyToClipboard
|
|
@@ -704,6 +704,7 @@ function useModelConfig() {
|
|
|
704
704
|
return {
|
|
705
705
|
models: data?.models ?? [],
|
|
706
706
|
defaultModel: data?.default ?? "",
|
|
707
|
+
thinkingAvailable: data?.thinkingAvailable ?? false,
|
|
707
708
|
isLoading
|
|
708
709
|
};
|
|
709
710
|
}
|
|
@@ -1000,9 +1001,9 @@ function useInputHistory(sessionId) {
|
|
|
1000
1001
|
// src/react/hooks/use-resolved-model.ts
|
|
1001
1002
|
import { useMemo as useMemo3 } from "react";
|
|
1002
1003
|
function useResolvedModel(activeSessionModel) {
|
|
1003
|
-
const { models, defaultModel, isLoading } = useModelConfig();
|
|
1004
|
+
const { models, defaultModel, thinkingAvailable, isLoading } = useModelConfig();
|
|
1004
1005
|
const { preferredModel } = usePreferredModel();
|
|
1005
|
-
|
|
1006
|
+
const resolved = useMemo3(() => {
|
|
1006
1007
|
if (isLoading) {
|
|
1007
1008
|
const optimistic = activeSessionModel || preferredModel || defaultModel || "";
|
|
1008
1009
|
return {
|
|
@@ -1027,6 +1028,8 @@ function useResolvedModel(activeSessionModel) {
|
|
|
1027
1028
|
}
|
|
1028
1029
|
return { resolvedModel: "", hasAvailableModel: false, isLoading };
|
|
1029
1030
|
}, [activeSessionModel, preferredModel, defaultModel, models, isLoading]);
|
|
1031
|
+
const selectedThinkingAvailable = models.find((model) => model.id === resolved.resolvedModel)?.thinkingAvailable ?? thinkingAvailable;
|
|
1032
|
+
return { ...resolved, thinkingAvailable: selectedThinkingAvailable };
|
|
1030
1033
|
}
|
|
1031
1034
|
|
|
1032
1035
|
// src/react/lib/attachment-upload.ts
|
|
@@ -2849,6 +2852,7 @@ function ChatInput({
|
|
|
2849
2852
|
onCommand,
|
|
2850
2853
|
canShareSession = false,
|
|
2851
2854
|
slotAboveTextarea,
|
|
2855
|
+
slotBeforeSend,
|
|
2852
2856
|
externalDraft,
|
|
2853
2857
|
externalAttachments,
|
|
2854
2858
|
maxWidthClassName = "max-w-3xl",
|
|
@@ -2935,7 +2939,12 @@ function ChatInput({
|
|
|
2935
2939
|
const draftAppends = useUiBridgeStore(
|
|
2936
2940
|
(state) => activeSessionId ? state.draftAppends[activeSessionId] : void 0
|
|
2937
2941
|
);
|
|
2938
|
-
const {
|
|
2942
|
+
const {
|
|
2943
|
+
resolvedModel,
|
|
2944
|
+
hasAvailableModel,
|
|
2945
|
+
thinkingAvailable,
|
|
2946
|
+
isLoading: isModelLoading
|
|
2947
|
+
} = useResolvedModel(activeSessionModel);
|
|
2939
2948
|
useEffect7(() => {
|
|
2940
2949
|
setSelectedModel(resolvedModel);
|
|
2941
2950
|
}, [resolvedModel]);
|
|
@@ -3301,6 +3310,7 @@ function ChatInput({
|
|
|
3301
3310
|
setRewindDraft(activeSessionId, null);
|
|
3302
3311
|
}, [activeSessionId, editor, rewindDraft, setRewindDraft]);
|
|
3303
3312
|
const submittingRef = useRef5(false);
|
|
3313
|
+
const [isSubmitting, setIsSubmitting] = useState8(false);
|
|
3304
3314
|
useEffect7(() => {
|
|
3305
3315
|
let changed = false;
|
|
3306
3316
|
const nextLocalIds = /* @__PURE__ */ new Set();
|
|
@@ -3425,6 +3435,7 @@ function ChatInput({
|
|
|
3425
3435
|
return;
|
|
3426
3436
|
}
|
|
3427
3437
|
submittingRef.current = true;
|
|
3438
|
+
setIsSubmitting(true);
|
|
3428
3439
|
try {
|
|
3429
3440
|
const json = editor?.getJSON();
|
|
3430
3441
|
const rawText = serializeEditorContent(json);
|
|
@@ -3437,9 +3448,15 @@ function ChatInput({
|
|
|
3437
3448
|
let uploadTargetSessionId;
|
|
3438
3449
|
if (pendingFiles.length > 0) {
|
|
3439
3450
|
let uploadSessionId = activeSessionId;
|
|
3451
|
+
let pendingFilesHandled = false;
|
|
3440
3452
|
if (!uploadSessionId && ensureSession) {
|
|
3441
3453
|
try {
|
|
3442
|
-
|
|
3454
|
+
const ensured = await ensureSession(text || "\u65B0\u4F1A\u8BDD", {
|
|
3455
|
+
model: selectedModel || null,
|
|
3456
|
+
thinkingOverride: thinkingAvailable && (sessionThinking != null || thinkingPreference != null) ? thinkingEnabled : void 0
|
|
3457
|
+
});
|
|
3458
|
+
uploadSessionId = typeof ensured === "string" ? ensured : ensured.sessionId;
|
|
3459
|
+
pendingFilesHandled = typeof ensured === "object" && ensured.pendingFilesHandled === true;
|
|
3443
3460
|
} catch {
|
|
3444
3461
|
setComposerAttachments(
|
|
3445
3462
|
(prev) => prev.map(
|
|
@@ -3451,7 +3468,12 @@ function ChatInput({
|
|
|
3451
3468
|
}
|
|
3452
3469
|
if (uploadSessionId) {
|
|
3453
3470
|
uploadTargetSessionId = uploadSessionId;
|
|
3454
|
-
if (
|
|
3471
|
+
if (pendingFilesHandled) {
|
|
3472
|
+
updatedAttachments = composerAttachments.map(
|
|
3473
|
+
(attachment) => pendingFiles.some((pending) => pending.id === attachment.id) ? { ...attachment, status: "uploaded", uploadedPath: attachment.name } : attachment
|
|
3474
|
+
);
|
|
3475
|
+
setComposerAttachments(updatedAttachments);
|
|
3476
|
+
} else if (pendingFiles.length > 0) {
|
|
3455
3477
|
setComposerAttachments(
|
|
3456
3478
|
(prev) => prev.map(
|
|
3457
3479
|
(a) => a.kind === "file" && a.status === "pending" ? { ...a, status: "uploading", uploadProgress: 0 } : a
|
|
@@ -3525,17 +3547,23 @@ function ChatInput({
|
|
|
3525
3547
|
if (activeSessionId && activePendingContexts.length > 0) {
|
|
3526
3548
|
consumePendingContexts(activeSessionId);
|
|
3527
3549
|
}
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3550
|
+
try {
|
|
3551
|
+
await onSend(nextPayload, {
|
|
3552
|
+
targetSessionId: uploadTargetSessionId,
|
|
3553
|
+
model: selectedModel || null,
|
|
3554
|
+
thinkingOverride: thinkingAvailable && (sessionThinking != null || thinkingPreference != null) ? thinkingEnabled : void 0
|
|
3555
|
+
});
|
|
3556
|
+
} catch (error) {
|
|
3557
|
+
toast.error(error instanceof Error ? error.message : "\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
3558
|
+
return;
|
|
3559
|
+
}
|
|
3533
3560
|
if (text.trim()) {
|
|
3534
3561
|
inputHistory.addEntry(text);
|
|
3535
3562
|
}
|
|
3536
3563
|
resetComposer();
|
|
3537
3564
|
} finally {
|
|
3538
3565
|
submittingRef.current = false;
|
|
3566
|
+
setIsSubmitting(false);
|
|
3539
3567
|
}
|
|
3540
3568
|
};
|
|
3541
3569
|
const removeAttachment = (id) => {
|
|
@@ -3561,7 +3589,7 @@ function ChatInput({
|
|
|
3561
3589
|
(attachment) => attachment.status === "uploading"
|
|
3562
3590
|
);
|
|
3563
3591
|
const hasValidAttachments = composerAttachments.some((attachment) => attachment.status !== "failed");
|
|
3564
|
-
const isSendDisabled = connectionStatus !== "connected" || isModelLoading || !hasAvailableModel || hasUploadingFiles || !input.trim() && !hasValidAttachments && !hasRenderedAttachments && !hasPendingContexts;
|
|
3592
|
+
const isSendDisabled = connectionStatus !== "connected" || isModelLoading || !hasAvailableModel || isSubmitting || hasUploadingFiles || !input.trim() && !hasValidAttachments && !hasRenderedAttachments && !hasPendingContexts;
|
|
3565
3593
|
return /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
3566
3594
|
/* @__PURE__ */ jsx10("div", { className: `bg-[hsl(var(--background))] px-5 py-3 ${className ?? ""}`, children: /* @__PURE__ */ jsxs9("div", { className: `mx-auto ${maxWidthClassName} ${innerClassName ?? ""}`, children: [
|
|
3567
3595
|
activeSessionId ? /* @__PURE__ */ jsx10(BackgroundTasksPill, { sessionId: activeSessionId }) : null,
|
|
@@ -3859,7 +3887,7 @@ function ChatInput({
|
|
|
3859
3887
|
}
|
|
3860
3888
|
)
|
|
3861
3889
|
] }) : null,
|
|
3862
|
-
/* @__PURE__ */ jsx10(
|
|
3890
|
+
thinkingAvailable ? /* @__PURE__ */ jsx10(
|
|
3863
3891
|
ThinkingToggle,
|
|
3864
3892
|
{
|
|
3865
3893
|
enabled: thinkingEnabled,
|
|
@@ -3868,7 +3896,7 @@ function ChatInput({
|
|
|
3868
3896
|
setPreferredThinkingEnabled(next);
|
|
3869
3897
|
}
|
|
3870
3898
|
}
|
|
3871
|
-
),
|
|
3899
|
+
) : null,
|
|
3872
3900
|
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 px-3 py-2", children: [
|
|
3873
3901
|
/* @__PURE__ */ jsx10(Sparkles2, { size: 13 }),
|
|
3874
3902
|
/* @__PURE__ */ jsx10("span", { className: "shrink-0", children: "\u6A21\u578B" }),
|
|
@@ -3929,6 +3957,7 @@ function ChatInput({
|
|
|
3929
3957
|
children: !asrEnabled ? /* @__PURE__ */ jsx10(MicOff, { size: 13 }) : isRecording ? /* @__PURE__ */ jsx10(VoiceWaveform, { level: voiceLevel, size: 18 }) : /* @__PURE__ */ jsx10(Mic, { size: 13 })
|
|
3930
3958
|
}
|
|
3931
3959
|
),
|
|
3960
|
+
slotBeforeSend,
|
|
3932
3961
|
isStreaming ? /* @__PURE__ */ jsx10(
|
|
3933
3962
|
"button",
|
|
3934
3963
|
{
|
|
@@ -3948,7 +3977,7 @@ function ChatInput({
|
|
|
3948
3977
|
"aria-label": "\u53D1\u9001\u6D88\u606F",
|
|
3949
3978
|
title: isModelLoading ? "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u914D\u7F6E" : !hasAvailableModel ? "\u6CA1\u6709\u53EF\u7528\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5\u6A21\u578B\u670D\u52A1\u914D\u7F6E" : "\u53D1\u9001\u6D88\u606F",
|
|
3950
3979
|
className: "flex h-7 w-7 items-center justify-center rounded-lg bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-80 disabled:opacity-25",
|
|
3951
|
-
children: /* @__PURE__ */ jsx10(Send, { size: 13 })
|
|
3980
|
+
children: isSubmitting ? /* @__PURE__ */ jsx10(Loader22, { size: 13, className: "animate-spin" }) : /* @__PURE__ */ jsx10(Send, { size: 13 })
|
|
3952
3981
|
}
|
|
3953
3982
|
)
|
|
3954
3983
|
] })
|
|
@@ -5997,7 +6026,7 @@ function ToolCallBlock({
|
|
|
5997
6026
|
}, [shouldAutoExpandWhileStreaming]);
|
|
5998
6027
|
const resolvedSessionStatus = sessionStatus ?? sessions.find((session) => session.id === resolvedSessionId)?.status;
|
|
5999
6028
|
const resolvedAnswerData = answerData ?? storedAnswerData;
|
|
6000
|
-
const resolvedAnswered = answered ?? (!!resolvedAnswerData && resolvedSessionStatus !== "waiting_for_input");
|
|
6029
|
+
const resolvedAnswered = toolCall.status === "awaiting_answer" ? false : answered ?? (!!resolvedAnswerData && resolvedSessionStatus !== "waiting_for_input");
|
|
6001
6030
|
const resolvedOnAnswer = onAnswer ?? answerCallback;
|
|
6002
6031
|
const uiMeta = useMemo13(() => {
|
|
6003
6032
|
const block = turnBlocks?.find(
|
|
@@ -6007,9 +6036,8 @@ function ToolCallBlock({
|
|
|
6007
6036
|
}, [toolCall.id, turnBlocks]);
|
|
6008
6037
|
if (normalizedName === "AskUserQuestion") {
|
|
6009
6038
|
const askData = parseAskUserQuestion(typeof toolCall.result === "string" ? toolCall.result : null) ?? parseAskUserQuestion(buildAskUserPayload(toolCall.arguments));
|
|
6010
|
-
const canAnswer = toolCall.status === "awaiting_answer" &&
|
|
6011
|
-
|
|
6012
|
-
if (askData && shouldRenderQuestion) {
|
|
6039
|
+
const canAnswer = toolCall.status === "awaiting_answer" && resolvedSessionStatus === "waiting_for_input" && Boolean(resolvedOnAnswer);
|
|
6040
|
+
if (askData) {
|
|
6013
6041
|
const questionBlock = /* @__PURE__ */ jsx22(
|
|
6014
6042
|
AskUserQuestionBlock,
|
|
6015
6043
|
{
|
|
@@ -6029,6 +6057,14 @@ function ToolCallBlock({
|
|
|
6029
6057
|
/* @__PURE__ */ jsx22("div", { className: "ml-4", children: /* @__PURE__ */ jsx22(ThinkingBadge, { reasoning, variant: "block" }) })
|
|
6030
6058
|
] });
|
|
6031
6059
|
}
|
|
6060
|
+
return /* @__PURE__ */ jsxs18("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
|
|
6061
|
+
/* @__PURE__ */ jsx22("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
|
|
6062
|
+
/* @__PURE__ */ jsxs18("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: [
|
|
6063
|
+
"\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u6216\u7248\u672C\u4E0D\u517C\u5BB9\u3002\u8BF7\u91CD\u8BD5\uFF1B\u82E5\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u63D0\u4F9B\u4EA4\u4E92\u7F16\u53F7",
|
|
6064
|
+
/* @__PURE__ */ jsx22("span", { className: "ml-1 font-mono", children: toolCall.id }),
|
|
6065
|
+
"\u3002"
|
|
6066
|
+
] })
|
|
6067
|
+
] });
|
|
6032
6068
|
}
|
|
6033
6069
|
const filePath = extractToolFilePath(toolCall);
|
|
6034
6070
|
const tone = getToolTone(toolCall.status);
|
|
@@ -8218,6 +8254,21 @@ function TurnNavRail({
|
|
|
8218
8254
|
);
|
|
8219
8255
|
}
|
|
8220
8256
|
|
|
8257
|
+
// src/react/components/chat/scroll-anchor.ts
|
|
8258
|
+
var ENTRY_SCROLL_MARGIN_PX = 24;
|
|
8259
|
+
function normalizeScrollEntryKey(value) {
|
|
8260
|
+
const text = value?.trim();
|
|
8261
|
+
return text ? text : null;
|
|
8262
|
+
}
|
|
8263
|
+
function calculateAnchoredScrollTop({
|
|
8264
|
+
scrollTop,
|
|
8265
|
+
containerTop,
|
|
8266
|
+
targetTop,
|
|
8267
|
+
margin = ENTRY_SCROLL_MARGIN_PX
|
|
8268
|
+
}) {
|
|
8269
|
+
return Math.max(0, scrollTop + targetTop - containerTop - margin);
|
|
8270
|
+
}
|
|
8271
|
+
|
|
8221
8272
|
// src/react/components/chat/MessageList.tsx
|
|
8222
8273
|
import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
8223
8274
|
function parseModeChange(message) {
|
|
@@ -8296,13 +8347,20 @@ function findScrollContainer(start) {
|
|
|
8296
8347
|
}
|
|
8297
8348
|
return null;
|
|
8298
8349
|
}
|
|
8350
|
+
function findEntryAnchor(root, entryId) {
|
|
8351
|
+
return Array.from(root.querySelectorAll("[data-entry-id]")).find(
|
|
8352
|
+
(el) => el.dataset.entryId === entryId
|
|
8353
|
+
) ?? null;
|
|
8354
|
+
}
|
|
8299
8355
|
function MessageList({
|
|
8300
8356
|
sessionId,
|
|
8301
8357
|
messages: rawMessages,
|
|
8302
8358
|
onAnswer,
|
|
8303
8359
|
sessionStatus,
|
|
8304
8360
|
onConfirmPlan,
|
|
8305
|
-
customization
|
|
8361
|
+
customization,
|
|
8362
|
+
scrollToStartKey,
|
|
8363
|
+
scrollToEntryKey
|
|
8306
8364
|
}) {
|
|
8307
8365
|
const messages = Array.isArray(rawMessages) ? rawMessages : [];
|
|
8308
8366
|
const agentLoops = useChatStore((state) => state.agentLoops[sessionId]);
|
|
@@ -8381,7 +8439,8 @@ function MessageList({
|
|
|
8381
8439
|
blocks.push({
|
|
8382
8440
|
type: "message",
|
|
8383
8441
|
message,
|
|
8384
|
-
key: message.entry_id ?? `${message.role}-${blocks.length}
|
|
8442
|
+
key: message.entry_id ?? `${message.role}-${blocks.length}`,
|
|
8443
|
+
anchorId: message.entry_id ? `chat-entry-${message.entry_id}` : null
|
|
8385
8444
|
});
|
|
8386
8445
|
}
|
|
8387
8446
|
flushAssistant();
|
|
@@ -8429,9 +8488,18 @@ function MessageList({
|
|
|
8429
8488
|
) ?? null,
|
|
8430
8489
|
[lastTurnId, renderBlocks]
|
|
8431
8490
|
);
|
|
8491
|
+
const scrollEntryId = useMemo18(() => normalizeScrollEntryKey(scrollToEntryKey), [scrollToEntryKey]);
|
|
8492
|
+
const shouldAnchorEntry = Boolean(scrollEntryId);
|
|
8493
|
+
const hasMessages = messages.length > 0;
|
|
8432
8494
|
const containerRef = useRef13(null);
|
|
8433
8495
|
const scrollContainerRef = useRef13(null);
|
|
8434
8496
|
const frameRef = useRef13(null);
|
|
8497
|
+
const startScrollFrameRef = useRef13(null);
|
|
8498
|
+
const startScrollTimeoutRef = useRef13(null);
|
|
8499
|
+
const entryScrollFrameRef = useRef13(null);
|
|
8500
|
+
const entryScrollTimeoutRefs = useRef13([]);
|
|
8501
|
+
const appliedStartScrollKeyRef = useRef13(null);
|
|
8502
|
+
const appliedEntryScrollKeyRef = useRef13(null);
|
|
8435
8503
|
const [activeTurnId, setActiveTurnId] = useState21(lastTurnId);
|
|
8436
8504
|
const layoutSignature = useMemo18(
|
|
8437
8505
|
() => getMessagesMeasureSignature(messages),
|
|
@@ -8492,6 +8560,123 @@ function MessageList({
|
|
|
8492
8560
|
useEffect18(() => {
|
|
8493
8561
|
scheduleMeasure();
|
|
8494
8562
|
}, [lastTurnId, layoutSignature, scheduleMeasure]);
|
|
8563
|
+
const scrollToSessionStart = useEffectEvent4(() => {
|
|
8564
|
+
const container = scrollContainerRef.current ?? findScrollContainer(containerRef.current);
|
|
8565
|
+
if (!container) return;
|
|
8566
|
+
container.scrollTo({ top: 0, behavior: "auto" });
|
|
8567
|
+
setActiveTurnId(null);
|
|
8568
|
+
});
|
|
8569
|
+
useEffect18(() => {
|
|
8570
|
+
if (!scrollToStartKey || !hasMessages) return;
|
|
8571
|
+
const applyKey = scrollToStartKey;
|
|
8572
|
+
if (appliedStartScrollKeyRef.current === applyKey) return;
|
|
8573
|
+
appliedStartScrollKeyRef.current = applyKey;
|
|
8574
|
+
if (startScrollFrameRef.current != null) {
|
|
8575
|
+
window.cancelAnimationFrame(startScrollFrameRef.current);
|
|
8576
|
+
}
|
|
8577
|
+
if (startScrollTimeoutRef.current != null) {
|
|
8578
|
+
window.clearTimeout(startScrollTimeoutRef.current);
|
|
8579
|
+
}
|
|
8580
|
+
startScrollFrameRef.current = window.requestAnimationFrame(() => {
|
|
8581
|
+
startScrollFrameRef.current = null;
|
|
8582
|
+
scrollToSessionStart();
|
|
8583
|
+
startScrollTimeoutRef.current = window.setTimeout(() => {
|
|
8584
|
+
startScrollTimeoutRef.current = null;
|
|
8585
|
+
scrollToSessionStart();
|
|
8586
|
+
}, 80);
|
|
8587
|
+
});
|
|
8588
|
+
return () => {
|
|
8589
|
+
if (startScrollFrameRef.current != null) {
|
|
8590
|
+
window.cancelAnimationFrame(startScrollFrameRef.current);
|
|
8591
|
+
startScrollFrameRef.current = null;
|
|
8592
|
+
}
|
|
8593
|
+
if (startScrollTimeoutRef.current != null) {
|
|
8594
|
+
window.clearTimeout(startScrollTimeoutRef.current);
|
|
8595
|
+
startScrollTimeoutRef.current = null;
|
|
8596
|
+
}
|
|
8597
|
+
};
|
|
8598
|
+
}, [hasMessages, scrollToSessionStart, scrollToStartKey]);
|
|
8599
|
+
const scrollToEntry = useEffectEvent4((entryId) => {
|
|
8600
|
+
const root = containerRef.current;
|
|
8601
|
+
if (!root) return false;
|
|
8602
|
+
const target = findEntryAnchor(root, entryId);
|
|
8603
|
+
if (!target) return false;
|
|
8604
|
+
stickContextRef.current?.stopScroll();
|
|
8605
|
+
const container = scrollContainerRef.current ?? findScrollContainer(root);
|
|
8606
|
+
if (container) {
|
|
8607
|
+
const containerRect = container.getBoundingClientRect();
|
|
8608
|
+
const targetRect = target.getBoundingClientRect();
|
|
8609
|
+
container.scrollTo({
|
|
8610
|
+
top: calculateAnchoredScrollTop({
|
|
8611
|
+
scrollTop: container.scrollTop,
|
|
8612
|
+
containerTop: containerRect.top,
|
|
8613
|
+
targetTop: targetRect.top
|
|
8614
|
+
}),
|
|
8615
|
+
behavior: "auto"
|
|
8616
|
+
});
|
|
8617
|
+
} else {
|
|
8618
|
+
target.scrollIntoView({ behavior: "auto", block: "start" });
|
|
8619
|
+
}
|
|
8620
|
+
setActiveTurnId(target.dataset.turnId ?? null);
|
|
8621
|
+
return true;
|
|
8622
|
+
});
|
|
8623
|
+
const getAnchorTargetScrollTop = useCallback12(
|
|
8624
|
+
(defaultTargetScrollTop, { scrollElement, contentElement }) => {
|
|
8625
|
+
if (!scrollEntryId) return defaultTargetScrollTop;
|
|
8626
|
+
const target = findEntryAnchor(contentElement, scrollEntryId);
|
|
8627
|
+
if (!target) return defaultTargetScrollTop;
|
|
8628
|
+
const containerRect = scrollElement.getBoundingClientRect();
|
|
8629
|
+
const targetRect = target.getBoundingClientRect();
|
|
8630
|
+
return calculateAnchoredScrollTop({
|
|
8631
|
+
scrollTop: scrollElement.scrollTop,
|
|
8632
|
+
containerTop: containerRect.top,
|
|
8633
|
+
targetTop: targetRect.top
|
|
8634
|
+
});
|
|
8635
|
+
},
|
|
8636
|
+
[scrollEntryId]
|
|
8637
|
+
);
|
|
8638
|
+
useEffect18(() => {
|
|
8639
|
+
if (!scrollEntryId || !hasMessages) return;
|
|
8640
|
+
const entryId = scrollEntryId;
|
|
8641
|
+
const applyKey = scrollEntryId;
|
|
8642
|
+
if (appliedEntryScrollKeyRef.current === applyKey) return;
|
|
8643
|
+
appliedEntryScrollKeyRef.current = applyKey;
|
|
8644
|
+
if (entryScrollFrameRef.current != null) {
|
|
8645
|
+
window.cancelAnimationFrame(entryScrollFrameRef.current);
|
|
8646
|
+
}
|
|
8647
|
+
for (const timeoutId of entryScrollTimeoutRefs.current) {
|
|
8648
|
+
window.clearTimeout(timeoutId);
|
|
8649
|
+
}
|
|
8650
|
+
entryScrollTimeoutRefs.current = [];
|
|
8651
|
+
entryScrollFrameRef.current = window.requestAnimationFrame(() => {
|
|
8652
|
+
entryScrollFrameRef.current = null;
|
|
8653
|
+
const attempts = [0, 60, 160, 360, 700, 1200];
|
|
8654
|
+
let matched = false;
|
|
8655
|
+
for (const delay of attempts) {
|
|
8656
|
+
const timeoutId = window.setTimeout(() => {
|
|
8657
|
+
matched = scrollToEntry(entryId) || matched;
|
|
8658
|
+
}, delay);
|
|
8659
|
+
entryScrollTimeoutRefs.current.push(timeoutId);
|
|
8660
|
+
}
|
|
8661
|
+
const fallbackId = window.setTimeout(() => {
|
|
8662
|
+
entryScrollTimeoutRefs.current = [];
|
|
8663
|
+
if (!matched && !scrollToEntry(entryId)) {
|
|
8664
|
+
scrollToSessionStart();
|
|
8665
|
+
}
|
|
8666
|
+
}, 1400);
|
|
8667
|
+
entryScrollTimeoutRefs.current.push(fallbackId);
|
|
8668
|
+
});
|
|
8669
|
+
return () => {
|
|
8670
|
+
if (entryScrollFrameRef.current != null) {
|
|
8671
|
+
window.cancelAnimationFrame(entryScrollFrameRef.current);
|
|
8672
|
+
entryScrollFrameRef.current = null;
|
|
8673
|
+
}
|
|
8674
|
+
for (const timeoutId of entryScrollTimeoutRefs.current) {
|
|
8675
|
+
window.clearTimeout(timeoutId);
|
|
8676
|
+
}
|
|
8677
|
+
entryScrollTimeoutRefs.current = [];
|
|
8678
|
+
};
|
|
8679
|
+
}, [hasMessages, scrollEntryId, scrollToEntry, scrollToSessionStart]);
|
|
8495
8680
|
const handleSelectTurn = useCallback12((turnId) => {
|
|
8496
8681
|
document.getElementById(turnId)?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
8497
8682
|
}, []);
|
|
@@ -8514,8 +8699,9 @@ function MessageList({
|
|
|
8514
8699
|
{
|
|
8515
8700
|
contextRef: stickContextRef,
|
|
8516
8701
|
className: "h-full overflow-y-hidden",
|
|
8517
|
-
initial: "instant",
|
|
8702
|
+
initial: shouldAnchorEntry ? "instant" : "instant",
|
|
8518
8703
|
resize: stickyTurn ? { damping: 0.8, stiffness: 0.08, mass: 1 } : "instant",
|
|
8704
|
+
targetScrollTop: shouldAnchorEntry ? getAnchorTargetScrollTop : void 0,
|
|
8519
8705
|
children: [
|
|
8520
8706
|
/* @__PURE__ */ jsx37(StickToBottom.Content, { className: "px-5 py-6", children: /* @__PURE__ */ jsx37(
|
|
8521
8707
|
MessageListContent,
|
|
@@ -8536,7 +8722,8 @@ function MessageList({
|
|
|
8536
8722
|
) }),
|
|
8537
8723
|
/* @__PURE__ */ jsx37(ScrollToBottomButton, {})
|
|
8538
8724
|
]
|
|
8539
|
-
}
|
|
8725
|
+
},
|
|
8726
|
+
scrollEntryId ?? "latest"
|
|
8540
8727
|
)
|
|
8541
8728
|
]
|
|
8542
8729
|
}
|
|
@@ -8581,31 +8768,40 @@ function MessageListContent({
|
|
|
8581
8768
|
/* @__PURE__ */ jsx37("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
8582
8769
|
] }) : renderBlocks.map((block, blockIndex) => {
|
|
8583
8770
|
if (block.type === "message") {
|
|
8584
|
-
return /* @__PURE__ */ jsx37(
|
|
8585
|
-
|
|
8586
|
-
{
|
|
8587
|
-
message: block.message,
|
|
8588
|
-
className: customization.classNames?.userMessage
|
|
8589
|
-
}
|
|
8590
|
-
) : /* @__PURE__ */ jsx37(
|
|
8591
|
-
UserMessageBubble,
|
|
8592
|
-
{
|
|
8593
|
-
message: block.message,
|
|
8594
|
-
className: customization?.classNames?.userMessage
|
|
8595
|
-
}
|
|
8596
|
-
) : isErrorMessage(block.message) ? customization?.components?.ErrorMessage ? /* @__PURE__ */ jsx37(
|
|
8597
|
-
customization.components.ErrorMessage,
|
|
8598
|
-
{
|
|
8599
|
-
message: block.message,
|
|
8600
|
-
className: customization.classNames?.errorMessage
|
|
8601
|
-
}
|
|
8602
|
-
) : /* @__PURE__ */ jsx37(
|
|
8603
|
-
ErrorMessageBlock,
|
|
8771
|
+
return /* @__PURE__ */ jsx37(
|
|
8772
|
+
"div",
|
|
8604
8773
|
{
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8774
|
+
id: block.anchorId ?? void 0,
|
|
8775
|
+
"data-entry-id": block.message.entry_id,
|
|
8776
|
+
className: "msg-animate scroll-mt-6",
|
|
8777
|
+
children: isUserMessage(block.message) ? customization?.components?.UserMessage ? /* @__PURE__ */ jsx37(
|
|
8778
|
+
customization.components.UserMessage,
|
|
8779
|
+
{
|
|
8780
|
+
message: block.message,
|
|
8781
|
+
className: customization.classNames?.userMessage
|
|
8782
|
+
}
|
|
8783
|
+
) : /* @__PURE__ */ jsx37(
|
|
8784
|
+
UserMessageBubble,
|
|
8785
|
+
{
|
|
8786
|
+
message: block.message,
|
|
8787
|
+
className: customization?.classNames?.userMessage
|
|
8788
|
+
}
|
|
8789
|
+
) : isErrorMessage(block.message) ? customization?.components?.ErrorMessage ? /* @__PURE__ */ jsx37(
|
|
8790
|
+
customization.components.ErrorMessage,
|
|
8791
|
+
{
|
|
8792
|
+
message: block.message,
|
|
8793
|
+
className: customization.classNames?.errorMessage
|
|
8794
|
+
}
|
|
8795
|
+
) : /* @__PURE__ */ jsx37(
|
|
8796
|
+
ErrorMessageBlock,
|
|
8797
|
+
{
|
|
8798
|
+
message: block.message,
|
|
8799
|
+
className: customization?.classNames?.errorMessage
|
|
8800
|
+
}
|
|
8801
|
+
) : null
|
|
8802
|
+
},
|
|
8803
|
+
block.key
|
|
8804
|
+
);
|
|
8609
8805
|
}
|
|
8610
8806
|
if (block.type === "assistant_turn") {
|
|
8611
8807
|
const nextBlock = renderBlocks[blockIndex + 1];
|
|
@@ -8621,6 +8817,7 @@ function MessageListContent({
|
|
|
8621
8817
|
{
|
|
8622
8818
|
id: block.anchorId,
|
|
8623
8819
|
"data-turn-id": block.anchorId,
|
|
8820
|
+
"data-entry-id": block.messages[0]?.entry_id,
|
|
8624
8821
|
className: "msg-animate flex scroll-mt-6 flex-col gap-4",
|
|
8625
8822
|
children: [
|
|
8626
8823
|
/* @__PURE__ */ jsx37(
|
|
@@ -8735,6 +8932,8 @@ function ChatView({
|
|
|
8735
8932
|
inputLabel,
|
|
8736
8933
|
onDoubleEscape,
|
|
8737
8934
|
composerHint,
|
|
8935
|
+
scrollToStartKey,
|
|
8936
|
+
scrollToEntryKey,
|
|
8738
8937
|
classNames,
|
|
8739
8938
|
components,
|
|
8740
8939
|
renderers
|
|
@@ -8789,7 +8988,9 @@ function ChatView({
|
|
|
8789
8988
|
onAnswer: isViewer ? void 0 : (msg, toolCallId, answerData) => send(msg, mode, { tool_call_id: toolCallId, ...answerData }),
|
|
8790
8989
|
sessionStatus,
|
|
8791
8990
|
onConfirmPlan: isViewer ? void 0 : onConfirmPlan,
|
|
8792
|
-
customization
|
|
8991
|
+
customization,
|
|
8992
|
+
scrollToStartKey,
|
|
8993
|
+
scrollToEntryKey
|
|
8793
8994
|
},
|
|
8794
8995
|
sessionId
|
|
8795
8996
|
),
|
|
@@ -8878,4 +9079,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
|
|
|
8878
9079
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
8879
9080
|
*--------------------------------------------------------------------------------------------*)
|
|
8880
9081
|
*/
|
|
8881
|
-
//# sourceMappingURL=chunk-
|
|
9082
|
+
//# sourceMappingURL=chunk-N2OE5MVV.js.map
|