@blade-hq/agent-kit 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -1
- package/dist/{SkillStatusBar-DItrW2vv.d.ts → SkillStatusBar-DQyipdzn.d.ts} +112 -8
- package/dist/chunk-2UP7MG3J.js +66 -0
- package/dist/chunk-2UP7MG3J.js.map +1 -0
- package/dist/chunk-4VWLTG5L.js +2984 -0
- package/dist/chunk-4VWLTG5L.js.map +1 -0
- package/dist/chunk-7LEKQI47.js +32 -0
- package/dist/chunk-7LEKQI47.js.map +1 -0
- package/dist/chunk-CGOQI7ZL.js +8124 -0
- package/dist/chunk-CGOQI7ZL.js.map +1 -0
- package/dist/chunk-DQCXSPHP.js +33 -0
- package/dist/chunk-DQCXSPHP.js.map +1 -0
- package/dist/chunk-I3FFV63W.js +30 -0
- package/dist/chunk-I3FFV63W.js.map +1 -0
- package/dist/chunk-J3XVFPOV.js +58 -0
- package/dist/chunk-J3XVFPOV.js.map +1 -0
- package/dist/chunk-JCJFFJ42.js +39 -0
- package/dist/chunk-JCJFFJ42.js.map +1 -0
- package/dist/chunk-OKQWPNE3.js +1077 -0
- package/dist/chunk-OKQWPNE3.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-TC5BBLWO.js +29 -0
- package/dist/chunk-TC5BBLWO.js.map +1 -0
- package/dist/chunk-VD4CKRMT.js +127 -0
- package/dist/chunk-VD4CKRMT.js.map +1 -0
- package/dist/chunk-X6MEYCU7.js +1401 -0
- package/dist/chunk-X6MEYCU7.js.map +1 -0
- package/dist/client/index.js +24 -1052
- package/dist/client/index.js.map +1 -1
- package/dist/react/api/licenses.js +11 -1470
- package/dist/react/api/licenses.js.map +1 -1
- package/dist/react/api/vibe-coding.js +25 -1481
- package/dist/react/api/vibe-coding.js.map +1 -1
- package/dist/react/cards/register.js +45 -138
- package/dist/react/cards/register.js.map +1 -1
- package/dist/react/components/chat/index.d.ts +7 -21
- package/dist/react/components/chat/index.js +28 -11366
- package/dist/react/components/chat/index.js.map +1 -1
- package/dist/react/components/plan/index.js +135 -3054
- package/dist/react/components/plan/index.js.map +1 -1
- package/dist/react/components/session/index.js +21 -1499
- package/dist/react/components/session/index.js.map +1 -1
- package/dist/react/components/workspace/index.js +116 -1715
- package/dist/react/components/workspace/index.js.map +1 -1
- package/dist/react/devtools/bridge-devtools/index.js +8 -51
- package/dist/react/devtools/bridge-devtools/index.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +625 -14035
- package/dist/react/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -1,1521 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Collapsible,
|
|
3
|
+
CollapsibleContent,
|
|
4
|
+
CollapsibleTrigger,
|
|
5
|
+
resolveSessionFilePreviewTarget
|
|
6
|
+
} from "../../../chunk-VD4CKRMT.js";
|
|
7
|
+
import {
|
|
8
|
+
apiFetchText,
|
|
9
|
+
copyFile,
|
|
10
|
+
deleteFile,
|
|
11
|
+
getAuthedUrl,
|
|
12
|
+
getDownloadDirUrl,
|
|
13
|
+
isUiMeta,
|
|
14
|
+
listDir,
|
|
15
|
+
renameFile,
|
|
16
|
+
uploadFiles,
|
|
17
|
+
useChatStore,
|
|
18
|
+
useSessionStore,
|
|
19
|
+
useUiStore
|
|
20
|
+
} from "../../../chunk-4VWLTG5L.js";
|
|
21
|
+
import "../../../chunk-J3XVFPOV.js";
|
|
22
|
+
import "../../../chunk-OKQWPNE3.js";
|
|
23
|
+
import {
|
|
24
|
+
cn
|
|
25
|
+
} from "../../../chunk-7LEKQI47.js";
|
|
26
|
+
import "../../../chunk-JCJFFJ42.js";
|
|
27
|
+
import "../../../chunk-PZ5AY32C.js";
|
|
28
|
+
|
|
1
29
|
// src/react/components/workspace/WorkspaceFilesPanel.tsx
|
|
2
30
|
import { useQueryClient as useQueryClient2 } from "@tanstack/react-query";
|
|
3
31
|
import { FileText, FolderTree, FolderUp, Loader2 as Loader22, PanelRightOpen, RefreshCw, Upload } from "lucide-react";
|
|
4
32
|
import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
|
|
5
33
|
|
|
6
|
-
// src/client/resources/models.ts
|
|
7
|
-
import { type } from "arktype";
|
|
8
|
-
var ModelOption = type({
|
|
9
|
-
id: "string",
|
|
10
|
-
label: "string"
|
|
11
|
-
});
|
|
12
|
-
var ModelsConfig = type({
|
|
13
|
-
default: "string",
|
|
14
|
-
models: ModelOption.array()
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// src/client/socket.ts
|
|
18
|
-
import { io } from "socket.io-client";
|
|
19
|
-
|
|
20
|
-
// src/react/schemas/partner-skill.ts
|
|
21
|
-
import { type as type2 } from "arktype";
|
|
22
|
-
var PartnerSkillName = type2("/^[a-z0-9-]+\\/[a-z0-9-]+$/");
|
|
23
|
-
var PartnerSkillFile = type2({
|
|
24
|
-
path: "string > 0",
|
|
25
|
-
content: "string"
|
|
26
|
-
});
|
|
27
|
-
var PartnerSkillInstallPayload = type2({
|
|
28
|
-
name: PartnerSkillName,
|
|
29
|
-
files: PartnerSkillFile.array().atLeastLength(1)
|
|
30
|
-
});
|
|
31
|
-
var PartnerSkillInstallResult = type2({
|
|
32
|
-
name: PartnerSkillName,
|
|
33
|
-
skill_dir: "string",
|
|
34
|
-
file_count: "number.integer >= 0",
|
|
35
|
-
overwritten: "boolean"
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// src/react/stores/ui-bridge-store.ts
|
|
39
|
-
import { create } from "zustand";
|
|
40
|
-
|
|
41
|
-
// src/react/stores/client-aware.ts
|
|
42
|
-
function createClientActions(set) {
|
|
43
|
-
return {
|
|
44
|
-
_client: null,
|
|
45
|
-
setClient: (client) => set({ _client: client })
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/react/stores/ui-bridge-store.ts
|
|
50
|
-
function buildSignalId() {
|
|
51
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
52
|
-
return crypto.randomUUID();
|
|
53
|
-
}
|
|
54
|
-
return `bridge-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
55
|
-
}
|
|
56
|
-
function clearSignalRecord(record, sessionId) {
|
|
57
|
-
if (!(sessionId in record)) {
|
|
58
|
-
return record;
|
|
59
|
-
}
|
|
60
|
-
const next = { ...record };
|
|
61
|
-
delete next[sessionId];
|
|
62
|
-
return next;
|
|
63
|
-
}
|
|
64
|
-
var useUiBridgeStore = create()((set, get) => ({
|
|
65
|
-
...createClientActions(set),
|
|
66
|
-
pendingContexts: {},
|
|
67
|
-
draftAppends: {},
|
|
68
|
-
sendRequests: {},
|
|
69
|
-
addPendingContext: (sessionId, context) => set((state) => ({
|
|
70
|
-
pendingContexts: {
|
|
71
|
-
...state.pendingContexts,
|
|
72
|
-
[sessionId]: [
|
|
73
|
-
...state.pendingContexts[sessionId] ?? [],
|
|
74
|
-
{
|
|
75
|
-
id: buildSignalId(),
|
|
76
|
-
label: context.label,
|
|
77
|
-
content: context.content
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
}
|
|
81
|
-
})),
|
|
82
|
-
removePendingContext: (sessionId, contextId) => set((state) => {
|
|
83
|
-
const contexts = state.pendingContexts[sessionId];
|
|
84
|
-
if (!contexts?.length) {
|
|
85
|
-
return state;
|
|
86
|
-
}
|
|
87
|
-
const nextContexts = contexts.filter((context) => context.id !== contextId);
|
|
88
|
-
if (nextContexts.length === contexts.length) {
|
|
89
|
-
return state;
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
pendingContexts: nextContexts.length > 0 ? {
|
|
93
|
-
...state.pendingContexts,
|
|
94
|
-
[sessionId]: nextContexts
|
|
95
|
-
} : clearSignalRecord(state.pendingContexts, sessionId)
|
|
96
|
-
};
|
|
97
|
-
}),
|
|
98
|
-
consumePendingContexts: (sessionId) => {
|
|
99
|
-
const signals = get().pendingContexts[sessionId] ?? [];
|
|
100
|
-
if (signals.length === 0) return [];
|
|
101
|
-
set((state) => ({
|
|
102
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
|
|
103
|
-
}));
|
|
104
|
-
return signals;
|
|
105
|
-
},
|
|
106
|
-
clearPendingContexts: (sessionId) => set((state) => ({
|
|
107
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
|
|
108
|
-
})),
|
|
109
|
-
addDraftAppend: (sessionId, text) => set((state) => ({
|
|
110
|
-
draftAppends: {
|
|
111
|
-
...state.draftAppends,
|
|
112
|
-
[sessionId]: [
|
|
113
|
-
...state.draftAppends[sessionId] ?? [],
|
|
114
|
-
{
|
|
115
|
-
id: buildSignalId(),
|
|
116
|
-
text
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
}
|
|
120
|
-
})),
|
|
121
|
-
consumeDraftAppends: (sessionId) => {
|
|
122
|
-
const signals = get().draftAppends[sessionId] ?? [];
|
|
123
|
-
if (signals.length === 0) return [];
|
|
124
|
-
set((state) => ({
|
|
125
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId)
|
|
126
|
-
}));
|
|
127
|
-
return signals;
|
|
128
|
-
},
|
|
129
|
-
clearDraftAppends: (sessionId) => set((state) => ({
|
|
130
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId)
|
|
131
|
-
})),
|
|
132
|
-
addSendRequest: (sessionId) => set((state) => ({
|
|
133
|
-
sendRequests: {
|
|
134
|
-
...state.sendRequests,
|
|
135
|
-
[sessionId]: [
|
|
136
|
-
...state.sendRequests[sessionId] ?? [],
|
|
137
|
-
{
|
|
138
|
-
id: buildSignalId()
|
|
139
|
-
}
|
|
140
|
-
]
|
|
141
|
-
}
|
|
142
|
-
})),
|
|
143
|
-
consumeSendRequests: (sessionId) => {
|
|
144
|
-
const signals = get().sendRequests[sessionId] ?? [];
|
|
145
|
-
if (signals.length === 0) return [];
|
|
146
|
-
set((state) => ({
|
|
147
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
148
|
-
}));
|
|
149
|
-
return signals;
|
|
150
|
-
},
|
|
151
|
-
clearSendRequests: (sessionId) => set((state) => ({
|
|
152
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
153
|
-
})),
|
|
154
|
-
clearSession: (sessionId) => set((state) => ({
|
|
155
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),
|
|
156
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId),
|
|
157
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
158
|
-
}))
|
|
159
|
-
}));
|
|
160
|
-
|
|
161
|
-
// src/react/lib/chat.ts
|
|
162
|
-
function normalizeMessageContent(content) {
|
|
163
|
-
if (typeof content === "string") return content;
|
|
164
|
-
if (Array.isArray(content)) return content;
|
|
165
|
-
return String(content ?? "");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// src/react/lib/ui-meta.ts
|
|
169
|
-
function isNonEmptyString(value) {
|
|
170
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
171
|
-
}
|
|
172
|
-
function isPositiveNumber(value) {
|
|
173
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
174
|
-
}
|
|
175
|
-
function isUiMeta(value) {
|
|
176
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
const raw = value;
|
|
180
|
-
if (raw.target !== "inline" && raw.target !== "preview") {
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
if (!isPositiveNumber(raw.height)) {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
if (!isNonEmptyString(raw.resourceHTML) && !isNonEmptyString(raw.resourceUri) && !isNonEmptyString(raw.resourceURI)) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
if (raw.title != null && !isNonEmptyString(raw.title)) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// src/react/stores/auth-store.ts
|
|
196
|
-
import { create as create6 } from "zustand";
|
|
197
|
-
import { createJSONStorage, persist } from "zustand/middleware";
|
|
198
|
-
|
|
199
|
-
// src/react/api/auth.ts
|
|
200
|
-
var r = () => getClient().auth;
|
|
201
|
-
var getMe = (...args) => r().getMe(...args);
|
|
202
|
-
var logout = (...args) => r().logout(...args);
|
|
203
|
-
|
|
204
|
-
// src/react/sockets/socket-state.ts
|
|
205
|
-
var agentSocket = null;
|
|
206
|
-
|
|
207
|
-
// src/react/stores/session-store.ts
|
|
208
|
-
import { create as create5 } from "zustand";
|
|
209
|
-
|
|
210
|
-
// src/react/stores/chat-store.ts
|
|
211
|
-
import { create as create3 } from "zustand";
|
|
212
|
-
|
|
213
|
-
// src/react/components/chat/display-utils.ts
|
|
214
|
-
var TOOL_NAME_ALIASES = {
|
|
215
|
-
agent: "Agent",
|
|
216
|
-
ask_user_question: "AskUserQuestion",
|
|
217
|
-
bash: "Bash",
|
|
218
|
-
bg_bash: "BgBash",
|
|
219
|
-
edit: "Edit",
|
|
220
|
-
exit_plan_mode: "ExitPlanMode",
|
|
221
|
-
file_edit: "Edit",
|
|
222
|
-
file_read: "Read",
|
|
223
|
-
file_write: "Write",
|
|
224
|
-
finish_task: "FinishTask",
|
|
225
|
-
get_skill_content: "GetSkillContent",
|
|
226
|
-
glob: "Glob",
|
|
227
|
-
grep: "Grep",
|
|
228
|
-
list_skill_tools: "ListSkillTools",
|
|
229
|
-
load_skill_tools: "LoadSkillTools",
|
|
230
|
-
ls: "Ls",
|
|
231
|
-
read: "Read",
|
|
232
|
-
run_skill_tool: "RunSkillTool",
|
|
233
|
-
search_skills: "SearchSkills",
|
|
234
|
-
web_fetch: "WebFetch",
|
|
235
|
-
web_search: "WebSearch",
|
|
236
|
-
write: "Write"
|
|
237
|
-
};
|
|
238
|
-
var TOOL_DISPLAY_LABELS = {
|
|
239
|
-
Bash: "\u6267\u884C\u547D\u4EE4",
|
|
240
|
-
BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
|
|
241
|
-
Read: "\u8BFB\u53D6\u6587\u4EF6",
|
|
242
|
-
Write: "\u5199\u5165\u6587\u4EF6",
|
|
243
|
-
Edit: "\u7F16\u8F91\u6587\u4EF6",
|
|
244
|
-
Ls: "\u5217\u51FA\u76EE\u5F55",
|
|
245
|
-
Glob: "\u5339\u914D\u6587\u4EF6",
|
|
246
|
-
Grep: "\u641C\u7D22\u6587\u672C",
|
|
247
|
-
WebSearch: "\u641C\u7D22\u7F51\u9875",
|
|
248
|
-
WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
|
|
249
|
-
Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
|
|
250
|
-
AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
|
|
251
|
-
SearchSkills: "\u641C\u7D22\u6280\u80FD",
|
|
252
|
-
GetSkillContent: "\u8BFB\u53D6\u6280\u80FD",
|
|
253
|
-
ListSkillTools: "\u67E5\u770B\u5DE5\u5177\u5217\u8868",
|
|
254
|
-
LoadSkillTools: "\u52A0\u8F7D\u6280\u80FD",
|
|
255
|
-
RunSkillTool: "\u6267\u884C\u6280\u80FD\u5DE5\u5177",
|
|
256
|
-
FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
|
|
257
|
-
ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
|
|
258
|
-
ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
|
|
259
|
-
GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
|
|
260
|
-
};
|
|
261
|
-
var GENERIC_DISPLAY_NAMES = new Set(Object.values(TOOL_DISPLAY_LABELS));
|
|
262
|
-
function formatToolName(name) {
|
|
263
|
-
const trimmed = name.trim();
|
|
264
|
-
if (!trimmed) return name;
|
|
265
|
-
const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
|
|
266
|
-
const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
267
|
-
return TOOL_NAME_ALIASES[normalized] ?? stripped;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// src/react/stores/ui-store.ts
|
|
271
|
-
import { create as create2 } from "zustand";
|
|
272
|
-
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
273
|
-
function resolveEffectiveTheme(theme) {
|
|
274
|
-
if (theme !== "system") return theme;
|
|
275
|
-
return isBrowser && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
276
|
-
}
|
|
277
|
-
function applyTheme(theme) {
|
|
278
|
-
if (!isBrowser) return;
|
|
279
|
-
const effective = resolveEffectiveTheme(theme);
|
|
280
|
-
document.documentElement.setAttribute("data-theme", effective);
|
|
281
|
-
}
|
|
282
|
-
var storedTheme = isBrowser ? localStorage.getItem("blade-theme") ?? "light" : "light";
|
|
283
|
-
applyTheme(storedTheme);
|
|
284
|
-
if (isBrowser) {
|
|
285
|
-
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
|
286
|
-
const current = useUiStore?.getState?.()?.theme;
|
|
287
|
-
if (current === "system") applyTheme("system");
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function removeArtifactAtIndex(state, index) {
|
|
291
|
-
const next = state.artifacts.filter((_, i) => i !== index);
|
|
292
|
-
let nextActive = state.activeArtifactIndex;
|
|
293
|
-
if (next.length === 0) {
|
|
294
|
-
return { artifacts: [], activeArtifactIndex: -1, rightPanelCollapsed: true };
|
|
295
|
-
}
|
|
296
|
-
if (index < nextActive) {
|
|
297
|
-
nextActive -= 1;
|
|
298
|
-
} else if (index === nextActive) {
|
|
299
|
-
nextActive = Math.min(index, next.length - 1);
|
|
300
|
-
}
|
|
301
|
-
return { artifacts: next, activeArtifactIndex: nextActive };
|
|
302
|
-
}
|
|
303
|
-
function upsertArtifactState(state, target, options) {
|
|
304
|
-
const reveal = options?.reveal ?? true;
|
|
305
|
-
const activate = options?.activate ?? true;
|
|
306
|
-
const targetKey = target.key ?? target.title;
|
|
307
|
-
const applyUiState = (partial) => ({
|
|
308
|
-
...partial,
|
|
309
|
-
...reveal ? { rightPanelCollapsed: false, activeRightTab: "preview" } : {}
|
|
310
|
-
});
|
|
311
|
-
const existing = state.artifacts.findIndex((artifact) => targetKey && (artifact.key ?? artifact.title) === targetKey);
|
|
312
|
-
if (existing >= 0) {
|
|
313
|
-
const updated = [...state.artifacts];
|
|
314
|
-
updated[existing] = target;
|
|
315
|
-
return applyUiState({
|
|
316
|
-
artifacts: updated,
|
|
317
|
-
activeArtifactIndex: activate ? existing : state.activeArtifactIndex
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
const next = [...state.artifacts, target];
|
|
321
|
-
return applyUiState({
|
|
322
|
-
artifacts: next,
|
|
323
|
-
activeArtifactIndex: activate ? next.length - 1 : state.activeArtifactIndex >= 0 ? state.activeArtifactIndex : 0
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
var useUiStore = create2()((set) => ({
|
|
327
|
-
...createClientActions(set),
|
|
328
|
-
leftPanelSize: 20,
|
|
329
|
-
rightPanelSize: 25,
|
|
330
|
-
leftPanelCollapsed: false,
|
|
331
|
-
rightPanelCollapsed: false,
|
|
332
|
-
activeRightTab: "situation",
|
|
333
|
-
artifacts: [],
|
|
334
|
-
activeArtifactIndex: -1,
|
|
335
|
-
theme: storedTheme,
|
|
336
|
-
setLeftPanelSize: (size) => set({ leftPanelSize: size }),
|
|
337
|
-
setRightPanelSize: (size) => set({ rightPanelSize: size }),
|
|
338
|
-
setLeftPanelCollapsed: (collapsed) => set({ leftPanelCollapsed: collapsed }),
|
|
339
|
-
setRightPanelCollapsed: (collapsed) => set({ rightPanelCollapsed: collapsed }),
|
|
340
|
-
toggleLeftPanel: () => set((s) => ({ leftPanelCollapsed: !s.leftPanelCollapsed })),
|
|
341
|
-
toggleRightPanel: () => set((s) => ({ rightPanelCollapsed: !s.rightPanelCollapsed })),
|
|
342
|
-
setActiveRightTab: (tab) => set({ activeRightTab: tab }),
|
|
343
|
-
pushArtifact: (target) => set((state) => upsertArtifactState(state, target)),
|
|
344
|
-
upsertArtifact: (target, options) => set((state) => upsertArtifactState(state, target, options)),
|
|
345
|
-
setActiveArtifact: (index) => set({ activeArtifactIndex: index }),
|
|
346
|
-
closeArtifact: (index) => set((state) => removeArtifactAtIndex(state, index)),
|
|
347
|
-
removeArtifactByKey: (key) => set((state) => {
|
|
348
|
-
const index = state.artifacts.findIndex((artifact) => (artifact.key ?? artifact.title) === key);
|
|
349
|
-
if (index < 0) return state;
|
|
350
|
-
return removeArtifactAtIndex(state, index);
|
|
351
|
-
}),
|
|
352
|
-
clearArtifacts: () => set({ artifacts: [], activeArtifactIndex: -1 }),
|
|
353
|
-
setPreviewTarget: (target) => set(() => {
|
|
354
|
-
if (target === null) {
|
|
355
|
-
return { artifacts: [], activeArtifactIndex: -1 };
|
|
356
|
-
}
|
|
357
|
-
return {
|
|
358
|
-
artifacts: [target],
|
|
359
|
-
activeArtifactIndex: 0,
|
|
360
|
-
rightPanelCollapsed: false,
|
|
361
|
-
activeRightTab: "preview"
|
|
362
|
-
};
|
|
363
|
-
}),
|
|
364
|
-
setTheme: (theme) => {
|
|
365
|
-
localStorage.setItem("blade-theme", theme);
|
|
366
|
-
applyTheme(theme);
|
|
367
|
-
set({ theme });
|
|
368
|
-
}
|
|
369
|
-
}));
|
|
370
|
-
|
|
371
|
-
// src/react/stores/chat-store.ts
|
|
372
|
-
var _getActiveSessionId = null;
|
|
373
|
-
function setChatStoreSessionAccessor(fn) {
|
|
374
|
-
_getActiveSessionId = fn;
|
|
375
|
-
}
|
|
376
|
-
function parseAgentDescription(argumentsJson) {
|
|
377
|
-
try {
|
|
378
|
-
return JSON.parse(argumentsJson)?.description ?? "\u5B50\u667A\u80FD\u4F53";
|
|
379
|
-
} catch {
|
|
380
|
-
return "\u5B50\u667A\u80FD\u4F53";
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
function inferLoopStatusFromMessages(messages) {
|
|
384
|
-
const toolCalls = messages.flatMap((message) => message.tool_calls ?? []);
|
|
385
|
-
if (messages.some((message) => message.status === "streaming")) return "running";
|
|
386
|
-
if (toolCalls.some((toolCall) => toolCall.status === "awaiting_answer")) return "awaiting_answer";
|
|
387
|
-
if (toolCalls.some((toolCall) => toolCall.status === "error")) return "error";
|
|
388
|
-
if (toolCalls.some((toolCall) => toolCall.status === "cancelled")) return "cancelled";
|
|
389
|
-
if (toolCalls.some((toolCall) => toolCall.status === "pending")) return "running";
|
|
390
|
-
return "done";
|
|
391
|
-
}
|
|
392
|
-
function inferLoopStatusFromTurns(turns, messages) {
|
|
393
|
-
const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
|
|
394
|
-
if (block.type !== "system_notification" || !isRecord(block.content)) return false;
|
|
395
|
-
return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
|
|
396
|
-
});
|
|
397
|
-
if (latestAgentNotification?.type === "system_notification" && isRecord(latestAgentNotification.content)) {
|
|
398
|
-
const notificationType = latestAgentNotification.content.notification_type;
|
|
399
|
-
const status = latestAgentNotification.content.status;
|
|
400
|
-
if (notificationType === "agent:start" || status === "running") return "running";
|
|
401
|
-
if (status === "error") return "error";
|
|
402
|
-
if (status === "cancelled") return "cancelled";
|
|
403
|
-
}
|
|
404
|
-
return inferLoopStatusFromMessages(messages);
|
|
405
|
-
}
|
|
406
|
-
function isRecord(value) {
|
|
407
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
408
|
-
}
|
|
409
|
-
function parentForkToolCallIdFromTurn(turn) {
|
|
410
|
-
if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
|
|
411
|
-
return turn.parent_fork_tool_call_id;
|
|
412
|
-
}
|
|
413
|
-
for (const block of turn.blocks) {
|
|
414
|
-
if (block.type !== "system_notification" || !isRecord(block.content)) continue;
|
|
415
|
-
const metadata = block.content.metadata;
|
|
416
|
-
if (!isRecord(metadata)) continue;
|
|
417
|
-
const parentId = metadata.parent_fork_tool_call_id;
|
|
418
|
-
if (typeof parentId === "string" && parentId.length > 0) return parentId;
|
|
419
|
-
}
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
function buildMessageContent(turn) {
|
|
423
|
-
const textBlocks = turn.blocks.filter((block) => block.type === "text");
|
|
424
|
-
if (textBlocks.length === 0) return "";
|
|
425
|
-
if (textBlocks.length === 1) return normalizeMessageContent(textBlocks[0].content);
|
|
426
|
-
return textBlocks.map((block) => {
|
|
427
|
-
if (typeof block.content === "string") return block.content;
|
|
428
|
-
return JSON.stringify(block.content);
|
|
429
|
-
}).join("");
|
|
430
|
-
}
|
|
431
|
-
function buildReasoning(turn) {
|
|
432
|
-
const thinking = turn.blocks.filter((block) => block.type === "thinking").map((block) => typeof block.content === "string" ? block.content : JSON.stringify(block.content)).filter(Boolean);
|
|
433
|
-
if (thinking.length === 0) return void 0;
|
|
434
|
-
return thinking.join("\n\n");
|
|
435
|
-
}
|
|
436
|
-
function isModeChangeContent(value) {
|
|
437
|
-
return typeof value === "object" && value !== null && typeof value.from === "string" && typeof value.to === "string";
|
|
438
|
-
}
|
|
439
|
-
function extractModeFromBlocks(blocks) {
|
|
440
|
-
const modeBlock = blocks.find((block) => block.type === "mode_change");
|
|
441
|
-
if (modeBlock && isModeChangeContent(modeBlock.content)) {
|
|
442
|
-
return modeBlock.content.to === "planning" || modeBlock.content.to === "executing" ? modeBlock.content.to : null;
|
|
443
|
-
}
|
|
444
|
-
if (blocks.some((block) => block.type === "planning_enter")) return "planning";
|
|
445
|
-
if (blocks.some((block) => block.type === "planning_exit")) return "executing";
|
|
446
|
-
return null;
|
|
447
|
-
}
|
|
448
|
-
function projectionToMessage(turn) {
|
|
449
|
-
if (turn.kind === "compaction" && turn.compaction_id) {
|
|
450
|
-
return {
|
|
451
|
-
role: "assistant",
|
|
452
|
-
content: turn.summary_preview ?? "",
|
|
453
|
-
kind: "compaction",
|
|
454
|
-
loop_name: turn.loop_id,
|
|
455
|
-
entry_id: turn.turn_id,
|
|
456
|
-
status: turn.status,
|
|
457
|
-
compaction: {
|
|
458
|
-
compaction_id: turn.compaction_id,
|
|
459
|
-
summary_preview: turn.summary_preview,
|
|
460
|
-
summary_full: turn.summary_full,
|
|
461
|
-
archived_count: turn.archived_count,
|
|
462
|
-
archived_files: turn.archived_files,
|
|
463
|
-
archived_tool_calls: turn.archived_tool_calls,
|
|
464
|
-
tokens_before: turn.tokens_before,
|
|
465
|
-
tokens_after: turn.tokens_after,
|
|
466
|
-
saved_ratio: turn.saved_ratio,
|
|
467
|
-
trigger: turn.trigger,
|
|
468
|
-
failure_reason: turn.failure_reason,
|
|
469
|
-
fallback_applied: turn.fallback_applied
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
const planningBlock = turn.blocks.find(
|
|
474
|
-
(block) => block.type === "mode_change" || block.type === "planning_enter" || block.type === "planning_exit" || block.type === "plan_status"
|
|
475
|
-
);
|
|
476
|
-
if (planningBlock) {
|
|
477
|
-
if (planningBlock.type === "plan_status") {
|
|
478
|
-
return {
|
|
479
|
-
role: "tool",
|
|
480
|
-
content: typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}, null, 2),
|
|
481
|
-
kind: "plan_status",
|
|
482
|
-
loop_name: turn.loop_id,
|
|
483
|
-
entry_id: turn.turn_id,
|
|
484
|
-
status: turn.status
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
return {
|
|
488
|
-
role: "assistant",
|
|
489
|
-
content: planningBlock.type === "mode_change" ? typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}) : "",
|
|
490
|
-
kind: planningBlock.type,
|
|
491
|
-
loop_name: turn.loop_id,
|
|
492
|
-
entry_id: turn.turn_id,
|
|
493
|
-
status: turn.status
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
if (turn.blocks.some((block) => block.type === "ask_user_answer")) {
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
const content = buildMessageContent(turn);
|
|
500
|
-
const reasoning = buildReasoning(turn);
|
|
501
|
-
const toolCalls = turn.tool_calls.length > 0 ? turn.tool_calls.map((toolCall) => ({
|
|
502
|
-
id: toolCall.id,
|
|
503
|
-
name: toolCall.tool_name,
|
|
504
|
-
display_name: toolCall.display_name,
|
|
505
|
-
arguments: toolCall.arguments,
|
|
506
|
-
result: toolCall.result ?? void 0,
|
|
507
|
-
pending_question_ref: toolCall.pending_question_ref ?? void 0,
|
|
508
|
-
status: toolCall.status === "pending" || toolCall.status === "awaiting_answer" || toolCall.status === "done" || toolCall.status === "error" || toolCall.status === "cancelled" ? toolCall.status : "pending",
|
|
509
|
-
...typeof toolCall.duration_ms === "number" ? { duration_ms: toolCall.duration_ms } : {}
|
|
510
|
-
})) : void 0;
|
|
511
|
-
if (turn.role === "system" && !content && !toolCalls?.length) {
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
return {
|
|
515
|
-
role: turn.role === "system" ? "assistant" : turn.role,
|
|
516
|
-
content,
|
|
517
|
-
blocks: turn.blocks,
|
|
518
|
-
...reasoning ? { reasoning } : {},
|
|
519
|
-
...toolCalls ? { tool_calls: toolCalls } : {},
|
|
520
|
-
loop_name: turn.loop_id,
|
|
521
|
-
entry_id: turn.turn_id,
|
|
522
|
-
status: turn.status,
|
|
523
|
-
...typeof turn.duration_ms === "number" ? { duration_ms: turn.duration_ms } : {},
|
|
524
|
-
...turn.started_at ? { timestamp: turn.started_at } : {},
|
|
525
|
-
...turn.memory_refs?.length ? { memory_refs: turn.memory_refs } : {}
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
function rebuildAgentLoops(turns) {
|
|
529
|
-
const messages = turns.map(projectionToMessage).filter(Boolean);
|
|
530
|
-
const childLoopNames = [...new Set(turns.map((turn) => turn.loop_id).filter((name) => name !== "root"))];
|
|
531
|
-
if (childLoopNames.length === 0) return {};
|
|
532
|
-
const agentToolCalls = messages.filter((message) => message.role === "assistant" && (message.loop_name ?? "root") === "root").flatMap((message) => message.tool_calls ?? []).filter((toolCall) => formatToolName(toolCall.name) === "Agent");
|
|
533
|
-
const loops = {};
|
|
534
|
-
const agentToolCallsById = new Map(agentToolCalls.map((toolCall) => [toolCall.id, toolCall]));
|
|
535
|
-
const explicitParentToolCallIds = new Set(
|
|
536
|
-
turns.map(parentForkToolCallIdFromTurn).filter((id) => id !== null)
|
|
537
|
-
);
|
|
538
|
-
const usedToolCallIds = /* @__PURE__ */ new Set();
|
|
539
|
-
for (const loopName of childLoopNames) {
|
|
540
|
-
const loopMessages = messages.filter((message) => (message.loop_name ?? "root") === loopName);
|
|
541
|
-
const loopTurns = turns.filter((turn) => turn.loop_id === loopName);
|
|
542
|
-
const parentToolCallId = loopTurns.map(parentForkToolCallIdFromTurn).find(Boolean);
|
|
543
|
-
const explicitToolCall = parentToolCallId && !usedToolCallIds.has(parentToolCallId) ? agentToolCallsById.get(parentToolCallId) ?? null : null;
|
|
544
|
-
const fallbackToolCall = explicitToolCall === null ? agentToolCalls.find(
|
|
545
|
-
(toolCall2) => !usedToolCallIds.has(toolCall2.id) && !explicitParentToolCallIds.has(toolCall2.id)
|
|
546
|
-
) : null;
|
|
547
|
-
const toolCall = explicitToolCall ?? fallbackToolCall;
|
|
548
|
-
if (!toolCall) continue;
|
|
549
|
-
usedToolCallIds.add(toolCall.id);
|
|
550
|
-
loops[loopName] = {
|
|
551
|
-
toolCallId: toolCall.id,
|
|
552
|
-
description: parseAgentDescription(toolCall.arguments),
|
|
553
|
-
status: inferLoopStatusFromTurns(loopTurns, loopMessages)
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
return loops;
|
|
557
|
-
}
|
|
558
|
-
function applyPlanningSideEffects(sessionId, turns) {
|
|
559
|
-
const latestMode = [...turns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
|
|
560
|
-
if (latestMode !== "planning") return;
|
|
561
|
-
if (_getActiveSessionId?.() !== sessionId) return;
|
|
562
|
-
const ui = useUiStore.getState();
|
|
563
|
-
ui.setActiveRightTab("situation");
|
|
564
|
-
if (ui.rightPanelCollapsed) {
|
|
565
|
-
ui.toggleRightPanel();
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
function materialize(turns) {
|
|
569
|
-
const messages = turns.map(projectionToMessage).filter((message) => message !== null);
|
|
570
|
-
const activeCompaction = [...turns].reverse().find(
|
|
571
|
-
(turn) => turn.kind === "compaction" && turn.status === "streaming" && typeof turn.compaction_id === "string"
|
|
572
|
-
);
|
|
573
|
-
return {
|
|
574
|
-
messages,
|
|
575
|
-
agentLoops: rebuildAgentLoops(turns),
|
|
576
|
-
activeCompaction: activeCompaction ? {
|
|
577
|
-
turn_id: activeCompaction.turn_id,
|
|
578
|
-
status: activeCompaction.status,
|
|
579
|
-
compaction_id: activeCompaction.compaction_id,
|
|
580
|
-
summary_preview: activeCompaction.summary_preview,
|
|
581
|
-
summary_full: activeCompaction.summary_full,
|
|
582
|
-
archived_count: activeCompaction.archived_count,
|
|
583
|
-
archived_files: activeCompaction.archived_files,
|
|
584
|
-
archived_tool_calls: activeCompaction.archived_tool_calls,
|
|
585
|
-
tokens_before: activeCompaction.tokens_before,
|
|
586
|
-
tokens_after: activeCompaction.tokens_after,
|
|
587
|
-
saved_ratio: activeCompaction.saved_ratio,
|
|
588
|
-
trigger: activeCompaction.trigger,
|
|
589
|
-
failure_reason: activeCompaction.failure_reason,
|
|
590
|
-
fallback_applied: activeCompaction.fallback_applied
|
|
591
|
-
} : null
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
var ERROR_ANCHOR_PREFIX = "error-anchor:";
|
|
595
|
-
function updateSessionState(state, sessionId, turns) {
|
|
596
|
-
const orderedTurns = [...turns].sort((left, right) => left.sequence - right.sequence);
|
|
597
|
-
const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
|
|
598
|
-
applyPlanningSideEffects(sessionId, orderedTurns);
|
|
599
|
-
const lastTurnId = orderedTurns[orderedTurns.length - 1]?.turn_id ?? null;
|
|
600
|
-
const preservedErrors = lastTurnId ? (state.messages[sessionId] ?? []).filter(
|
|
601
|
-
(m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
|
|
602
|
-
) : [];
|
|
603
|
-
const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages;
|
|
604
|
-
return {
|
|
605
|
-
turns: { ...state.turns, [sessionId]: orderedTurns },
|
|
606
|
-
messages: { ...state.messages, [sessionId]: mergedMessages },
|
|
607
|
-
agentLoops: { ...state.agentLoops, [sessionId]: agentLoops },
|
|
608
|
-
activeCompactions: { ...state.activeCompactions, [sessionId]: activeCompaction }
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
var useChatStore = create3()((set) => ({
|
|
612
|
-
...createClientActions(set),
|
|
613
|
-
turns: {},
|
|
614
|
-
messages: {},
|
|
615
|
-
askAnswers: {},
|
|
616
|
-
isStreaming: {},
|
|
617
|
-
agentLoops: {},
|
|
618
|
-
activeCompactions: {},
|
|
619
|
-
addUserMessage: (sessionId, content) => {
|
|
620
|
-
set((state) => {
|
|
621
|
-
const existing = state.turns[sessionId] ?? [];
|
|
622
|
-
const turnId = `local-user-${Date.now()}`;
|
|
623
|
-
const optimisticTurn = {
|
|
624
|
-
id: turnId,
|
|
625
|
-
sequence: Math.max(0, ...existing.map((turn) => turn.sequence)) + 1,
|
|
626
|
-
turn_id: turnId,
|
|
627
|
-
loop_id: "root",
|
|
628
|
-
role: "user",
|
|
629
|
-
status: "completed",
|
|
630
|
-
blocks: [{ type: "text", content }],
|
|
631
|
-
tool_calls: [],
|
|
632
|
-
model: null,
|
|
633
|
-
usage: null,
|
|
634
|
-
duration_ms: 0
|
|
635
|
-
};
|
|
636
|
-
return updateSessionState(state, sessionId, [...existing, optimisticTurn]);
|
|
637
|
-
});
|
|
638
|
-
},
|
|
639
|
-
setTurns: (sessionId, turns) => {
|
|
640
|
-
set((state) => updateSessionState(state, sessionId, [...turns]));
|
|
641
|
-
},
|
|
642
|
-
upsertTurn: (sessionId, turn) => {
|
|
643
|
-
set((state) => {
|
|
644
|
-
const existing = [...state.turns[sessionId] ?? []];
|
|
645
|
-
const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
|
|
646
|
-
if (index >= 0) {
|
|
647
|
-
existing[index] = turn;
|
|
648
|
-
} else {
|
|
649
|
-
existing.push(turn);
|
|
650
|
-
}
|
|
651
|
-
return {
|
|
652
|
-
...updateSessionState(state, sessionId, existing)
|
|
653
|
-
};
|
|
654
|
-
});
|
|
655
|
-
},
|
|
656
|
-
applyTurnPatch: (sessionId, patch) => {
|
|
657
|
-
set((state) => {
|
|
658
|
-
const turn = patch.data.turn;
|
|
659
|
-
if (!turn) return state;
|
|
660
|
-
const existing = [...state.turns[sessionId] ?? []];
|
|
661
|
-
const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
|
|
662
|
-
const lastSequence = index >= 0 ? existing[index].sequence : null;
|
|
663
|
-
if (lastSequence !== null && patch.sequence <= lastSequence) {
|
|
664
|
-
return state;
|
|
665
|
-
}
|
|
666
|
-
const nextTurn = {
|
|
667
|
-
...turn,
|
|
668
|
-
sequence: patch.sequence
|
|
669
|
-
};
|
|
670
|
-
if (index >= 0) {
|
|
671
|
-
existing[index] = nextTurn;
|
|
672
|
-
} else {
|
|
673
|
-
existing.push(nextTurn);
|
|
674
|
-
}
|
|
675
|
-
return {
|
|
676
|
-
...updateSessionState(state, sessionId, existing)
|
|
677
|
-
};
|
|
678
|
-
});
|
|
679
|
-
},
|
|
680
|
-
addErrorMessage: (sessionId, content) => {
|
|
681
|
-
set((state) => {
|
|
682
|
-
const turns = state.turns[sessionId] ?? [];
|
|
683
|
-
const anchorTurnId = turns[turns.length - 1]?.turn_id ?? null;
|
|
684
|
-
const entry_id = anchorTurnId ? `${ERROR_ANCHOR_PREFIX}${anchorTurnId}:${Date.now()}` : void 0;
|
|
685
|
-
return {
|
|
686
|
-
messages: {
|
|
687
|
-
...state.messages,
|
|
688
|
-
[sessionId]: [
|
|
689
|
-
...state.messages[sessionId] ?? [],
|
|
690
|
-
{ role: "error", content, loop_name: "root", entry_id }
|
|
691
|
-
]
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
});
|
|
695
|
-
},
|
|
696
|
-
markInterrupted: (sessionId) => {
|
|
697
|
-
set((state) => {
|
|
698
|
-
const turns = (state.turns[sessionId] ?? []).map((turn) => {
|
|
699
|
-
if (turn.status !== "streaming") return turn;
|
|
700
|
-
return {
|
|
701
|
-
...turn,
|
|
702
|
-
status: "interrupted",
|
|
703
|
-
tool_calls: turn.tool_calls.map(
|
|
704
|
-
(toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "cancelled" } : toolCall
|
|
705
|
-
)
|
|
706
|
-
};
|
|
707
|
-
});
|
|
708
|
-
return {
|
|
709
|
-
...updateSessionState(state, sessionId, turns)
|
|
710
|
-
};
|
|
711
|
-
});
|
|
712
|
-
},
|
|
713
|
-
markFailed: (sessionId) => {
|
|
714
|
-
set((state) => {
|
|
715
|
-
const turns = (state.turns[sessionId] ?? []).map((turn) => {
|
|
716
|
-
if (turn.status !== "streaming") return turn;
|
|
717
|
-
return {
|
|
718
|
-
...turn,
|
|
719
|
-
status: "failed",
|
|
720
|
-
tool_calls: turn.tool_calls.map(
|
|
721
|
-
(toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "error" } : toolCall
|
|
722
|
-
)
|
|
723
|
-
};
|
|
724
|
-
});
|
|
725
|
-
return {
|
|
726
|
-
...updateSessionState(state, sessionId, turns)
|
|
727
|
-
};
|
|
728
|
-
});
|
|
729
|
-
},
|
|
730
|
-
setStreaming: (sessionId, streaming) => {
|
|
731
|
-
set((state) => ({
|
|
732
|
-
isStreaming: { ...state.isStreaming, [sessionId]: streaming }
|
|
733
|
-
}));
|
|
734
|
-
},
|
|
735
|
-
setAskAnswers: (sessionId, answers) => {
|
|
736
|
-
set((state) => ({
|
|
737
|
-
askAnswers: {
|
|
738
|
-
...state.askAnswers,
|
|
739
|
-
[sessionId]: answers
|
|
740
|
-
}
|
|
741
|
-
}));
|
|
742
|
-
},
|
|
743
|
-
clearMessages: (sessionId) => {
|
|
744
|
-
set((state) => {
|
|
745
|
-
const { [sessionId]: _turns, ...restTurns } = state.turns;
|
|
746
|
-
const { [sessionId]: _messages, ...restMessages } = state.messages;
|
|
747
|
-
const { [sessionId]: _answers, ...restAnswers } = state.askAnswers;
|
|
748
|
-
const { [sessionId]: _loops, ...restLoops } = state.agentLoops;
|
|
749
|
-
const { [sessionId]: _compaction, ...restCompactions } = state.activeCompactions;
|
|
750
|
-
return {
|
|
751
|
-
turns: restTurns,
|
|
752
|
-
messages: restMessages,
|
|
753
|
-
askAnswers: restAnswers,
|
|
754
|
-
agentLoops: restLoops,
|
|
755
|
-
activeCompactions: restCompactions
|
|
756
|
-
};
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
}));
|
|
760
|
-
|
|
761
|
-
// src/react/stores/task-store.ts
|
|
762
|
-
import { create as create4 } from "zustand";
|
|
763
|
-
var EMPTY_TASKS = [];
|
|
764
|
-
var useTaskStore = create4()((set, get) => ({
|
|
765
|
-
...createClientActions(set),
|
|
766
|
-
tasks: {},
|
|
767
|
-
setTasks: (sessionId, tasks) => {
|
|
768
|
-
set((state) => ({
|
|
769
|
-
tasks: { ...state.tasks, [sessionId]: tasks }
|
|
770
|
-
}));
|
|
771
|
-
},
|
|
772
|
-
getTasks: (sessionId) => {
|
|
773
|
-
return get().tasks[sessionId] ?? EMPTY_TASKS;
|
|
774
|
-
}
|
|
775
|
-
}));
|
|
776
|
-
|
|
777
|
-
// src/react/stores/session-store.ts
|
|
778
|
-
var DEFAULT_SESSION_MODE = "executing";
|
|
779
|
-
var onSessionChange = null;
|
|
780
|
-
function invalidateHomeSidebarSessions() {
|
|
781
|
-
const queryClient = globalThis.__agentQueryClient;
|
|
782
|
-
if (!queryClient) return;
|
|
783
|
-
void queryClient.invalidateQueries({ queryKey: ["sessions", "home-sidebar"] });
|
|
784
|
-
}
|
|
785
|
-
function isSessionAccessRevoked(error) {
|
|
786
|
-
if (error instanceof Error) {
|
|
787
|
-
const msg = error.message;
|
|
788
|
-
return msg.includes("API 403") || msg.includes("API 404");
|
|
789
|
-
}
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
function removeSessionArtifacts(sessionId) {
|
|
793
|
-
useChatStore.getState().clearMessages(sessionId);
|
|
794
|
-
useTaskStore.getState().setTasks(sessionId, []);
|
|
795
|
-
}
|
|
796
|
-
function pruneSessionState(state, sessionId) {
|
|
797
|
-
const nextFresh = new Set(state._freshSessions);
|
|
798
|
-
nextFresh.delete(sessionId);
|
|
799
|
-
const { [sessionId]: _mode, ...modes } = state.modes;
|
|
800
|
-
const { [sessionId]: _rewindDraft, ...rewindDrafts } = state.rewindDrafts;
|
|
801
|
-
return {
|
|
802
|
-
sessions: state.sessions.filter((session) => session.id !== sessionId),
|
|
803
|
-
activeSessionId: state.activeSessionId === sessionId ? null : state.activeSessionId,
|
|
804
|
-
modes,
|
|
805
|
-
rewindDrafts,
|
|
806
|
-
_freshSessions: nextFresh
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
function navigateAwayFromSession(sessionId) {
|
|
810
|
-
if (typeof window === "undefined") return;
|
|
811
|
-
if (window.location.pathname !== `/chat/${sessionId}`) return;
|
|
812
|
-
window.history.replaceState(window.history.state, "", "/chat");
|
|
813
|
-
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
814
|
-
}
|
|
815
|
-
function handleUnreadableSession(set, get, sessionId) {
|
|
816
|
-
const wasActive = get().activeSessionId === sessionId;
|
|
817
|
-
removeSessionArtifacts(sessionId);
|
|
818
|
-
set((state) => pruneSessionState(state, sessionId));
|
|
819
|
-
if (wasActive) {
|
|
820
|
-
onSessionChange?.(null);
|
|
821
|
-
navigateAwayFromSession(sessionId);
|
|
822
|
-
}
|
|
823
|
-
invalidateHomeSidebarSessions();
|
|
824
|
-
}
|
|
825
|
-
function isPlainRecord(value) {
|
|
826
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
827
|
-
}
|
|
828
|
-
function extractModeFromBlocks2(blocks) {
|
|
829
|
-
const modeBlock = blocks.find((block) => block.type === "mode_change");
|
|
830
|
-
if (modeBlock && isPlainRecord(modeBlock.content) && (modeBlock.content.to === "planning" || modeBlock.content.to === "executing")) {
|
|
831
|
-
return modeBlock.content.to;
|
|
832
|
-
}
|
|
833
|
-
if (blocks.some((block) => block.type === "planning_enter")) return "planning";
|
|
834
|
-
if (blocks.some((block) => block.type === "planning_exit")) return "executing";
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
function toSelectionMap(value) {
|
|
838
|
-
if (!isPlainRecord(value)) return {};
|
|
839
|
-
const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
|
|
840
|
-
if (!Array.isArray(optionIndexes)) return null;
|
|
841
|
-
const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
|
|
842
|
-
return [Number(questionKey), parsedIndexes];
|
|
843
|
-
}).filter((entry) => entry !== null);
|
|
844
|
-
return Object.fromEntries(entries);
|
|
845
|
-
}
|
|
846
|
-
function toCustomMap(value) {
|
|
847
|
-
if (!isPlainRecord(value)) return {};
|
|
848
|
-
const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
|
|
849
|
-
return Object.fromEntries(entries);
|
|
850
|
-
}
|
|
851
|
-
function extractAskAnswers(turns) {
|
|
852
|
-
const answers = {};
|
|
853
|
-
for (const turn of turns) {
|
|
854
|
-
for (const block of turn.blocks) {
|
|
855
|
-
if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
|
|
856
|
-
answers[block.tool_call_id] = {
|
|
857
|
-
selections: toSelectionMap(isPlainRecord(block.content) ? block.content.selections : void 0),
|
|
858
|
-
custom: toCustomMap(isPlainRecord(block.content) ? block.content.custom : void 0)
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
return answers;
|
|
863
|
-
}
|
|
864
|
-
function registerCreatedSessionState(set, session, mode = DEFAULT_SESSION_MODE) {
|
|
865
|
-
useChatStore.getState().setTurns(session.id, []);
|
|
866
|
-
useTaskStore.getState().setTasks(session.id, []);
|
|
867
|
-
set((state) => ({
|
|
868
|
-
sessions: [session, ...state.sessions.filter((item) => item.id !== session.id)],
|
|
869
|
-
modes: { ...state.modes, [session.id]: state.modes[session.id] ?? mode },
|
|
870
|
-
_freshSessions: new Set(state._freshSessions).add(session.id)
|
|
871
|
-
}));
|
|
872
|
-
}
|
|
873
|
-
async function revalidateViewerSessions(existingSessions) {
|
|
874
|
-
const viewerSessions = existingSessions.filter((session) => session.viewer_role === "viewer");
|
|
875
|
-
if (viewerSessions.length === 0) {
|
|
876
|
-
return [];
|
|
877
|
-
}
|
|
878
|
-
const refreshed = await Promise.all(
|
|
879
|
-
viewerSessions.map(async (session) => {
|
|
880
|
-
try {
|
|
881
|
-
return await getSession(session.id);
|
|
882
|
-
} catch (error) {
|
|
883
|
-
if (isSessionAccessRevoked(error)) {
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
return session;
|
|
887
|
-
}
|
|
888
|
-
})
|
|
889
|
-
);
|
|
890
|
-
return refreshed.filter((session) => session !== null);
|
|
891
|
-
}
|
|
892
|
-
var useSessionStore = create5()((set, get) => ({
|
|
893
|
-
...createClientActions(set),
|
|
894
|
-
sessions: [],
|
|
895
|
-
activeSessionId: null,
|
|
896
|
-
loading: false,
|
|
897
|
-
modes: {},
|
|
898
|
-
rewindDrafts: {},
|
|
899
|
-
_freshSessions: /* @__PURE__ */ new Set(),
|
|
900
|
-
fetchSessions: async () => {
|
|
901
|
-
set({ loading: true });
|
|
902
|
-
try {
|
|
903
|
-
const currentState = get();
|
|
904
|
-
const [sessions, viewerSessions] = await Promise.all([
|
|
905
|
-
listSessions(),
|
|
906
|
-
revalidateViewerSessions(currentState.sessions)
|
|
907
|
-
]);
|
|
908
|
-
const knownSessionIds = new Set(sessions.map((session) => session.id));
|
|
909
|
-
const mergedSessions = [
|
|
910
|
-
...sessions,
|
|
911
|
-
...viewerSessions.filter((session) => !knownSessionIds.has(session.id))
|
|
912
|
-
];
|
|
913
|
-
const removedSessionIds = currentState.sessions.filter(
|
|
914
|
-
(session) => session.viewer_role === "viewer" && !mergedSessions.some((candidate) => candidate.id === session.id)
|
|
915
|
-
).map((session) => session.id);
|
|
916
|
-
const activeSessionId = currentState.activeSessionId;
|
|
917
|
-
for (const sessionId of removedSessionIds) {
|
|
918
|
-
removeSessionArtifacts(sessionId);
|
|
919
|
-
}
|
|
920
|
-
let nextState = {
|
|
921
|
-
sessions: mergedSessions,
|
|
922
|
-
activeSessionId: currentState.activeSessionId,
|
|
923
|
-
loading: false,
|
|
924
|
-
modes: currentState.modes,
|
|
925
|
-
rewindDrafts: currentState.rewindDrafts,
|
|
926
|
-
_freshSessions: currentState._freshSessions
|
|
927
|
-
};
|
|
928
|
-
for (const sessionId of removedSessionIds) {
|
|
929
|
-
nextState = {
|
|
930
|
-
...nextState,
|
|
931
|
-
...pruneSessionState(
|
|
932
|
-
{
|
|
933
|
-
...currentState,
|
|
934
|
-
sessions: nextState.sessions ?? currentState.sessions,
|
|
935
|
-
activeSessionId: nextState.activeSessionId ?? currentState.activeSessionId,
|
|
936
|
-
loading: nextState.loading ?? currentState.loading,
|
|
937
|
-
modes: nextState.modes ?? currentState.modes,
|
|
938
|
-
rewindDrafts: nextState.rewindDrafts ?? currentState.rewindDrafts,
|
|
939
|
-
_freshSessions: nextState._freshSessions ?? currentState._freshSessions
|
|
940
|
-
},
|
|
941
|
-
sessionId
|
|
942
|
-
)
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
set(nextState);
|
|
946
|
-
if (activeSessionId && removedSessionIds.includes(activeSessionId)) {
|
|
947
|
-
onSessionChange?.(null);
|
|
948
|
-
navigateAwayFromSession(activeSessionId);
|
|
949
|
-
}
|
|
950
|
-
invalidateHomeSidebarSessions();
|
|
951
|
-
} catch (error) {
|
|
952
|
-
set({ loading: false });
|
|
953
|
-
throw error;
|
|
954
|
-
}
|
|
955
|
-
},
|
|
956
|
-
createSession: async (intent) => {
|
|
957
|
-
const { session_id } = await createSession(intent);
|
|
958
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
959
|
-
get().registerCreatedSession({
|
|
960
|
-
id: session_id,
|
|
961
|
-
intent: intent ?? "",
|
|
962
|
-
status: "created",
|
|
963
|
-
created_at: now,
|
|
964
|
-
updated_at: now
|
|
965
|
-
});
|
|
966
|
-
get().setActiveSession(session_id);
|
|
967
|
-
invalidateHomeSidebarSessions();
|
|
968
|
-
get().fetchSessions().catch(() => {
|
|
969
|
-
});
|
|
970
|
-
return session_id;
|
|
971
|
-
},
|
|
972
|
-
registerCreatedSession: (session, mode = DEFAULT_SESSION_MODE) => {
|
|
973
|
-
registerCreatedSessionState(set, session, mode);
|
|
974
|
-
},
|
|
975
|
-
upsertSession: (session) => {
|
|
976
|
-
set((state) => ({
|
|
977
|
-
sessions: state.sessions.some((item) => item.id === session.id) ? state.sessions.map((item) => item.id === session.id ? { ...item, ...session } : item) : [session, ...state.sessions]
|
|
978
|
-
}));
|
|
979
|
-
},
|
|
980
|
-
isFreshSession: (sessionId) => get()._freshSessions.has(sessionId),
|
|
981
|
-
setActiveSession: (id) => {
|
|
982
|
-
set({ activeSessionId: id });
|
|
983
|
-
onSessionChange?.(id);
|
|
984
|
-
getSession(id).then((detail) => {
|
|
985
|
-
set((state) => ({
|
|
986
|
-
sessions: state.sessions.some((s) => s.id === id) ? state.sessions.map(
|
|
987
|
-
(s) => s.id === id ? { ...s, status: detail.status, updated_at: detail.updated_at } : s
|
|
988
|
-
) : [detail, ...state.sessions]
|
|
989
|
-
}));
|
|
990
|
-
}).catch(() => {
|
|
991
|
-
});
|
|
992
|
-
const tasksPromise = getSessionTasks(id).catch(() => null);
|
|
993
|
-
Promise.all([getSessionTurns(id), tasksPromise]).then(([turns, tasks]) => {
|
|
994
|
-
if (tasks) useTaskStore.getState().setTasks(id, tasks);
|
|
995
|
-
useChatStore.getState().setAskAnswers(id, extractAskAnswers(turns));
|
|
996
|
-
let inferredMode = DEFAULT_SESSION_MODE;
|
|
997
|
-
for (const turn of turns) {
|
|
998
|
-
const nextMode = extractModeFromBlocks2(turn.blocks);
|
|
999
|
-
if (nextMode) {
|
|
1000
|
-
inferredMode = nextMode;
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
set((state) => {
|
|
1004
|
-
if (state.modes[id] == null) {
|
|
1005
|
-
return { modes: { ...state.modes, [id]: inferredMode } };
|
|
1006
|
-
}
|
|
1007
|
-
return state;
|
|
1008
|
-
});
|
|
1009
|
-
const isFresh = get()._freshSessions.has(id);
|
|
1010
|
-
if (isFresh) {
|
|
1011
|
-
set((state) => {
|
|
1012
|
-
const next = new Set(state._freshSessions);
|
|
1013
|
-
next.delete(id);
|
|
1014
|
-
return { _freshSessions: next };
|
|
1015
|
-
});
|
|
1016
|
-
if (turns.length > 0) {
|
|
1017
|
-
useChatStore.getState().setTurns(id, turns);
|
|
1018
|
-
}
|
|
1019
|
-
} else {
|
|
1020
|
-
useChatStore.getState().setTurns(id, turns);
|
|
1021
|
-
}
|
|
1022
|
-
}).catch((error) => {
|
|
1023
|
-
if (isSessionAccessRevoked(error)) {
|
|
1024
|
-
handleUnreadableSession(set, get, id);
|
|
1025
|
-
return;
|
|
1026
|
-
}
|
|
1027
|
-
console.error("Failed to load session data", error);
|
|
1028
|
-
});
|
|
1029
|
-
},
|
|
1030
|
-
clearActiveSession: () => {
|
|
1031
|
-
set({ activeSessionId: null });
|
|
1032
|
-
onSessionChange?.(null);
|
|
1033
|
-
},
|
|
1034
|
-
deleteSession: async (id) => {
|
|
1035
|
-
await deleteSession(id);
|
|
1036
|
-
invalidateHomeSidebarSessions();
|
|
1037
|
-
const wasActive = get().activeSessionId === id;
|
|
1038
|
-
await get().fetchSessions();
|
|
1039
|
-
if (!wasActive) {
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
const nextId = get().sessions[0]?.id ?? null;
|
|
1043
|
-
if (nextId) {
|
|
1044
|
-
get().setActiveSession(nextId);
|
|
1045
|
-
return;
|
|
1046
|
-
}
|
|
1047
|
-
get().clearActiveSession();
|
|
1048
|
-
},
|
|
1049
|
-
updateSessionStatus: (sessionId, status) => {
|
|
1050
|
-
set((state) => ({
|
|
1051
|
-
sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, status } : s)
|
|
1052
|
-
}));
|
|
1053
|
-
if (status === "failed" || status === "interrupted") {
|
|
1054
|
-
useChatStore.getState().setStreaming(sessionId, false);
|
|
1055
|
-
if (status === "interrupted") {
|
|
1056
|
-
useChatStore.getState().markInterrupted(sessionId);
|
|
1057
|
-
} else {
|
|
1058
|
-
useChatStore.getState().markFailed(sessionId);
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
},
|
|
1062
|
-
updateSessionIntent: (sessionId, intent) => {
|
|
1063
|
-
set((state) => ({
|
|
1064
|
-
sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, intent } : s)
|
|
1065
|
-
}));
|
|
1066
|
-
},
|
|
1067
|
-
patchSession: (sessionId, patch) => {
|
|
1068
|
-
set((state) => ({
|
|
1069
|
-
sessions: state.sessions.map(
|
|
1070
|
-
(s) => s.id === sessionId ? { ...s, ...patch } : s
|
|
1071
|
-
)
|
|
1072
|
-
}));
|
|
1073
|
-
},
|
|
1074
|
-
pinSession: async (sessionId, pinned) => {
|
|
1075
|
-
const updated = await pinSession(sessionId, pinned);
|
|
1076
|
-
set((state) => ({
|
|
1077
|
-
sessions: state.sessions.map(
|
|
1078
|
-
(s) => s.id === sessionId ? { ...s, is_pinned: updated.is_pinned, pinned_at: updated.pinned_at } : s
|
|
1079
|
-
)
|
|
1080
|
-
}));
|
|
1081
|
-
invalidateHomeSidebarSessions();
|
|
1082
|
-
},
|
|
1083
|
-
setRewindDraft: (sessionId, text) => {
|
|
1084
|
-
set((state) => {
|
|
1085
|
-
if (text == null) {
|
|
1086
|
-
const { [sessionId]: _, ...rest } = state.rewindDrafts;
|
|
1087
|
-
return { rewindDrafts: rest };
|
|
1088
|
-
}
|
|
1089
|
-
return {
|
|
1090
|
-
rewindDrafts: {
|
|
1091
|
-
...state.rewindDrafts,
|
|
1092
|
-
[sessionId]: text
|
|
1093
|
-
}
|
|
1094
|
-
};
|
|
1095
|
-
});
|
|
1096
|
-
},
|
|
1097
|
-
setMode: (sessionId, mode) => {
|
|
1098
|
-
set((state) => ({ modes: { ...state.modes, [sessionId]: mode } }));
|
|
1099
|
-
},
|
|
1100
|
-
togglePlanningMode: (sessionId) => {
|
|
1101
|
-
const current = get().modes[sessionId] ?? DEFAULT_SESSION_MODE;
|
|
1102
|
-
const next = current === "executing" ? "planning" : "executing";
|
|
1103
|
-
set((state) => ({ modes: { ...state.modes, [sessionId]: next } }));
|
|
1104
|
-
},
|
|
1105
|
-
toggleSharing: async (sessionId) => {
|
|
1106
|
-
const session = get().sessions.find((s) => s.id === sessionId);
|
|
1107
|
-
if (!session) return;
|
|
1108
|
-
const newShared = !session.shared;
|
|
1109
|
-
const result = await updateSharing(sessionId, newShared);
|
|
1110
|
-
set((state) => ({
|
|
1111
|
-
sessions: state.sessions.map(
|
|
1112
|
-
(s) => s.id === sessionId ? { ...s, shared: result.shared } : s
|
|
1113
|
-
)
|
|
1114
|
-
}));
|
|
1115
|
-
},
|
|
1116
|
-
reset: () => {
|
|
1117
|
-
set({
|
|
1118
|
-
sessions: [],
|
|
1119
|
-
activeSessionId: null,
|
|
1120
|
-
loading: false,
|
|
1121
|
-
modes: {},
|
|
1122
|
-
rewindDrafts: {},
|
|
1123
|
-
_freshSessions: /* @__PURE__ */ new Set()
|
|
1124
|
-
});
|
|
1125
|
-
invalidateHomeSidebarSessions();
|
|
1126
|
-
}
|
|
1127
|
-
}));
|
|
1128
|
-
setChatStoreSessionAccessor(() => useSessionStore.getState().activeSessionId);
|
|
1129
|
-
|
|
1130
|
-
// src/react/stores/auth-store.ts
|
|
1131
|
-
var noopStorage = {
|
|
1132
|
-
getItem: () => null,
|
|
1133
|
-
setItem: () => {
|
|
1134
|
-
},
|
|
1135
|
-
removeItem: () => {
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1138
|
-
function shouldClearPersistedAuthState(value) {
|
|
1139
|
-
try {
|
|
1140
|
-
const parsed = JSON.parse(value);
|
|
1141
|
-
return parsed.state?.token == null && parsed.state?.user == null;
|
|
1142
|
-
} catch {
|
|
1143
|
-
return false;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
var authStorage = createJSONStorage(() => {
|
|
1147
|
-
if (typeof localStorage === "undefined") {
|
|
1148
|
-
return noopStorage;
|
|
1149
|
-
}
|
|
1150
|
-
return {
|
|
1151
|
-
getItem: (name) => localStorage.getItem(name),
|
|
1152
|
-
setItem: (name, value) => {
|
|
1153
|
-
if (shouldClearPersistedAuthState(value)) {
|
|
1154
|
-
localStorage.removeItem(name);
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
localStorage.setItem(name, value);
|
|
1158
|
-
},
|
|
1159
|
-
removeItem: (name) => localStorage.removeItem(name)
|
|
1160
|
-
};
|
|
1161
|
-
});
|
|
1162
|
-
function finishAuth(set, payload) {
|
|
1163
|
-
set({
|
|
1164
|
-
token: payload.token,
|
|
1165
|
-
socketAuthToken: null,
|
|
1166
|
-
user: payload.user,
|
|
1167
|
-
loading: false,
|
|
1168
|
-
error: null
|
|
1169
|
-
});
|
|
1170
|
-
agentSocket?.reconnect();
|
|
1171
|
-
useSessionStore.getState().fetchSessions().catch(() => {
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
function finishCookieHydration(set, payload) {
|
|
1175
|
-
set({
|
|
1176
|
-
token: null,
|
|
1177
|
-
socketAuthToken: payload.token,
|
|
1178
|
-
user: payload.user,
|
|
1179
|
-
loading: false,
|
|
1180
|
-
error: null
|
|
1181
|
-
});
|
|
1182
|
-
agentSocket?.reconnect();
|
|
1183
|
-
useSessionStore.getState().fetchSessions().catch(() => {
|
|
1184
|
-
});
|
|
1185
|
-
}
|
|
1186
|
-
function toUser(info) {
|
|
1187
|
-
return {
|
|
1188
|
-
id: info.id,
|
|
1189
|
-
username: info.username,
|
|
1190
|
-
display_name: info.display_name,
|
|
1191
|
-
avatar_url: info.avatar_url,
|
|
1192
|
-
is_admin: info.is_admin
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
var useAuthStore = create6()(
|
|
1196
|
-
persist(
|
|
1197
|
-
(set, get) => ({
|
|
1198
|
-
...createClientActions(set),
|
|
1199
|
-
token: null,
|
|
1200
|
-
socketAuthToken: null,
|
|
1201
|
-
user: null,
|
|
1202
|
-
loading: false,
|
|
1203
|
-
error: null,
|
|
1204
|
-
logout: () => {
|
|
1205
|
-
void logout().catch(() => {
|
|
1206
|
-
});
|
|
1207
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1208
|
-
agentSocket?.disconnect();
|
|
1209
|
-
useSessionStore.getState().reset();
|
|
1210
|
-
},
|
|
1211
|
-
checkAuth: async () => {
|
|
1212
|
-
const token = get().token;
|
|
1213
|
-
if (!token) return;
|
|
1214
|
-
try {
|
|
1215
|
-
const auth = await getMe();
|
|
1216
|
-
finishAuth(set, { token: auth.token, user: toUser(auth) });
|
|
1217
|
-
} catch {
|
|
1218
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1219
|
-
useSessionStore.getState().reset();
|
|
1220
|
-
}
|
|
1221
|
-
},
|
|
1222
|
-
hydrateFromCookie: async () => {
|
|
1223
|
-
set({ loading: true, error: null });
|
|
1224
|
-
const previousToken = get().token;
|
|
1225
|
-
if (previousToken) {
|
|
1226
|
-
try {
|
|
1227
|
-
const auth = await getMe();
|
|
1228
|
-
finishAuth(set, { token: auth.token, user: toUser(auth) });
|
|
1229
|
-
return;
|
|
1230
|
-
} catch {
|
|
1231
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1232
|
-
useSessionStore.getState().reset();
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
try {
|
|
1236
|
-
const auth = await getMe();
|
|
1237
|
-
finishCookieHydration(set, { token: auth.token, user: toUser(auth) });
|
|
1238
|
-
} catch {
|
|
1239
|
-
set({
|
|
1240
|
-
token: null,
|
|
1241
|
-
socketAuthToken: null,
|
|
1242
|
-
user: null,
|
|
1243
|
-
loading: false,
|
|
1244
|
-
error: null
|
|
1245
|
-
});
|
|
1246
|
-
useSessionStore.getState().reset();
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}),
|
|
1250
|
-
{
|
|
1251
|
-
name: "agent-auth",
|
|
1252
|
-
storage: authStorage,
|
|
1253
|
-
partialize: (state) => {
|
|
1254
|
-
if (state.socketAuthToken) {
|
|
1255
|
-
return { token: null, user: null };
|
|
1256
|
-
}
|
|
1257
|
-
return { token: state.token, user: state.user };
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
)
|
|
1261
|
-
);
|
|
1262
|
-
|
|
1263
|
-
// src/react/stores/background-store.ts
|
|
1264
|
-
import { create as create7 } from "zustand";
|
|
1265
|
-
var useBackgroundStore = create7()((set) => ({
|
|
1266
|
-
...createClientActions(set),
|
|
1267
|
-
tasks: {},
|
|
1268
|
-
selectedTaskId: {},
|
|
1269
|
-
setTasks: (sessionId, tasks) => set((state) => ({
|
|
1270
|
-
tasks: { ...state.tasks, [sessionId]: tasks },
|
|
1271
|
-
selectedTaskId: {
|
|
1272
|
-
...state.selectedTaskId,
|
|
1273
|
-
[sessionId]: state.selectedTaskId[sessionId] ?? tasks[0]?.id ?? null
|
|
1274
|
-
}
|
|
1275
|
-
})),
|
|
1276
|
-
upsertTask: (sessionId, task) => set((state) => {
|
|
1277
|
-
const existing = state.tasks[sessionId] ?? [];
|
|
1278
|
-
const index = existing.findIndex((item) => item.id === task.id);
|
|
1279
|
-
const next = [...existing];
|
|
1280
|
-
if (index >= 0) {
|
|
1281
|
-
next[index] = { ...next[index], ...task };
|
|
1282
|
-
} else {
|
|
1283
|
-
next.unshift(task);
|
|
1284
|
-
}
|
|
1285
|
-
return {
|
|
1286
|
-
tasks: { ...state.tasks, [sessionId]: next },
|
|
1287
|
-
selectedTaskId: {
|
|
1288
|
-
...state.selectedTaskId,
|
|
1289
|
-
[sessionId]: state.selectedTaskId[sessionId] ?? task.id
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
}),
|
|
1293
|
-
selectTask: (sessionId, taskId) => set((state) => ({
|
|
1294
|
-
selectedTaskId: { ...state.selectedTaskId, [sessionId]: taskId }
|
|
1295
|
-
}))
|
|
1296
|
-
}));
|
|
1297
|
-
|
|
1298
|
-
// src/react/stores/card-state-store.ts
|
|
1299
|
-
import { create as create8 } from "zustand";
|
|
1300
|
-
var useCardStateStore = create8((set, get) => ({
|
|
1301
|
-
...createClientActions(set),
|
|
1302
|
-
states: {},
|
|
1303
|
-
getCardState: (cardId) => {
|
|
1304
|
-
return get().states[cardId];
|
|
1305
|
-
},
|
|
1306
|
-
setCardState: (cardId, state) => {
|
|
1307
|
-
set((prev) => ({
|
|
1308
|
-
states: { ...prev.states, [cardId]: state }
|
|
1309
|
-
}));
|
|
1310
|
-
},
|
|
1311
|
-
removeCardState: (cardId) => {
|
|
1312
|
-
set((prev) => {
|
|
1313
|
-
const { [cardId]: _, ...rest } = prev.states;
|
|
1314
|
-
return { states: rest };
|
|
1315
|
-
});
|
|
1316
|
-
},
|
|
1317
|
-
clearAllStates: () => {
|
|
1318
|
-
set({ states: {} });
|
|
1319
|
-
}
|
|
1320
|
-
}));
|
|
1321
|
-
|
|
1322
|
-
// src/react/stores/connection-store.ts
|
|
1323
|
-
import { create as create9 } from "zustand";
|
|
1324
|
-
var initialConnectionState = {
|
|
1325
|
-
status: "disconnected",
|
|
1326
|
-
reconnectAttempt: 0,
|
|
1327
|
-
lastConnectedAt: null,
|
|
1328
|
-
lastDisconnectedAt: null,
|
|
1329
|
-
hasEverConnected: false
|
|
1330
|
-
};
|
|
1331
|
-
var useConnectionStore = create9()((set) => ({
|
|
1332
|
-
...createClientActions(set),
|
|
1333
|
-
...initialConnectionState,
|
|
1334
|
-
markConnecting: (attempt = 0) => set({
|
|
1335
|
-
status: "connecting",
|
|
1336
|
-
reconnectAttempt: attempt
|
|
1337
|
-
}),
|
|
1338
|
-
markConnected: () => set({
|
|
1339
|
-
status: "connected",
|
|
1340
|
-
reconnectAttempt: 0,
|
|
1341
|
-
lastConnectedAt: Date.now(),
|
|
1342
|
-
hasEverConnected: true
|
|
1343
|
-
}),
|
|
1344
|
-
markDisconnected: () => set({
|
|
1345
|
-
status: "disconnected",
|
|
1346
|
-
reconnectAttempt: 0,
|
|
1347
|
-
lastDisconnectedAt: Date.now()
|
|
1348
|
-
}),
|
|
1349
|
-
reset: () => set(initialConnectionState)
|
|
1350
|
-
}));
|
|
1351
|
-
|
|
1352
|
-
// src/react/stores/runtime-store.ts
|
|
1353
|
-
import { create as create10 } from "zustand";
|
|
1354
|
-
var useRuntimeStore = create10()((set) => ({
|
|
1355
|
-
...createClientActions(set),
|
|
1356
|
-
events: {},
|
|
1357
|
-
addEvent: (sessionId, event) => set((state) => {
|
|
1358
|
-
const current = state.events[sessionId] ?? [];
|
|
1359
|
-
const nextEvent = {
|
|
1360
|
-
...event,
|
|
1361
|
-
id: `${Date.now()}-${current.length}`,
|
|
1362
|
-
sessionId,
|
|
1363
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1364
|
-
};
|
|
1365
|
-
return {
|
|
1366
|
-
events: {
|
|
1367
|
-
...state.events,
|
|
1368
|
-
[sessionId]: [...current.slice(-59), nextEvent]
|
|
1369
|
-
}
|
|
1370
|
-
};
|
|
1371
|
-
}),
|
|
1372
|
-
clearSession: (sessionId) => set((state) => ({
|
|
1373
|
-
events: { ...state.events, [sessionId]: [] }
|
|
1374
|
-
}))
|
|
1375
|
-
}));
|
|
1376
|
-
|
|
1377
|
-
// src/react/stores/gis-store.ts
|
|
1378
|
-
import { create as create11 } from "zustand";
|
|
1379
|
-
var EMPTY_GOALS = [];
|
|
1380
|
-
var EMPTY_RESOURCES = [];
|
|
1381
|
-
var EMPTY_TARGETS = [];
|
|
1382
|
-
var EMPTY_COMMANDS = [];
|
|
1383
|
-
function newCommandId() {
|
|
1384
|
-
if (typeof globalThis !== "undefined" && "crypto" in globalThis) {
|
|
1385
|
-
return globalThis.crypto?.randomUUID?.() ?? `gis-map-${Date.now()}-${Math.random()}`;
|
|
1386
|
-
}
|
|
1387
|
-
return `gis-map-${Date.now()}-${Math.random()}`;
|
|
1388
|
-
}
|
|
1389
|
-
var useGisStore = create11()((set, get) => ({
|
|
1390
|
-
...createClientActions(set),
|
|
1391
|
-
goalsBySession: {},
|
|
1392
|
-
resourcesBySession: {},
|
|
1393
|
-
targetsBySession: {},
|
|
1394
|
-
pendingMapCommandsBySession: {},
|
|
1395
|
-
setGoals: (sessionId, goals) => {
|
|
1396
|
-
set((state) => ({
|
|
1397
|
-
goalsBySession: { ...state.goalsBySession, [sessionId]: goals }
|
|
1398
|
-
}));
|
|
1399
|
-
},
|
|
1400
|
-
setResources: (sessionId, resources) => {
|
|
1401
|
-
set((state) => ({
|
|
1402
|
-
resourcesBySession: { ...state.resourcesBySession, [sessionId]: resources }
|
|
1403
|
-
}));
|
|
1404
|
-
},
|
|
1405
|
-
setTargets: (sessionId, targets) => {
|
|
1406
|
-
set((state) => ({
|
|
1407
|
-
targetsBySession: { ...state.targetsBySession, [sessionId]: targets }
|
|
1408
|
-
}));
|
|
1409
|
-
},
|
|
1410
|
-
pushMapCommand: (sessionId, command) => {
|
|
1411
|
-
set((state) => ({
|
|
1412
|
-
pendingMapCommandsBySession: {
|
|
1413
|
-
...state.pendingMapCommandsBySession,
|
|
1414
|
-
[sessionId]: [
|
|
1415
|
-
...state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS,
|
|
1416
|
-
{
|
|
1417
|
-
...command,
|
|
1418
|
-
id: newCommandId(),
|
|
1419
|
-
createdAt: Date.now()
|
|
1420
|
-
}
|
|
1421
|
-
]
|
|
1422
|
-
}
|
|
1423
|
-
}));
|
|
1424
|
-
},
|
|
1425
|
-
consumeMapCommand: (sessionId, commandId) => {
|
|
1426
|
-
set((state) => ({
|
|
1427
|
-
pendingMapCommandsBySession: {
|
|
1428
|
-
...state.pendingMapCommandsBySession,
|
|
1429
|
-
[sessionId]: (state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS).filter(
|
|
1430
|
-
(command) => command.id !== commandId
|
|
1431
|
-
)
|
|
1432
|
-
}
|
|
1433
|
-
}));
|
|
1434
|
-
},
|
|
1435
|
-
getGoals: (sessionId) => get().goalsBySession[sessionId] ?? EMPTY_GOALS,
|
|
1436
|
-
getResources: (sessionId) => get().resourcesBySession[sessionId] ?? EMPTY_RESOURCES,
|
|
1437
|
-
getTargets: (sessionId) => get().targetsBySession[sessionId] ?? EMPTY_TARGETS,
|
|
1438
|
-
getPendingMapCommands: (sessionId) => get().pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS
|
|
1439
|
-
}));
|
|
1440
|
-
|
|
1441
|
-
// src/react/stores/answer-callback-store.ts
|
|
1442
|
-
import { create as create12 } from "zustand";
|
|
1443
|
-
var useAnswerCallbackStore = create12()((set) => ({
|
|
1444
|
-
...createClientActions(set),
|
|
1445
|
-
callbacks: {},
|
|
1446
|
-
setAnswerCallback: (sessionId, callback) => {
|
|
1447
|
-
set((state) => ({
|
|
1448
|
-
callbacks: {
|
|
1449
|
-
...state.callbacks,
|
|
1450
|
-
[sessionId]: callback
|
|
1451
|
-
}
|
|
1452
|
-
}));
|
|
1453
|
-
}
|
|
1454
|
-
}));
|
|
1455
|
-
|
|
1456
|
-
// src/react/stores/runtime-features-store.ts
|
|
1457
|
-
import { create as create13 } from "zustand";
|
|
1458
|
-
var useRuntimeFeaturesStore = create13((set) => ({
|
|
1459
|
-
...createClientActions(set),
|
|
1460
|
-
asrEnabled: false,
|
|
1461
|
-
asrProvider: "volcengine",
|
|
1462
|
-
publicSharingEnabled: false,
|
|
1463
|
-
memoryEnabled: false,
|
|
1464
|
-
setFeatures: (features) => set((prev) => ({
|
|
1465
|
-
asrEnabled: features.asrEnabled ?? prev.asrEnabled,
|
|
1466
|
-
asrProvider: features.asrProvider ?? prev.asrProvider,
|
|
1467
|
-
publicSharingEnabled: features.publicSharingEnabled ?? prev.publicSharingEnabled,
|
|
1468
|
-
memoryEnabled: features.memoryEnabled ?? prev.memoryEnabled
|
|
1469
|
-
}))
|
|
1470
|
-
}));
|
|
1471
|
-
|
|
1472
|
-
// src/react/bootstrap.ts
|
|
1473
|
-
var bootstrappedClient = null;
|
|
1474
|
-
function getBootstrappedClient() {
|
|
1475
|
-
if (!bootstrappedClient) {
|
|
1476
|
-
throw new Error("bootstrapBladeClient() must be called before any SDK usage");
|
|
1477
|
-
}
|
|
1478
|
-
return bootstrappedClient;
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
// src/react/api/client.ts
|
|
1482
|
-
function getAuthedUrl(path) {
|
|
1483
|
-
return getClient().buildAuthedUrl(path);
|
|
1484
|
-
}
|
|
1485
|
-
async function apiFetchText(path, init) {
|
|
1486
|
-
return getClient().textFromInit(path, init);
|
|
1487
|
-
}
|
|
1488
|
-
async function apiFetchResponse(path, init) {
|
|
1489
|
-
return getClient().responseFromInit(path, init);
|
|
1490
|
-
}
|
|
1491
|
-
function getClient() {
|
|
1492
|
-
return getBootstrappedClient();
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
// src/react/api/sessions.ts
|
|
1496
|
-
var r2 = () => getClient().sessions;
|
|
1497
|
-
var listSessions = (...args) => r2().listSessions(...args);
|
|
1498
|
-
var createSession = (...args) => r2().createSession(...args);
|
|
1499
|
-
var getSession = (...args) => r2().getSession(...args);
|
|
1500
|
-
var pinSession = (...args) => r2().pinSession(...args);
|
|
1501
|
-
var updateSharing = (...args) => r2().updateSharing(...args);
|
|
1502
|
-
var getSessionTasks = (...args) => r2().getSessionTasks(...args);
|
|
1503
|
-
var getSessionTurns = (...args) => r2().getSessionTurns(...args);
|
|
1504
|
-
var deleteSession = (...args) => r2().deleteSession(...args);
|
|
1505
|
-
var listDir = (...args) => r2().listDir(...args);
|
|
1506
|
-
var uploadFiles = (...args) => r2().uploadFiles(...args);
|
|
1507
|
-
var deleteFile = (...args) => r2().deleteFile(...args);
|
|
1508
|
-
var renameFile = (...args) => r2().renameFile(...args);
|
|
1509
|
-
var copyFile = (...args) => r2().copyFile(...args);
|
|
1510
|
-
var getDownloadDirUrl = (...args) => r2().getDownloadDirUrl(...args);
|
|
1511
|
-
|
|
1512
|
-
// src/react/lib/utils.ts
|
|
1513
|
-
import { clsx } from "clsx";
|
|
1514
|
-
import { twMerge } from "tailwind-merge";
|
|
1515
|
-
function cn(...inputs) {
|
|
1516
|
-
return twMerge(clsx(inputs));
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
34
|
// src/react/components/workspace/FileTree.tsx
|
|
1520
35
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
1521
36
|
import {
|
|
@@ -1529,89 +44,6 @@ import {
|
|
|
1529
44
|
} from "lucide-react";
|
|
1530
45
|
import { useEffect, useEffectEvent, useRef, useState as useState2 } from "react";
|
|
1531
46
|
|
|
1532
|
-
// src/react/lib/session-file-preview.ts
|
|
1533
|
-
var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
|
|
1534
|
-
var DOCX_EXTS = /* @__PURE__ */ new Set(["docx", "doc"]);
|
|
1535
|
-
var EXCEL_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb"]);
|
|
1536
|
-
var PPT_EXTS = /* @__PURE__ */ new Set(["pptx", "ppt"]);
|
|
1537
|
-
var CSV_EXTS = /* @__PURE__ */ new Set(["csv"]);
|
|
1538
|
-
var MARKDOWN_EXTS = /* @__PURE__ */ new Set(["md", "markdown"]);
|
|
1539
|
-
var HTML_EXTS = /* @__PURE__ */ new Set(["html", "htm"]);
|
|
1540
|
-
function getExt(fileName) {
|
|
1541
|
-
return fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
1542
|
-
}
|
|
1543
|
-
function getDisplayFileName(filePath, fileName) {
|
|
1544
|
-
return fileName?.trim() || filePath.split("/").pop() || filePath;
|
|
1545
|
-
}
|
|
1546
|
-
function getSessionFilePath(sessionId, filePath) {
|
|
1547
|
-
return `/api/sessions/${sessionId}/files/${encodeURIComponent(filePath)}`;
|
|
1548
|
-
}
|
|
1549
|
-
function getSessionFileDownloadUrl(sessionId, filePath) {
|
|
1550
|
-
return getAuthedUrl(getSessionFilePath(sessionId, filePath));
|
|
1551
|
-
}
|
|
1552
|
-
function isTextualContentType(contentType) {
|
|
1553
|
-
return contentType.startsWith("text/") || contentType.includes("json") || contentType.includes("xml") || contentType.includes("yaml") || contentType.includes("javascript");
|
|
1554
|
-
}
|
|
1555
|
-
function buildSessionBinaryPreviewTarget(sessionId, filePath, fileName) {
|
|
1556
|
-
const resolvedFileName = getDisplayFileName(filePath, fileName);
|
|
1557
|
-
const ext = getExt(resolvedFileName);
|
|
1558
|
-
const filePathUrl = getSessionFilePath(sessionId, filePath);
|
|
1559
|
-
const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath);
|
|
1560
|
-
if (IMAGE_EXTS.has(ext)) {
|
|
1561
|
-
return { type: "image", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1562
|
-
}
|
|
1563
|
-
if (ext === "pdf") {
|
|
1564
|
-
return { type: "pdf", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1565
|
-
}
|
|
1566
|
-
if (DOCX_EXTS.has(ext)) {
|
|
1567
|
-
return { type: "docx", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1568
|
-
}
|
|
1569
|
-
if (EXCEL_EXTS.has(ext)) {
|
|
1570
|
-
return { type: "excel", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1571
|
-
}
|
|
1572
|
-
if (PPT_EXTS.has(ext)) {
|
|
1573
|
-
return {
|
|
1574
|
-
type: "ppt",
|
|
1575
|
-
content: filePathUrl,
|
|
1576
|
-
sourceUrl: downloadUrl,
|
|
1577
|
-
title: resolvedFileName,
|
|
1578
|
-
key: filePath
|
|
1579
|
-
};
|
|
1580
|
-
}
|
|
1581
|
-
return null;
|
|
1582
|
-
}
|
|
1583
|
-
async function resolveSessionFilePreviewTarget(sessionId, filePath, fileName) {
|
|
1584
|
-
const resolvedFileName = getDisplayFileName(filePath, fileName);
|
|
1585
|
-
const binaryTarget = buildSessionBinaryPreviewTarget(sessionId, filePath, resolvedFileName);
|
|
1586
|
-
if (binaryTarget) {
|
|
1587
|
-
return binaryTarget;
|
|
1588
|
-
}
|
|
1589
|
-
const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath);
|
|
1590
|
-
const res = await apiFetchResponse(getSessionFilePath(sessionId, filePath));
|
|
1591
|
-
const contentType = (res.headers.get("content-type") ?? "").toLowerCase();
|
|
1592
|
-
if (contentType.startsWith("video/")) {
|
|
1593
|
-
void res.body?.cancel();
|
|
1594
|
-
return { type: "video", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1595
|
-
}
|
|
1596
|
-
if (contentType.startsWith("audio/")) {
|
|
1597
|
-
void res.body?.cancel();
|
|
1598
|
-
return { type: "audio", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1599
|
-
}
|
|
1600
|
-
if (!isTextualContentType(contentType)) {
|
|
1601
|
-
void res.body?.cancel();
|
|
1602
|
-
return { type: "download", content: downloadUrl, title: resolvedFileName, key: filePath };
|
|
1603
|
-
}
|
|
1604
|
-
const content = await res.text();
|
|
1605
|
-
const ext = getExt(resolvedFileName);
|
|
1606
|
-
const type3 = MARKDOWN_EXTS.has(ext) ? "markdown" : CSV_EXTS.has(ext) ? "csv" : HTML_EXTS.has(ext) ? "html" : "file";
|
|
1607
|
-
return {
|
|
1608
|
-
type: type3,
|
|
1609
|
-
content,
|
|
1610
|
-
title: resolvedFileName,
|
|
1611
|
-
key: filePath
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
47
|
// src/react/lib/open-session-file.ts
|
|
1616
48
|
async function openSessionFileInPreview(sessionId, filePath, fileName, pushArtifact, shouldOpen = () => true) {
|
|
1617
49
|
try {
|
|
@@ -1624,37 +56,6 @@ async function openSessionFileInPreview(sessionId, filePath, fileName, pushArtif
|
|
|
1624
56
|
}
|
|
1625
57
|
}
|
|
1626
58
|
|
|
1627
|
-
// src/react/components/ui/collapsible.tsx
|
|
1628
|
-
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
1629
|
-
import { jsx } from "react/jsx-runtime";
|
|
1630
|
-
function Collapsible({
|
|
1631
|
-
...props
|
|
1632
|
-
}) {
|
|
1633
|
-
return /* @__PURE__ */ jsx(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
|
|
1634
|
-
}
|
|
1635
|
-
function CollapsibleTrigger({
|
|
1636
|
-
...props
|
|
1637
|
-
}) {
|
|
1638
|
-
return /* @__PURE__ */ jsx(
|
|
1639
|
-
CollapsiblePrimitive.CollapsibleTrigger,
|
|
1640
|
-
{
|
|
1641
|
-
"data-slot": "collapsible-trigger",
|
|
1642
|
-
...props
|
|
1643
|
-
}
|
|
1644
|
-
);
|
|
1645
|
-
}
|
|
1646
|
-
function CollapsibleContent({
|
|
1647
|
-
...props
|
|
1648
|
-
}) {
|
|
1649
|
-
return /* @__PURE__ */ jsx(
|
|
1650
|
-
CollapsiblePrimitive.CollapsibleContent,
|
|
1651
|
-
{
|
|
1652
|
-
"data-slot": "collapsible-content",
|
|
1653
|
-
...props
|
|
1654
|
-
}
|
|
1655
|
-
);
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
59
|
// src/react/components/ai-elements/file-tree.tsx
|
|
1659
60
|
import {
|
|
1660
61
|
ChevronRightIcon,
|
|
@@ -1669,7 +70,7 @@ import {
|
|
|
1669
70
|
useMemo,
|
|
1670
71
|
useState
|
|
1671
72
|
} from "react";
|
|
1672
|
-
import { Fragment, jsx
|
|
73
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
1673
74
|
var noop = () => {
|
|
1674
75
|
};
|
|
1675
76
|
var FileTreeContext = createContext({
|
|
@@ -1706,7 +107,7 @@ var FileTree = ({
|
|
|
1706
107
|
() => ({ expandedPaths, onSelect, selectedPath, togglePath }),
|
|
1707
108
|
[expandedPaths, onSelect, selectedPath, togglePath]
|
|
1708
109
|
);
|
|
1709
|
-
return /* @__PURE__ */
|
|
110
|
+
return /* @__PURE__ */ jsx(FileTreeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
1710
111
|
"div",
|
|
1711
112
|
{
|
|
1712
113
|
className: cn(
|
|
@@ -1715,7 +116,7 @@ var FileTree = ({
|
|
|
1715
116
|
),
|
|
1716
117
|
role: "tree",
|
|
1717
118
|
...props,
|
|
1718
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ jsx("div", { className: "p-2", children })
|
|
1719
120
|
}
|
|
1720
121
|
) });
|
|
1721
122
|
};
|
|
@@ -1723,12 +124,12 @@ var FileTreeIcon = ({
|
|
|
1723
124
|
className,
|
|
1724
125
|
children,
|
|
1725
126
|
...props
|
|
1726
|
-
}) => /* @__PURE__ */
|
|
127
|
+
}) => /* @__PURE__ */ jsx("span", { className: cn("shrink-0", className), ...props, children });
|
|
1727
128
|
var FileTreeName = ({
|
|
1728
129
|
className,
|
|
1729
130
|
children,
|
|
1730
131
|
...props
|
|
1731
|
-
}) => /* @__PURE__ */
|
|
132
|
+
}) => /* @__PURE__ */ jsx("span", { className: cn("truncate", className), ...props, children });
|
|
1732
133
|
var FileTreeFolderContext = createContext({
|
|
1733
134
|
isExpanded: false,
|
|
1734
135
|
name: "",
|
|
@@ -1743,7 +144,7 @@ var FileTreeActions = ({
|
|
|
1743
144
|
className,
|
|
1744
145
|
children,
|
|
1745
146
|
...props
|
|
1746
|
-
}) => /* @__PURE__ */
|
|
147
|
+
}) => /* @__PURE__ */ jsx(
|
|
1747
148
|
"div",
|
|
1748
149
|
{
|
|
1749
150
|
className: cn("ml-auto flex items-center gap-1", className),
|
|
@@ -1755,7 +156,7 @@ var FileTreeActions = ({
|
|
|
1755
156
|
);
|
|
1756
157
|
|
|
1757
158
|
// src/react/components/workspace/FileTree.tsx
|
|
1758
|
-
import { Fragment as Fragment2, jsx as
|
|
159
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1759
160
|
var ROOT_PATH = ".";
|
|
1760
161
|
var TREE_ROW_CLASS = "group flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50";
|
|
1761
162
|
var FILE_ICON_GROUPS = {
|
|
@@ -1983,16 +384,16 @@ function FileTree2({
|
|
|
1983
384
|
setSelectedPath(path);
|
|
1984
385
|
};
|
|
1985
386
|
if (!sessionId) {
|
|
1986
|
-
return /* @__PURE__ */
|
|
387
|
+
return /* @__PURE__ */ jsx2(EmptyState, { title: "\u6682\u65E0\u6D3B\u8DC3\u4F1A\u8BDD", description: "\u9009\u62E9\u6216\u521B\u5EFA\u4F1A\u8BDD\u540E\u67E5\u770B\u5DE5\u4F5C\u533A\u6587\u4EF6" });
|
|
1987
388
|
}
|
|
1988
389
|
if (rootLoading) {
|
|
1989
390
|
return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-3 py-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
|
|
1990
|
-
/* @__PURE__ */
|
|
1991
|
-
/* @__PURE__ */
|
|
391
|
+
/* @__PURE__ */ jsx2(Loader2, { size: 14, className: "animate-spin" }),
|
|
392
|
+
/* @__PURE__ */ jsx2("span", { children: "\u52A0\u8F7D\u4E2D..." })
|
|
1992
393
|
] });
|
|
1993
394
|
}
|
|
1994
395
|
if (rootError) {
|
|
1995
|
-
return /* @__PURE__ */
|
|
396
|
+
return /* @__PURE__ */ jsx2(
|
|
1996
397
|
EmptyState,
|
|
1997
398
|
{
|
|
1998
399
|
title: "\u6587\u4EF6\u6811\u52A0\u8F7D\u5931\u8D25",
|
|
@@ -2001,9 +402,9 @@ function FileTree2({
|
|
|
2001
402
|
);
|
|
2002
403
|
}
|
|
2003
404
|
if (!rootEntries || rootEntries.length === 0) {
|
|
2004
|
-
return /* @__PURE__ */
|
|
405
|
+
return /* @__PURE__ */ jsx2(EmptyState, { title: "\u5F53\u524D\u5DE5\u4F5C\u533A\u4E3A\u7A7A", description: "\u4F1A\u8BDD\u4EA7\u751F\u6587\u4EF6\u540E\u4F1A\u663E\u793A\u5728\u8FD9\u91CC" });
|
|
2005
406
|
}
|
|
2006
|
-
return /* @__PURE__ */
|
|
407
|
+
return /* @__PURE__ */ jsx2(
|
|
2007
408
|
FileTree,
|
|
2008
409
|
{
|
|
2009
410
|
expanded: expandedDirs,
|
|
@@ -2012,7 +413,7 @@ function FileTree2({
|
|
|
2012
413
|
onSelect: handleSelect,
|
|
2013
414
|
className: "border-none bg-transparent",
|
|
2014
415
|
children: rootEntries.map(
|
|
2015
|
-
(entry) => entry.is_dir ? /* @__PURE__ */
|
|
416
|
+
(entry) => entry.is_dir ? /* @__PURE__ */ jsx2(
|
|
2016
417
|
FolderNode,
|
|
2017
418
|
{
|
|
2018
419
|
entry,
|
|
@@ -2030,7 +431,7 @@ function FileTree2({
|
|
|
2030
431
|
allowDelete
|
|
2031
432
|
},
|
|
2032
433
|
entry.path
|
|
2033
|
-
) : /* @__PURE__ */
|
|
434
|
+
) : /* @__PURE__ */ jsx2(
|
|
2034
435
|
FileNode,
|
|
2035
436
|
{
|
|
2036
437
|
entry,
|
|
@@ -2132,15 +533,15 @@ function FolderNode({
|
|
|
2132
533
|
setActiveAction(null);
|
|
2133
534
|
}
|
|
2134
535
|
};
|
|
2135
|
-
return /* @__PURE__ */
|
|
536
|
+
return /* @__PURE__ */ jsx2(Collapsible, { open: isExpanded, onOpenChange: () => onToggleDir(entry.path), children: /* @__PURE__ */ jsxs2("div", { children: [
|
|
2136
537
|
/* @__PURE__ */ jsxs2("div", { className: cn(TREE_ROW_CLASS, isSelected && "bg-muted"), children: [
|
|
2137
|
-
/* @__PURE__ */
|
|
538
|
+
/* @__PURE__ */ jsx2(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
|
|
2138
539
|
"button",
|
|
2139
540
|
{
|
|
2140
541
|
type: "button",
|
|
2141
542
|
className: "flex shrink-0 cursor-pointer items-center rounded border-none bg-transparent p-0",
|
|
2142
543
|
title: isExpanded ? "\u6536\u8D77\u76EE\u5F55" : "\u5C55\u5F00\u76EE\u5F55",
|
|
2143
|
-
children: /* @__PURE__ */
|
|
544
|
+
children: /* @__PURE__ */ jsx2(
|
|
2144
545
|
ChevronRight,
|
|
2145
546
|
{
|
|
2146
547
|
className: cn(
|
|
@@ -2152,8 +553,8 @@ function FolderNode({
|
|
|
2152
553
|
}
|
|
2153
554
|
) }),
|
|
2154
555
|
isRenaming ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
2155
|
-
/* @__PURE__ */
|
|
2156
|
-
/* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ jsx2(FileTreeIcon, { children: getFolderIcon() }),
|
|
557
|
+
/* @__PURE__ */ jsx2(
|
|
2157
558
|
"input",
|
|
2158
559
|
{
|
|
2159
560
|
ref: renameInputRef,
|
|
@@ -2185,13 +586,13 @@ function FolderNode({
|
|
|
2185
586
|
onSelectPath(entry.path);
|
|
2186
587
|
},
|
|
2187
588
|
children: [
|
|
2188
|
-
/* @__PURE__ */
|
|
2189
|
-
/* @__PURE__ */
|
|
589
|
+
/* @__PURE__ */ jsx2(FileTreeIcon, { children: getFolderIcon() }),
|
|
590
|
+
/* @__PURE__ */ jsx2(FileTreeName, { className: "min-w-0 flex-1", children: entry.name })
|
|
2190
591
|
]
|
|
2191
592
|
}
|
|
2192
593
|
),
|
|
2193
594
|
!readOnly ? /* @__PURE__ */ jsxs2(FileTreeActions, { className: "ml-auto hidden shrink-0 group-hover:flex", children: [
|
|
2194
|
-
/* @__PURE__ */
|
|
595
|
+
/* @__PURE__ */ jsx2(
|
|
2195
596
|
TreeActionButton,
|
|
2196
597
|
{
|
|
2197
598
|
onClick: () => {
|
|
@@ -2201,10 +602,10 @@ function FolderNode({
|
|
|
2201
602
|
anchor.click();
|
|
2202
603
|
},
|
|
2203
604
|
title: "\u4E0B\u8F7D\u76EE\u5F55",
|
|
2204
|
-
children: /* @__PURE__ */
|
|
605
|
+
children: /* @__PURE__ */ jsx2(Download, { size: 12 })
|
|
2205
606
|
}
|
|
2206
607
|
),
|
|
2207
|
-
/* @__PURE__ */
|
|
608
|
+
/* @__PURE__ */ jsx2(
|
|
2208
609
|
TreeActionButton,
|
|
2209
610
|
{
|
|
2210
611
|
disabled: activeAction !== null,
|
|
@@ -2214,19 +615,19 @@ function FolderNode({
|
|
|
2214
615
|
setIsRenaming(true);
|
|
2215
616
|
},
|
|
2216
617
|
title: "\u91CD\u547D\u540D",
|
|
2217
|
-
children: activeAction === "rename" ? /* @__PURE__ */
|
|
618
|
+
children: activeAction === "rename" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Pencil, { size: 12 })
|
|
2218
619
|
}
|
|
2219
620
|
),
|
|
2220
|
-
/* @__PURE__ */
|
|
621
|
+
/* @__PURE__ */ jsx2(
|
|
2221
622
|
TreeActionButton,
|
|
2222
623
|
{
|
|
2223
624
|
disabled: activeAction !== null,
|
|
2224
625
|
onClick: () => void handleCopy(),
|
|
2225
626
|
title: "\u590D\u5236\u76EE\u5F55",
|
|
2226
|
-
children: activeAction === "copy" ? /* @__PURE__ */
|
|
627
|
+
children: activeAction === "copy" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Copy, { size: 12 })
|
|
2227
628
|
}
|
|
2228
629
|
),
|
|
2229
|
-
/* @__PURE__ */
|
|
630
|
+
/* @__PURE__ */ jsx2(
|
|
2230
631
|
TreeActionButton,
|
|
2231
632
|
{
|
|
2232
633
|
disabled: activeAction !== null || !onShareFile,
|
|
@@ -2243,29 +644,29 @@ function FolderNode({
|
|
|
2243
644
|
}
|
|
2244
645
|
},
|
|
2245
646
|
title: "\u5171\u4EAB\u5230 .share",
|
|
2246
|
-
children: activeAction === "share" ? /* @__PURE__ */
|
|
647
|
+
children: activeAction === "share" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Link2, { size: 12 })
|
|
2247
648
|
}
|
|
2248
649
|
),
|
|
2249
|
-
/* @__PURE__ */
|
|
650
|
+
/* @__PURE__ */ jsx2(
|
|
2250
651
|
TreeActionButton,
|
|
2251
652
|
{
|
|
2252
653
|
disabled: activeAction !== null,
|
|
2253
654
|
onClick: () => void handleDelete(),
|
|
2254
655
|
title: "\u5220\u9664\u76EE\u5F55",
|
|
2255
|
-
children: activeAction === "delete" ? /* @__PURE__ */
|
|
656
|
+
children: activeAction === "delete" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Trash2, { size: 12 })
|
|
2256
657
|
}
|
|
2257
658
|
)
|
|
2258
|
-
] }) : allowDelete ? /* @__PURE__ */
|
|
659
|
+
] }) : allowDelete ? /* @__PURE__ */ jsx2(FileTreeActions, { className: "ml-auto hidden shrink-0 group-hover:flex", children: /* @__PURE__ */ jsx2(
|
|
2259
660
|
TreeActionButton,
|
|
2260
661
|
{
|
|
2261
662
|
disabled: activeAction !== null,
|
|
2262
663
|
onClick: () => void handleDelete(),
|
|
2263
664
|
title: "\u5220\u9664\u76EE\u5F55",
|
|
2264
|
-
children: activeAction === "delete" ? /* @__PURE__ */
|
|
665
|
+
children: activeAction === "delete" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Trash2, { size: 12 })
|
|
2265
666
|
}
|
|
2266
667
|
) }) : null
|
|
2267
668
|
] }),
|
|
2268
|
-
/* @__PURE__ */
|
|
669
|
+
/* @__PURE__ */ jsx2(CollapsibleContent, { children: /* @__PURE__ */ jsx2("div", { className: "ml-4 border-l pl-2", children: /* @__PURE__ */ jsx2(
|
|
2269
670
|
LazyDirChildren,
|
|
2270
671
|
{
|
|
2271
672
|
dirPath: entry.path,
|
|
@@ -2369,9 +770,9 @@ function FileNode({
|
|
|
2369
770
|
}
|
|
2370
771
|
};
|
|
2371
772
|
return /* @__PURE__ */ jsxs2("div", { className: cn(TREE_ROW_CLASS, isSelected && "bg-muted"), children: [
|
|
2372
|
-
/* @__PURE__ */
|
|
2373
|
-
/* @__PURE__ */
|
|
2374
|
-
isRenaming ? /* @__PURE__ */
|
|
773
|
+
/* @__PURE__ */ jsx2("span", { className: "size-4 shrink-0" }),
|
|
774
|
+
/* @__PURE__ */ jsx2(FileTreeIcon, { children: getFileIcon(entry.name) }),
|
|
775
|
+
isRenaming ? /* @__PURE__ */ jsx2(
|
|
2375
776
|
"input",
|
|
2376
777
|
{
|
|
2377
778
|
ref: renameInputRef,
|
|
@@ -2402,13 +803,13 @@ function FileNode({
|
|
|
2402
803
|
void onOpenFile(entry.path, entry.name);
|
|
2403
804
|
},
|
|
2404
805
|
children: [
|
|
2405
|
-
/* @__PURE__ */
|
|
2406
|
-
/* @__PURE__ */
|
|
806
|
+
/* @__PURE__ */ jsx2(FileTreeName, { className: "min-w-0 flex-1", children: entry.name }),
|
|
807
|
+
/* @__PURE__ */ jsx2(FileTagList, { tags: entry.tags })
|
|
2407
808
|
]
|
|
2408
809
|
}
|
|
2409
810
|
),
|
|
2410
|
-
isOpening ? /* @__PURE__ */
|
|
2411
|
-
/* @__PURE__ */
|
|
811
|
+
isOpening ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "ml-auto shrink-0 animate-spin text-[hsl(var(--muted-foreground))]" }) : !readOnly ? /* @__PURE__ */ jsxs2(FileTreeActions, { className: "ml-auto hidden shrink-0 group-hover:flex", children: [
|
|
812
|
+
/* @__PURE__ */ jsx2(
|
|
2412
813
|
TreeActionButton,
|
|
2413
814
|
{
|
|
2414
815
|
onClick: () => {
|
|
@@ -2418,10 +819,10 @@ function FileNode({
|
|
|
2418
819
|
anchor.click();
|
|
2419
820
|
},
|
|
2420
821
|
title: "\u4E0B\u8F7D\u6587\u4EF6",
|
|
2421
|
-
children: /* @__PURE__ */
|
|
822
|
+
children: /* @__PURE__ */ jsx2(Download, { size: 12 })
|
|
2422
823
|
}
|
|
2423
824
|
),
|
|
2424
|
-
/* @__PURE__ */
|
|
825
|
+
/* @__PURE__ */ jsx2(
|
|
2425
826
|
TreeActionButton,
|
|
2426
827
|
{
|
|
2427
828
|
disabled: activeAction !== null,
|
|
@@ -2431,19 +832,19 @@ function FileNode({
|
|
|
2431
832
|
setIsRenaming(true);
|
|
2432
833
|
},
|
|
2433
834
|
title: "\u91CD\u547D\u540D",
|
|
2434
|
-
children: activeAction === "rename" ? /* @__PURE__ */
|
|
835
|
+
children: activeAction === "rename" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Pencil, { size: 12 })
|
|
2435
836
|
}
|
|
2436
837
|
),
|
|
2437
|
-
/* @__PURE__ */
|
|
838
|
+
/* @__PURE__ */ jsx2(
|
|
2438
839
|
TreeActionButton,
|
|
2439
840
|
{
|
|
2440
841
|
disabled: activeAction !== null,
|
|
2441
842
|
onClick: () => void handleCopy(),
|
|
2442
843
|
title: "\u590D\u5236\u6587\u4EF6",
|
|
2443
|
-
children: activeAction === "copy" ? /* @__PURE__ */
|
|
844
|
+
children: activeAction === "copy" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Copy, { size: 12 })
|
|
2444
845
|
}
|
|
2445
846
|
),
|
|
2446
|
-
/* @__PURE__ */
|
|
847
|
+
/* @__PURE__ */ jsx2(
|
|
2447
848
|
TreeActionButton,
|
|
2448
849
|
{
|
|
2449
850
|
disabled: activeAction !== null || !onShareFile,
|
|
@@ -2460,25 +861,25 @@ function FileNode({
|
|
|
2460
861
|
}
|
|
2461
862
|
},
|
|
2462
863
|
title: "\u5171\u4EAB\u5230 .share",
|
|
2463
|
-
children: activeAction === "share" ? /* @__PURE__ */
|
|
864
|
+
children: activeAction === "share" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Link2, { size: 12 })
|
|
2464
865
|
}
|
|
2465
866
|
),
|
|
2466
|
-
/* @__PURE__ */
|
|
867
|
+
/* @__PURE__ */ jsx2(
|
|
2467
868
|
TreeActionButton,
|
|
2468
869
|
{
|
|
2469
870
|
disabled: activeAction !== null,
|
|
2470
871
|
onClick: () => void handleDelete(),
|
|
2471
872
|
title: "\u5220\u9664\u6587\u4EF6",
|
|
2472
|
-
children: activeAction === "delete" ? /* @__PURE__ */
|
|
873
|
+
children: activeAction === "delete" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Trash2, { size: 12 })
|
|
2473
874
|
}
|
|
2474
875
|
)
|
|
2475
|
-
] }) : allowDelete ? /* @__PURE__ */
|
|
876
|
+
] }) : allowDelete ? /* @__PURE__ */ jsx2(FileTreeActions, { className: "ml-auto hidden shrink-0 group-hover:flex", children: /* @__PURE__ */ jsx2(
|
|
2476
877
|
TreeActionButton,
|
|
2477
878
|
{
|
|
2478
879
|
disabled: activeAction !== null,
|
|
2479
880
|
onClick: () => void handleDelete(),
|
|
2480
881
|
title: "\u5220\u9664\u6587\u4EF6",
|
|
2481
|
-
children: activeAction === "delete" ? /* @__PURE__ */
|
|
882
|
+
children: activeAction === "delete" ? /* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Trash2, { size: 12 })
|
|
2482
883
|
}
|
|
2483
884
|
) }) : null
|
|
2484
885
|
] });
|
|
@@ -2509,18 +910,18 @@ function LazyDirChildren({
|
|
|
2509
910
|
});
|
|
2510
911
|
if (isLoading) {
|
|
2511
912
|
return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
2512
|
-
/* @__PURE__ */
|
|
2513
|
-
/* @__PURE__ */
|
|
913
|
+
/* @__PURE__ */ jsx2(Loader2, { size: 12, className: "animate-spin" }),
|
|
914
|
+
/* @__PURE__ */ jsx2("span", { children: "\u52A0\u8F7D\u4E2D..." })
|
|
2514
915
|
] });
|
|
2515
916
|
}
|
|
2516
917
|
if (error) {
|
|
2517
|
-
return /* @__PURE__ */
|
|
918
|
+
return /* @__PURE__ */ jsx2("div", { className: "px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]", children: error instanceof Error ? error.message : "\u52A0\u8F7D\u5931\u8D25" });
|
|
2518
919
|
}
|
|
2519
920
|
if (!entries || entries.length === 0) {
|
|
2520
|
-
return /* @__PURE__ */
|
|
921
|
+
return /* @__PURE__ */ jsx2("div", { className: "px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u7A7A\u76EE\u5F55" });
|
|
2521
922
|
}
|
|
2522
|
-
return /* @__PURE__ */
|
|
2523
|
-
(child) => child.is_dir ? /* @__PURE__ */
|
|
923
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: entries.map(
|
|
924
|
+
(child) => child.is_dir ? /* @__PURE__ */ jsx2(
|
|
2524
925
|
FolderNode,
|
|
2525
926
|
{
|
|
2526
927
|
entry: child,
|
|
@@ -2538,7 +939,7 @@ function LazyDirChildren({
|
|
|
2538
939
|
allowDelete
|
|
2539
940
|
},
|
|
2540
941
|
child.path
|
|
2541
|
-
) : /* @__PURE__ */
|
|
942
|
+
) : /* @__PURE__ */ jsx2(
|
|
2542
943
|
FileNode,
|
|
2543
944
|
{
|
|
2544
945
|
entry: child,
|
|
@@ -2558,7 +959,7 @@ function LazyDirChildren({
|
|
|
2558
959
|
}
|
|
2559
960
|
function FileTagList({ tags }) {
|
|
2560
961
|
if (!tags || tags.length === 0) return null;
|
|
2561
|
-
return /* @__PURE__ */
|
|
962
|
+
return /* @__PURE__ */ jsx2("span", { className: "ml-2 flex min-w-0 shrink-0 items-center gap-1", children: tags.map((tag) => /* @__PURE__ */ jsx2(
|
|
2562
963
|
"span",
|
|
2563
964
|
{
|
|
2564
965
|
className: "max-w-20 truncate rounded border border-[hsl(var(--border))] bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] leading-none text-[hsl(var(--muted-foreground))]",
|
|
@@ -2574,7 +975,7 @@ function TreeActionButton({
|
|
|
2574
975
|
onClick,
|
|
2575
976
|
title
|
|
2576
977
|
}) {
|
|
2577
|
-
return /* @__PURE__ */
|
|
978
|
+
return /* @__PURE__ */ jsx2(
|
|
2578
979
|
"button",
|
|
2579
980
|
{
|
|
2580
981
|
type: "button",
|
|
@@ -2588,10 +989,10 @@ function TreeActionButton({
|
|
|
2588
989
|
}
|
|
2589
990
|
function EmptyState({ title, description }) {
|
|
2590
991
|
return /* @__PURE__ */ jsxs2("div", { className: "flex h-full min-h-[12rem] flex-col items-center justify-center gap-3 px-6 text-center", children: [
|
|
2591
|
-
/* @__PURE__ */
|
|
992
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-10 w-10 items-center justify-center text-[hsl(var(--muted-foreground))]", children: getFolderIcon() }),
|
|
2592
993
|
/* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
|
|
2593
|
-
/* @__PURE__ */
|
|
2594
|
-
/* @__PURE__ */
|
|
994
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-[hsl(var(--foreground))]", children: title }),
|
|
995
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm text-[hsl(var(--muted-foreground))]", children: description })
|
|
2595
996
|
] })
|
|
2596
997
|
] });
|
|
2597
998
|
}
|
|
@@ -2599,13 +1000,13 @@ function getFileIcon(fileName) {
|
|
|
2599
1000
|
const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
|
|
2600
1001
|
const group = FILE_ICON_GROUPS[ext];
|
|
2601
1002
|
const src = group ? `/file-icons/${group}/${ext}.png` : "/file-icons/generic-file.png";
|
|
2602
|
-
return /* @__PURE__ */
|
|
1003
|
+
return /* @__PURE__ */ jsx2(FileTypeIcon, { src, alt: `${ext || "file"} \u6587\u4EF6` });
|
|
2603
1004
|
}
|
|
2604
1005
|
function getFolderIcon() {
|
|
2605
|
-
return /* @__PURE__ */
|
|
1006
|
+
return /* @__PURE__ */ jsx2(FileTypeIcon, { src: "/file-icons/folder.png", alt: "\u6587\u4EF6\u5939" });
|
|
2606
1007
|
}
|
|
2607
1008
|
function FileTypeIcon({ src, alt }) {
|
|
2608
|
-
return /* @__PURE__ */
|
|
1009
|
+
return /* @__PURE__ */ jsx2(
|
|
2609
1010
|
"img",
|
|
2610
1011
|
{
|
|
2611
1012
|
src,
|
|
@@ -2624,7 +1025,7 @@ function getErrorMessage(error, fallback) {
|
|
|
2624
1025
|
}
|
|
2625
1026
|
|
|
2626
1027
|
// src/react/components/workspace/WorkspaceFilesPanel.tsx
|
|
2627
|
-
import { jsx as
|
|
1028
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2628
1029
|
var EMPTY_MESSAGES = [];
|
|
2629
1030
|
function WorkspaceFilesPanel({
|
|
2630
1031
|
sessionId,
|
|
@@ -2659,7 +1060,7 @@ function WorkspaceFilesPanel({
|
|
|
2659
1060
|
return [];
|
|
2660
1061
|
}
|
|
2661
1062
|
const ui = block.content;
|
|
2662
|
-
const
|
|
1063
|
+
const type = ui.resourceHTML ? "resource-html" : "resource-uri";
|
|
2663
1064
|
const content = ui.resourceHTML ?? ui.resourceUri ?? ui.resourceURI ?? "";
|
|
2664
1065
|
if (!content) return [];
|
|
2665
1066
|
const title2 = ui.title ?? (typeof block.display_name === "string" && block.display_name.trim().length > 0 ? block.display_name : "\u53EF\u89C6\u5316\u4EA7\u7269");
|
|
@@ -2669,7 +1070,7 @@ function WorkspaceFilesPanel({
|
|
|
2669
1070
|
key,
|
|
2670
1071
|
title: title2,
|
|
2671
1072
|
target: {
|
|
2672
|
-
type
|
|
1073
|
+
type,
|
|
2673
1074
|
content,
|
|
2674
1075
|
title: title2,
|
|
2675
1076
|
key,
|
|
@@ -2746,7 +1147,7 @@ function WorkspaceFilesPanel({
|
|
|
2746
1147
|
folderInputRef.current?.click();
|
|
2747
1148
|
};
|
|
2748
1149
|
return /* @__PURE__ */ jsxs3("div", { className: cn("flex flex-col", className), children: [
|
|
2749
|
-
topSlot ? /* @__PURE__ */
|
|
1150
|
+
topSlot ? /* @__PURE__ */ jsx3("div", { className: "mb-3", children: topSlot }) : null,
|
|
2750
1151
|
/* @__PURE__ */ jsxs3("div", { className: "mb-2 flex shrink-0 items-center justify-between gap-2", children: [
|
|
2751
1152
|
/* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-1 rounded-lg bg-[hsl(var(--muted))]/60 p-0.5 text-[11px] font-medium", children: [
|
|
2752
1153
|
/* @__PURE__ */ jsxs3(
|
|
@@ -2759,7 +1160,7 @@ function WorkspaceFilesPanel({
|
|
|
2759
1160
|
activeTab === "files" ? "bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
|
|
2760
1161
|
),
|
|
2761
1162
|
children: [
|
|
2762
|
-
/* @__PURE__ */
|
|
1163
|
+
/* @__PURE__ */ jsx3(FolderTree, { size: 12 }),
|
|
2763
1164
|
title
|
|
2764
1165
|
]
|
|
2765
1166
|
}
|
|
@@ -2774,14 +1175,14 @@ function WorkspaceFilesPanel({
|
|
|
2774
1175
|
activeTab === "resources" ? "bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
|
|
2775
1176
|
),
|
|
2776
1177
|
children: [
|
|
2777
|
-
/* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsx3(PanelRightOpen, { size: 12 }),
|
|
2778
1179
|
"\u53EF\u89C6\u5316\u4EA7\u7269",
|
|
2779
|
-
/* @__PURE__ */
|
|
1180
|
+
/* @__PURE__ */ jsx3("span", { className: "font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: inlineResources.length })
|
|
2780
1181
|
]
|
|
2781
1182
|
}
|
|
2782
1183
|
) : null
|
|
2783
1184
|
] }),
|
|
2784
|
-
onSync ? /* @__PURE__ */
|
|
1185
|
+
onSync ? /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx3(
|
|
2785
1186
|
"button",
|
|
2786
1187
|
{
|
|
2787
1188
|
type: "button",
|
|
@@ -2800,11 +1201,11 @@ function WorkspaceFilesPanel({
|
|
|
2800
1201
|
},
|
|
2801
1202
|
className: "rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50",
|
|
2802
1203
|
title: "\u540C\u6B65\u5171\u4EAB\u533A",
|
|
2803
|
-
children: isSyncing ? /* @__PURE__ */
|
|
1204
|
+
children: isSyncing ? /* @__PURE__ */ jsx3(Loader22, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx3(RefreshCw, { size: 12 })
|
|
2804
1205
|
}
|
|
2805
1206
|
) }) : null,
|
|
2806
1207
|
!readOnly && activeTab === "files" ? /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
|
|
2807
|
-
/* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsx3(
|
|
2808
1209
|
"button",
|
|
2809
1210
|
{
|
|
2810
1211
|
type: "button",
|
|
@@ -2812,10 +1213,10 @@ function WorkspaceFilesPanel({
|
|
|
2812
1213
|
onClick: handleUploadClick,
|
|
2813
1214
|
className: "rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50",
|
|
2814
1215
|
title: "\u4E0A\u4F20\u6587\u4EF6",
|
|
2815
|
-
children: isUploading ? /* @__PURE__ */
|
|
1216
|
+
children: isUploading ? /* @__PURE__ */ jsx3(Loader22, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx3(Upload, { size: 12 })
|
|
2816
1217
|
}
|
|
2817
1218
|
),
|
|
2818
|
-
/* @__PURE__ */
|
|
1219
|
+
/* @__PURE__ */ jsx3(
|
|
2819
1220
|
"button",
|
|
2820
1221
|
{
|
|
2821
1222
|
type: "button",
|
|
@@ -2823,12 +1224,12 @@ function WorkspaceFilesPanel({
|
|
|
2823
1224
|
onClick: handleFolderUploadClick,
|
|
2824
1225
|
className: "rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50",
|
|
2825
1226
|
title: "\u4E0A\u4F20\u6587\u4EF6\u5939",
|
|
2826
|
-
children: isUploading ? /* @__PURE__ */
|
|
1227
|
+
children: isUploading ? /* @__PURE__ */ jsx3(Loader22, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsx3(FolderUp, { size: 12 })
|
|
2827
1228
|
}
|
|
2828
1229
|
)
|
|
2829
1230
|
] }) : null
|
|
2830
1231
|
] }),
|
|
2831
|
-
activeTab === "resources" && inlineResources.length > 0 ? /* @__PURE__ */
|
|
1232
|
+
activeTab === "resources" && inlineResources.length > 0 ? /* @__PURE__ */ jsx3("div", { className: cn("min-h-0 overflow-auto", treeContainerClassName), children: /* @__PURE__ */ jsx3("div", { className: "flex flex-col gap-1.5", children: inlineResources.map((item, index) => /* @__PURE__ */ jsxs3(
|
|
2832
1233
|
"button",
|
|
2833
1234
|
{
|
|
2834
1235
|
type: "button",
|
|
@@ -2836,13 +1237,13 @@ function WorkspaceFilesPanel({
|
|
|
2836
1237
|
className: "group flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-[hsl(var(--muted))]",
|
|
2837
1238
|
title: item.title,
|
|
2838
1239
|
children: [
|
|
2839
|
-
/* @__PURE__ */
|
|
2840
|
-
/* @__PURE__ */
|
|
2841
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ jsx3(FileText, { size: 14, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
1241
|
+
/* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate text-[hsl(var(--foreground))]", children: item.title }),
|
|
1242
|
+
/* @__PURE__ */ jsx3("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: index + 1 })
|
|
2842
1243
|
]
|
|
2843
1244
|
},
|
|
2844
1245
|
item.key
|
|
2845
|
-
)) }) }) : /* @__PURE__ */
|
|
1246
|
+
)) }) }) : /* @__PURE__ */ jsx3("div", { className: treeContainerClassName, children: /* @__PURE__ */ jsx3(
|
|
2846
1247
|
FileTree2,
|
|
2847
1248
|
{
|
|
2848
1249
|
sessionId,
|
|
@@ -2856,7 +1257,7 @@ function WorkspaceFilesPanel({
|
|
|
2856
1257
|
},
|
|
2857
1258
|
sessionId ?? "workspace-empty"
|
|
2858
1259
|
) }),
|
|
2859
|
-
/* @__PURE__ */
|
|
1260
|
+
/* @__PURE__ */ jsx3(
|
|
2860
1261
|
"input",
|
|
2861
1262
|
{
|
|
2862
1263
|
ref: fileInputRef,
|
|
@@ -2866,7 +1267,7 @@ function WorkspaceFilesPanel({
|
|
|
2866
1267
|
onChange: handleUploadChange
|
|
2867
1268
|
}
|
|
2868
1269
|
),
|
|
2869
|
-
/* @__PURE__ */
|
|
1270
|
+
/* @__PURE__ */ jsx3(
|
|
2870
1271
|
"input",
|
|
2871
1272
|
{
|
|
2872
1273
|
ref: folderInputRef,
|