@gendive/chatllm 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -815
- package/dist/index.d.ts +2 -815
- package/dist/index.js +2 -2158
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -2043
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +309 -2
- package/dist/react/index.d.ts +309 -2
- package/dist/react/index.js +1284 -952
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1085 -763
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ArtifactCard: () => ArtifactCard,
|
|
34
|
+
ChatError: () => ChatError,
|
|
34
35
|
ChatFloatingWidget: () => ChatFloatingWidget,
|
|
35
36
|
ChatHeader: () => ChatHeader,
|
|
36
37
|
ChatInput: () => ChatInput,
|
|
@@ -43,6 +44,7 @@ __export(index_exports, {
|
|
|
43
44
|
ContentPartRenderer: () => ContentPartRenderer,
|
|
44
45
|
DEFAULT_PROJECT_ID: () => DEFAULT_PROJECT_ID,
|
|
45
46
|
DEFAULT_PROJECT_TITLE: () => DEFAULT_PROJECT_TITLE,
|
|
47
|
+
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
46
48
|
DeepResearchProgressUI: () => DeepResearchProgressUI,
|
|
47
49
|
DevDiveAvatar: () => DevDiveAvatar,
|
|
48
50
|
DevDiveFabCharacter: () => DevDiveFabCharacter,
|
|
@@ -66,28 +68,36 @@ __export(index_exports, {
|
|
|
66
68
|
ResizeHandles: () => ResizeHandles,
|
|
67
69
|
SettingsModal: () => SettingsModal,
|
|
68
70
|
SkillProgressUI: () => SkillProgressUI,
|
|
71
|
+
classifyFetchError: () => classifyFetchError,
|
|
69
72
|
convertSkillsToOpenAITools: () => convertSkillsToOpenAITools,
|
|
70
73
|
convertToolsToSkills: () => convertToolsToSkills,
|
|
71
74
|
createAdvancedResearchSkill: () => createAdvancedResearchSkill,
|
|
72
75
|
createConversationSearchSkill: () => createConversationSearchSkill,
|
|
73
76
|
createDeepResearchSkill: () => createDeepResearchSkill,
|
|
77
|
+
createTimeoutError: () => createTimeoutError,
|
|
74
78
|
migrateSessionsToProjects: () => migrateSessionsToProjects,
|
|
79
|
+
parseSSELine: () => parseSSELine,
|
|
80
|
+
parseSSEResponse: () => parseSSEResponse,
|
|
75
81
|
useChatUI: () => useChatUI,
|
|
82
|
+
useChecklist: () => useChecklist,
|
|
83
|
+
useContentParsers: () => useContentParsers,
|
|
76
84
|
useDeepResearch: () => useDeepResearch,
|
|
77
85
|
useDragResize: () => useDragResize,
|
|
78
86
|
useFloatingWidget: () => useFloatingWidget,
|
|
79
87
|
useImageError: () => useImageError,
|
|
80
88
|
useObserver: () => useObserver,
|
|
81
89
|
useProject: () => useProject,
|
|
82
|
-
useSkills: () => useSkills
|
|
90
|
+
useSkills: () => useSkills,
|
|
91
|
+
useStreamingFetch: () => useStreamingFetch,
|
|
92
|
+
withRetry: () => withRetry
|
|
83
93
|
});
|
|
84
94
|
module.exports = __toCommonJS(index_exports);
|
|
85
95
|
|
|
86
96
|
// src/react/ChatUI.tsx
|
|
87
|
-
var
|
|
97
|
+
var import_react26 = __toESM(require("react"));
|
|
88
98
|
|
|
89
99
|
// src/react/hooks/useChatUI.ts
|
|
90
|
-
var
|
|
100
|
+
var import_react8 = require("react");
|
|
91
101
|
|
|
92
102
|
// src/types.ts
|
|
93
103
|
var DEFAULT_PERSONALIZATION = {
|
|
@@ -2144,6 +2154,462 @@ var parseContextRefs = (content) => {
|
|
|
2144
2154
|
return { refs, cleanContent };
|
|
2145
2155
|
};
|
|
2146
2156
|
|
|
2157
|
+
// src/react/utils/errors.ts
|
|
2158
|
+
var ChatError = class extends Error {
|
|
2159
|
+
code;
|
|
2160
|
+
retryable;
|
|
2161
|
+
statusCode;
|
|
2162
|
+
originalError;
|
|
2163
|
+
constructor(code, message, options) {
|
|
2164
|
+
super(message);
|
|
2165
|
+
this.name = "ChatError";
|
|
2166
|
+
this.code = code;
|
|
2167
|
+
this.statusCode = options?.statusCode;
|
|
2168
|
+
this.originalError = options?.originalError;
|
|
2169
|
+
this.retryable = options?.retryable ?? isRetryableCode(code);
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
var classifyStatusCode = (status) => {
|
|
2173
|
+
if (status === 401 || status === 403) return "AUTH";
|
|
2174
|
+
if (status === 429) return "RATE_LIMIT";
|
|
2175
|
+
if (status >= 500) return "API";
|
|
2176
|
+
if (status >= 400) return "API";
|
|
2177
|
+
return "UNKNOWN";
|
|
2178
|
+
};
|
|
2179
|
+
var isRetryableCode = (code) => code === "NETWORK" || code === "RATE_LIMIT" || code === "API";
|
|
2180
|
+
var classifyFetchError = (error, response) => {
|
|
2181
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2182
|
+
return new ChatError("ABORT", "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", {
|
|
2183
|
+
originalError: error,
|
|
2184
|
+
retryable: false
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2188
|
+
return new ChatError("ABORT", "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", {
|
|
2189
|
+
originalError: error,
|
|
2190
|
+
retryable: false
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
if (error instanceof ChatError) return error;
|
|
2194
|
+
if (response && !response.ok) {
|
|
2195
|
+
const code = classifyStatusCode(response.status);
|
|
2196
|
+
const messages = {
|
|
2197
|
+
AUTH: "\uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. API \uD0A4\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.",
|
|
2198
|
+
RATE_LIMIT: "\uC694\uCCAD \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.",
|
|
2199
|
+
API: `\uC11C\uBC84 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. (${response.status})`,
|
|
2200
|
+
NETWORK: "\uB124\uD2B8\uC6CC\uD06C \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.",
|
|
2201
|
+
TIMEOUT: "\uC694\uCCAD \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
2202
|
+
ABORT: "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
2203
|
+
UNKNOWN: "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4."
|
|
2204
|
+
};
|
|
2205
|
+
return new ChatError(code, messages[code], {
|
|
2206
|
+
statusCode: response.status,
|
|
2207
|
+
originalError: error
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
if (error instanceof TypeError) {
|
|
2211
|
+
return new ChatError("NETWORK", "\uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694.", {
|
|
2212
|
+
originalError: error
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
const message = error instanceof Error ? error.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
2216
|
+
return new ChatError("UNKNOWN", message, { originalError: error });
|
|
2217
|
+
};
|
|
2218
|
+
var createTimeoutError = (timeoutMs) => new ChatError("TIMEOUT", `\uC2A4\uD2B8\uB9AC\uBC0D \uC751\uB2F5\uC774 ${timeoutMs / 1e3}\uCD08 \uB3D9\uC548 \uC5C6\uC2B5\uB2C8\uB2E4.`, {
|
|
2219
|
+
retryable: false
|
|
2220
|
+
});
|
|
2221
|
+
|
|
2222
|
+
// src/react/hooks/useStreamingFetch.ts
|
|
2223
|
+
var import_react6 = require("react");
|
|
2224
|
+
var DEFAULT_CHUNK_TIMEOUT = 3e4;
|
|
2225
|
+
var parseSSELine = (line) => {
|
|
2226
|
+
if (!line.trim()) return null;
|
|
2227
|
+
let data = line;
|
|
2228
|
+
if (line.startsWith("data: ")) {
|
|
2229
|
+
data = line.slice(6);
|
|
2230
|
+
if (data === "[DONE]") return null;
|
|
2231
|
+
}
|
|
2232
|
+
try {
|
|
2233
|
+
const parsed = JSON.parse(data);
|
|
2234
|
+
let usage = null;
|
|
2235
|
+
if (parsed.usage) {
|
|
2236
|
+
usage = {
|
|
2237
|
+
promptTokens: parsed.usage.prompt_tokens ?? parsed.usage.promptTokens ?? 0,
|
|
2238
|
+
completionTokens: parsed.usage.completion_tokens ?? parsed.usage.completionTokens ?? 0,
|
|
2239
|
+
totalTokens: parsed.usage.total_tokens ?? parsed.usage.totalTokens ?? 0
|
|
2240
|
+
};
|
|
2241
|
+
} else if (parsed.prompt_eval_count != null || parsed.eval_count != null) {
|
|
2242
|
+
usage = {
|
|
2243
|
+
promptTokens: parsed.prompt_eval_count ?? 0,
|
|
2244
|
+
completionTokens: parsed.eval_count ?? 0,
|
|
2245
|
+
totalTokens: (parsed.prompt_eval_count ?? 0) + (parsed.eval_count ?? 0)
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
const delta = parsed.choices?.[0]?.delta ?? null;
|
|
2249
|
+
const finishReason = parsed.choices?.[0]?.finish_reason ?? null;
|
|
2250
|
+
const content = delta?.content ?? parsed.message?.content ?? parsed.content ?? parsed.text ?? "";
|
|
2251
|
+
const thinking = parsed.message?.thinking ?? "";
|
|
2252
|
+
return { content, thinking, finishReason, delta, usage, raw: parsed };
|
|
2253
|
+
} catch {
|
|
2254
|
+
return null;
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
var parseSSEResponse = async (response) => {
|
|
2258
|
+
const reader = response.body?.getReader();
|
|
2259
|
+
if (!reader) return "";
|
|
2260
|
+
const decoder = new TextDecoder();
|
|
2261
|
+
let buffer = "";
|
|
2262
|
+
let result = "";
|
|
2263
|
+
try {
|
|
2264
|
+
while (true) {
|
|
2265
|
+
const { done, value } = await reader.read();
|
|
2266
|
+
if (done) break;
|
|
2267
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2268
|
+
const lines = buffer.split("\n");
|
|
2269
|
+
buffer = lines.pop() || "";
|
|
2270
|
+
for (const line of lines) {
|
|
2271
|
+
const chunk = parseSSELine(line);
|
|
2272
|
+
if (chunk?.content) result += chunk.content;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
if (buffer.trim()) {
|
|
2276
|
+
const chunk = parseSSELine(buffer);
|
|
2277
|
+
if (chunk?.content) result += chunk.content;
|
|
2278
|
+
}
|
|
2279
|
+
} finally {
|
|
2280
|
+
reader.releaseLock();
|
|
2281
|
+
}
|
|
2282
|
+
return result;
|
|
2283
|
+
};
|
|
2284
|
+
var useStreamingFetch = (options = {}) => {
|
|
2285
|
+
const { chunkTimeout = DEFAULT_CHUNK_TIMEOUT } = options;
|
|
2286
|
+
const abortControllersRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
2287
|
+
const createAbortController = (0, import_react6.useCallback)((sessionId) => {
|
|
2288
|
+
const controller = new AbortController();
|
|
2289
|
+
abortControllersRef.current.set(sessionId, controller);
|
|
2290
|
+
return controller;
|
|
2291
|
+
}, []);
|
|
2292
|
+
const abort = (0, import_react6.useCallback)((sessionId) => {
|
|
2293
|
+
abortControllersRef.current.get(sessionId)?.abort();
|
|
2294
|
+
}, []);
|
|
2295
|
+
const cleanup = (0, import_react6.useCallback)((sessionId) => {
|
|
2296
|
+
abortControllersRef.current.delete(sessionId);
|
|
2297
|
+
}, []);
|
|
2298
|
+
const getSignal = (0, import_react6.useCallback)((sessionId) => {
|
|
2299
|
+
return abortControllersRef.current.get(sessionId)?.signal;
|
|
2300
|
+
}, []);
|
|
2301
|
+
const readWithTimeout = (0, import_react6.useCallback)(async (reader) => {
|
|
2302
|
+
let timerId;
|
|
2303
|
+
try {
|
|
2304
|
+
return await Promise.race([
|
|
2305
|
+
reader.read(),
|
|
2306
|
+
new Promise((_, reject) => {
|
|
2307
|
+
timerId = setTimeout(() => reject(createTimeoutError(chunkTimeout)), chunkTimeout);
|
|
2308
|
+
})
|
|
2309
|
+
]);
|
|
2310
|
+
} finally {
|
|
2311
|
+
clearTimeout(timerId);
|
|
2312
|
+
}
|
|
2313
|
+
}, [chunkTimeout]);
|
|
2314
|
+
const streamResponse = (0, import_react6.useCallback)(async (response, onChunk, streamOptions) => {
|
|
2315
|
+
if (!response.ok) {
|
|
2316
|
+
throw classifyFetchError(new Error("API error"), response);
|
|
2317
|
+
}
|
|
2318
|
+
const reader = response.body?.getReader();
|
|
2319
|
+
if (!reader) throw new Error("No reader");
|
|
2320
|
+
const decoder = new TextDecoder();
|
|
2321
|
+
let buffer = "";
|
|
2322
|
+
let lastUsage = null;
|
|
2323
|
+
try {
|
|
2324
|
+
while (true) {
|
|
2325
|
+
const { done, value } = streamOptions?.noTimeout ? await reader.read() : await readWithTimeout(reader);
|
|
2326
|
+
if (done) break;
|
|
2327
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2328
|
+
const lines = buffer.split("\n");
|
|
2329
|
+
buffer = lines.pop() || "";
|
|
2330
|
+
let shouldBreak = false;
|
|
2331
|
+
for (const line of lines) {
|
|
2332
|
+
const chunk = parseSSELine(line);
|
|
2333
|
+
if (!chunk) continue;
|
|
2334
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
2335
|
+
const result = onChunk(chunk);
|
|
2336
|
+
if (result === "break") {
|
|
2337
|
+
shouldBreak = true;
|
|
2338
|
+
break;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
if (shouldBreak) break;
|
|
2342
|
+
}
|
|
2343
|
+
if (buffer.trim()) {
|
|
2344
|
+
const chunk = parseSSELine(buffer);
|
|
2345
|
+
if (chunk) {
|
|
2346
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
2347
|
+
onChunk(chunk);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
} finally {
|
|
2351
|
+
reader.releaseLock();
|
|
2352
|
+
}
|
|
2353
|
+
return { usage: lastUsage };
|
|
2354
|
+
}, [readWithTimeout]);
|
|
2355
|
+
const fetchAndStream = (0, import_react6.useCallback)(async (fetchOptions, onChunk) => {
|
|
2356
|
+
const response = await fetch(fetchOptions.url, {
|
|
2357
|
+
method: "POST",
|
|
2358
|
+
headers: { "Content-Type": "application/json" },
|
|
2359
|
+
body: JSON.stringify(fetchOptions.body),
|
|
2360
|
+
signal: fetchOptions.signal
|
|
2361
|
+
});
|
|
2362
|
+
fetchOptions.onHeaders?.(response);
|
|
2363
|
+
return streamResponse(response, onChunk);
|
|
2364
|
+
}, [streamResponse]);
|
|
2365
|
+
return {
|
|
2366
|
+
abortControllers: abortControllersRef,
|
|
2367
|
+
createAbortController,
|
|
2368
|
+
abort,
|
|
2369
|
+
cleanup,
|
|
2370
|
+
getSignal,
|
|
2371
|
+
readWithTimeout,
|
|
2372
|
+
streamResponse,
|
|
2373
|
+
fetchAndStream,
|
|
2374
|
+
parseSSELine
|
|
2375
|
+
};
|
|
2376
|
+
};
|
|
2377
|
+
|
|
2378
|
+
// src/react/hooks/useChecklist.ts
|
|
2379
|
+
var import_react7 = require("react");
|
|
2380
|
+
var CHECKLIST_STEP_DELAY_MS = 100;
|
|
2381
|
+
var toActiveItems = (items) => items.map((it) => ({
|
|
2382
|
+
id: it.id,
|
|
2383
|
+
title: it.title,
|
|
2384
|
+
skill: it.skill,
|
|
2385
|
+
fileIndex: it.fileIndex,
|
|
2386
|
+
fileType: it.fileType,
|
|
2387
|
+
refImage: it.refImage,
|
|
2388
|
+
refStep: it.refStep
|
|
2389
|
+
}));
|
|
2390
|
+
var useChecklist = ({
|
|
2391
|
+
sessionsRef,
|
|
2392
|
+
sessions,
|
|
2393
|
+
setSessions,
|
|
2394
|
+
sendMessage,
|
|
2395
|
+
abortControllers,
|
|
2396
|
+
removeLoadingSession,
|
|
2397
|
+
pendingAttachmentDataRef,
|
|
2398
|
+
trackChecklistSkip,
|
|
2399
|
+
resolveChecklistRefImage,
|
|
2400
|
+
buildChecklistStepPrompt
|
|
2401
|
+
}) => {
|
|
2402
|
+
const skipNextChecklistParsingRef = (0, import_react7.useRef)(false);
|
|
2403
|
+
const activeChecklistRef = (0, import_react7.useRef)(null);
|
|
2404
|
+
const pendingChecklistRef = (0, import_react7.useRef)(null);
|
|
2405
|
+
const sendMessageRef = (0, import_react7.useRef)(sendMessage);
|
|
2406
|
+
sendMessageRef.current = sendMessage;
|
|
2407
|
+
const findSessionAndMessage = (0, import_react7.useCallback)(
|
|
2408
|
+
(messageId) => {
|
|
2409
|
+
const session = sessionsRef.current?.find(
|
|
2410
|
+
(s) => s.messages.some((m) => m.id === messageId)
|
|
2411
|
+
);
|
|
2412
|
+
if (!session) return null;
|
|
2413
|
+
const message = session.messages.find((m) => m.id === messageId);
|
|
2414
|
+
if (!message?.checklistBlock) return null;
|
|
2415
|
+
return { session, message };
|
|
2416
|
+
},
|
|
2417
|
+
[sessionsRef]
|
|
2418
|
+
);
|
|
2419
|
+
const executeStep = (0, import_react7.useCallback)(
|
|
2420
|
+
(items, stepIndex, messageId, sessionId, isFirst) => {
|
|
2421
|
+
skipNextChecklistParsingRef.current = true;
|
|
2422
|
+
setTimeout(() => {
|
|
2423
|
+
const item = items[stepIndex];
|
|
2424
|
+
const refUrl = resolveChecklistRefImage(item, messageId, sessionId);
|
|
2425
|
+
if (refUrl) {
|
|
2426
|
+
pendingAttachmentDataRef.current = [
|
|
2427
|
+
{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }
|
|
2428
|
+
];
|
|
2429
|
+
}
|
|
2430
|
+
sendMessageRef.current(
|
|
2431
|
+
buildChecklistStepPrompt(stepIndex, items.length, item, isFirst, refUrl),
|
|
2432
|
+
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: stepIndex, title: item.title } }
|
|
2433
|
+
);
|
|
2434
|
+
}, CHECKLIST_STEP_DELAY_MS);
|
|
2435
|
+
},
|
|
2436
|
+
[resolveChecklistRefImage, buildChecklistStepPrompt, pendingAttachmentDataRef]
|
|
2437
|
+
);
|
|
2438
|
+
const updateChecklistItems = (0, import_react7.useCallback)(
|
|
2439
|
+
(sessionId, messageId, updater) => {
|
|
2440
|
+
setSessions(
|
|
2441
|
+
(prev) => prev.map((s) => {
|
|
2442
|
+
if (s.id !== sessionId) return s;
|
|
2443
|
+
return {
|
|
2444
|
+
...s,
|
|
2445
|
+
messages: s.messages.map((m) => {
|
|
2446
|
+
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
2447
|
+
const result = updater(m.checklistBlock.items, m.checklistBlock);
|
|
2448
|
+
return {
|
|
2449
|
+
...m,
|
|
2450
|
+
checklistBlock: {
|
|
2451
|
+
...m.checklistBlock,
|
|
2452
|
+
items: result.items,
|
|
2453
|
+
...result.currentStep !== void 0 ? { currentStep: result.currentStep } : {},
|
|
2454
|
+
...result.completed !== void 0 ? { completed: result.completed } : {}
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
})
|
|
2458
|
+
};
|
|
2459
|
+
})
|
|
2460
|
+
);
|
|
2461
|
+
},
|
|
2462
|
+
[setSessions]
|
|
2463
|
+
);
|
|
2464
|
+
const handleChecklistStart = (0, import_react7.useCallback)(
|
|
2465
|
+
(messageId) => {
|
|
2466
|
+
const found = findSessionAndMessage(messageId);
|
|
2467
|
+
if (!found) return;
|
|
2468
|
+
const { session, message } = found;
|
|
2469
|
+
pendingChecklistRef.current = null;
|
|
2470
|
+
activeChecklistRef.current = {
|
|
2471
|
+
messageId,
|
|
2472
|
+
sessionId: session.id,
|
|
2473
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2474
|
+
currentStep: 0,
|
|
2475
|
+
stepResults: []
|
|
2476
|
+
};
|
|
2477
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2478
|
+
items: items.map((it, idx) => ({
|
|
2479
|
+
...it,
|
|
2480
|
+
status: idx === 0 ? "in_progress" : it.status
|
|
2481
|
+
})),
|
|
2482
|
+
currentStep: 0
|
|
2483
|
+
}));
|
|
2484
|
+
executeStep(message.checklistBlock.items, 0, messageId, session.id, true);
|
|
2485
|
+
},
|
|
2486
|
+
[findSessionAndMessage, updateChecklistItems, executeStep]
|
|
2487
|
+
);
|
|
2488
|
+
const handleChecklistAbort = (0, import_react7.useCallback)(() => {
|
|
2489
|
+
if (!activeChecklistRef.current) return;
|
|
2490
|
+
const checklist = activeChecklistRef.current;
|
|
2491
|
+
const stepIdx = checklist.currentStep;
|
|
2492
|
+
abortControllers.current?.get(checklist.sessionId)?.abort();
|
|
2493
|
+
updateChecklistItems(checklist.sessionId, checklist.messageId, (items) => ({
|
|
2494
|
+
items: items.map((it, idx) => ({
|
|
2495
|
+
...it,
|
|
2496
|
+
status: idx === stepIdx ? "error" : it.status
|
|
2497
|
+
}))
|
|
2498
|
+
}));
|
|
2499
|
+
activeChecklistRef.current = null;
|
|
2500
|
+
removeLoadingSession(checklist.sessionId);
|
|
2501
|
+
}, [abortControllers, updateChecklistItems, removeLoadingSession]);
|
|
2502
|
+
const handleChecklistRetry = (0, import_react7.useCallback)(
|
|
2503
|
+
(messageId, stepIndex) => {
|
|
2504
|
+
const found = findSessionAndMessage(messageId);
|
|
2505
|
+
if (!found) return;
|
|
2506
|
+
const { session, message } = found;
|
|
2507
|
+
activeChecklistRef.current = {
|
|
2508
|
+
messageId,
|
|
2509
|
+
sessionId: session.id,
|
|
2510
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2511
|
+
currentStep: stepIndex,
|
|
2512
|
+
stepResults: message.checklistBlock.items.slice(0, stepIndex).map((it) => it.result || "")
|
|
2513
|
+
};
|
|
2514
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2515
|
+
items: items.map((it, idx) => ({
|
|
2516
|
+
...it,
|
|
2517
|
+
status: idx === stepIndex ? "in_progress" : idx > stepIndex ? "pending" : it.status
|
|
2518
|
+
})),
|
|
2519
|
+
currentStep: stepIndex
|
|
2520
|
+
}));
|
|
2521
|
+
executeStep(message.checklistBlock.items, stepIndex, messageId, session.id, stepIndex === 0);
|
|
2522
|
+
},
|
|
2523
|
+
[findSessionAndMessage, updateChecklistItems, executeStep]
|
|
2524
|
+
);
|
|
2525
|
+
const handleChecklistSkip = (0, import_react7.useCallback)(
|
|
2526
|
+
(messageId, stepIndex) => {
|
|
2527
|
+
const found = findSessionAndMessage(messageId);
|
|
2528
|
+
if (!found) return;
|
|
2529
|
+
const { session, message } = found;
|
|
2530
|
+
trackChecklistSkip();
|
|
2531
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2532
|
+
items: items.map((it, idx) => ({
|
|
2533
|
+
...it,
|
|
2534
|
+
status: idx === stepIndex ? "done" : it.status,
|
|
2535
|
+
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
2536
|
+
}))
|
|
2537
|
+
}));
|
|
2538
|
+
const nextPending = message.checklistBlock.items.findIndex(
|
|
2539
|
+
(it, idx) => idx > stepIndex && (it.status === "pending" || it.status === "error")
|
|
2540
|
+
);
|
|
2541
|
+
if (nextPending >= 0) {
|
|
2542
|
+
activeChecklistRef.current = {
|
|
2543
|
+
messageId,
|
|
2544
|
+
sessionId: session.id,
|
|
2545
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2546
|
+
currentStep: nextPending,
|
|
2547
|
+
stepResults: message.checklistBlock.items.slice(0, nextPending).map((it) => it.result || "(\uAC74\uB108\uB700)")
|
|
2548
|
+
};
|
|
2549
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2550
|
+
items: items.map((it, idx) => ({
|
|
2551
|
+
...it,
|
|
2552
|
+
status: idx === nextPending ? "in_progress" : it.status
|
|
2553
|
+
})),
|
|
2554
|
+
currentStep: nextPending
|
|
2555
|
+
}));
|
|
2556
|
+
executeStep(message.checklistBlock.items, nextPending, messageId, session.id, false);
|
|
2557
|
+
} else {
|
|
2558
|
+
const allResults = message.checklistBlock.items.map((it, i) => {
|
|
2559
|
+
const result = i === stepIndex ? "(\uAC74\uB108\uB700)" : it.result || "(\uAC74\uB108\uB700)";
|
|
2560
|
+
return `### ${i + 1}. ${it.title}
|
|
2561
|
+
${result}`;
|
|
2562
|
+
}).join("\n\n");
|
|
2563
|
+
updateChecklistItems(session.id, messageId, () => ({
|
|
2564
|
+
items: message.checklistBlock.items.map((it, idx) => ({
|
|
2565
|
+
...it,
|
|
2566
|
+
status: idx === stepIndex ? "done" : it.status,
|
|
2567
|
+
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
2568
|
+
})),
|
|
2569
|
+
completed: true
|
|
2570
|
+
}));
|
|
2571
|
+
skipNextChecklistParsingRef.current = true;
|
|
2572
|
+
setTimeout(() => {
|
|
2573
|
+
sendMessageRef.current(
|
|
2574
|
+
`\uBAA8\uB4E0 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uB2E8\uACC4\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC544\uB798\uB294 \uAC01 \uB2E8\uACC4\uBCC4 \uACB0\uACFC\uC785\uB2C8\uB2E4:
|
|
2575
|
+
|
|
2576
|
+
${allResults}
|
|
2577
|
+
|
|
2578
|
+
\uC704 \uACB0\uACFC\uB97C \uC885\uD569\uD558\uC5EC \uCD5C\uC885 \uACB0\uACFC\uBB3C\uC744 \uC644\uC131\uD574\uC8FC\uC138\uC694. checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`,
|
|
2579
|
+
{ hiddenUserMessage: true, isChecklistExecution: true }
|
|
2580
|
+
);
|
|
2581
|
+
}, CHECKLIST_STEP_DELAY_MS);
|
|
2582
|
+
}
|
|
2583
|
+
},
|
|
2584
|
+
[findSessionAndMessage, updateChecklistItems, executeStep, trackChecklistSkip]
|
|
2585
|
+
);
|
|
2586
|
+
const activeChecklistMessage = (0, import_react7.useMemo)(() => {
|
|
2587
|
+
const active = activeChecklistRef.current;
|
|
2588
|
+
if (!active) {
|
|
2589
|
+
for (const session2 of sessions) {
|
|
2590
|
+
const msg = session2.messages.find(
|
|
2591
|
+
(m) => m.checklistBlock && !m.checklistBlock.completed
|
|
2592
|
+
);
|
|
2593
|
+
if (msg) return msg;
|
|
2594
|
+
}
|
|
2595
|
+
return null;
|
|
2596
|
+
}
|
|
2597
|
+
const session = sessions.find((s) => s.id === active.sessionId);
|
|
2598
|
+
if (!session) return null;
|
|
2599
|
+
return session.messages.find((m) => m.id === active.messageId) || null;
|
|
2600
|
+
}, [sessions]);
|
|
2601
|
+
return {
|
|
2602
|
+
activeChecklistRef,
|
|
2603
|
+
pendingChecklistRef,
|
|
2604
|
+
skipNextChecklistParsingRef,
|
|
2605
|
+
handleChecklistStart,
|
|
2606
|
+
handleChecklistAbort,
|
|
2607
|
+
handleChecklistRetry,
|
|
2608
|
+
handleChecklistSkip,
|
|
2609
|
+
activeChecklistMessage
|
|
2610
|
+
};
|
|
2611
|
+
};
|
|
2612
|
+
|
|
2147
2613
|
// src/react/utils/sessionCache.ts
|
|
2148
2614
|
var buildCacheKey = (storageKey, sessionId) => `${storageKey}_cache_${sessionId}`;
|
|
2149
2615
|
var writeSessionCache = (storageKey, session) => {
|
|
@@ -2176,38 +2642,14 @@ var removeSessionCache = (storageKey, sessionId) => {
|
|
|
2176
2642
|
};
|
|
2177
2643
|
|
|
2178
2644
|
// src/react/hooks/useChatUI.ts
|
|
2179
|
-
var parseSSEResponse = async (response) => {
|
|
2180
|
-
const reader = response.body?.getReader();
|
|
2181
|
-
if (!reader) return "";
|
|
2182
|
-
const decoder = new TextDecoder();
|
|
2183
|
-
let buffer = "";
|
|
2184
|
-
let result = "";
|
|
2185
|
-
while (true) {
|
|
2186
|
-
const { done, value } = await reader.read();
|
|
2187
|
-
if (done) break;
|
|
2188
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2189
|
-
const lines = buffer.split("\n");
|
|
2190
|
-
buffer = lines.pop() || "";
|
|
2191
|
-
for (const line of lines) {
|
|
2192
|
-
if (line.startsWith("data: ")) {
|
|
2193
|
-
const data = line.slice(6);
|
|
2194
|
-
if (data === "[DONE]") continue;
|
|
2195
|
-
try {
|
|
2196
|
-
const parsed = JSON.parse(data);
|
|
2197
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
2198
|
-
if (chunk) result += chunk;
|
|
2199
|
-
} catch {
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
return result;
|
|
2205
|
-
};
|
|
2206
2645
|
var DEFAULT_STORAGE_KEY2 = "chatllm_sessions";
|
|
2207
2646
|
var DEFAULT_COMPRESSION_THRESHOLD = 20;
|
|
2208
2647
|
var DEFAULT_KEEP_RECENT = 6;
|
|
2209
2648
|
var DEFAULT_RECOMPRESSION_THRESHOLD = 10;
|
|
2210
2649
|
var DEFAULT_TOKEN_LIMIT = 8e3;
|
|
2650
|
+
var DEFAULT_STREAM_CHUNK_TIMEOUT = 3e4;
|
|
2651
|
+
var DEFAULT_MAX_TOOL_CALL_DEPTH = 5;
|
|
2652
|
+
var DEFAULT_MAX_TOOL_RESULT_SIZE = 1e4;
|
|
2211
2653
|
var DEFAULT_SESSION_CONTEXT_MAX_CHARS = 4e3;
|
|
2212
2654
|
var DEFAULT_SESSION_CONTEXT_MAX_ITEMS = 10;
|
|
2213
2655
|
var SESSION_CONTEXT_ITEM_MAX_CHARS = 500;
|
|
@@ -2269,15 +2711,22 @@ var convertAttachmentsToBase64 = async (attachments) => Promise.all(
|
|
|
2269
2711
|
size: att.size
|
|
2270
2712
|
}))
|
|
2271
2713
|
);
|
|
2272
|
-
var
|
|
2273
|
-
attachments.map(async (att) =>
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2714
|
+
var convertAttachmentsWithUploaderCached = async (attachments, uploader, cache) => Promise.all(
|
|
2715
|
+
attachments.map(async (att) => {
|
|
2716
|
+
let url = cache.get(att.id);
|
|
2717
|
+
if (!url) {
|
|
2718
|
+
url = await uploader(att.file);
|
|
2719
|
+
cache.set(att.id, url);
|
|
2720
|
+
}
|
|
2721
|
+
return {
|
|
2722
|
+
name: att.name,
|
|
2723
|
+
mimeType: att.mimeType,
|
|
2724
|
+
base64: "",
|
|
2725
|
+
url,
|
|
2726
|
+
size: att.size,
|
|
2727
|
+
source: "uploader"
|
|
2728
|
+
};
|
|
2729
|
+
})
|
|
2281
2730
|
);
|
|
2282
2731
|
var findPreviousResultImage = (messages) => {
|
|
2283
2732
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -2327,6 +2776,12 @@ var useChatUI = (options) => {
|
|
|
2327
2776
|
onSendMessage,
|
|
2328
2777
|
onSessionChange,
|
|
2329
2778
|
onError,
|
|
2779
|
+
streamChunkTimeout = DEFAULT_STREAM_CHUNK_TIMEOUT,
|
|
2780
|
+
retry: retryConfig,
|
|
2781
|
+
onAbort,
|
|
2782
|
+
onTokenUsage,
|
|
2783
|
+
maxToolCallDepth = DEFAULT_MAX_TOOL_CALL_DEPTH,
|
|
2784
|
+
maxToolResultSize = DEFAULT_MAX_TOOL_RESULT_SIZE,
|
|
2330
2785
|
onTitleChange,
|
|
2331
2786
|
generateTitle: generateTitleCallback,
|
|
2332
2787
|
// Memory options
|
|
@@ -2388,74 +2843,78 @@ var useChatUI = (options) => {
|
|
|
2388
2843
|
onChecklistStepModel
|
|
2389
2844
|
} = options;
|
|
2390
2845
|
const enableAutoExtraction = enableAutoExtractionProp ?? !useExternalStorage;
|
|
2391
|
-
const [sessions, setSessions] = (0,
|
|
2392
|
-
const [currentSessionId, setCurrentSessionId] = (0,
|
|
2393
|
-
const [input, setInput] = (0,
|
|
2394
|
-
const [loadingSessionIds, setLoadingSessionIds] = (0,
|
|
2846
|
+
const [sessions, setSessions] = (0, import_react8.useState)([]);
|
|
2847
|
+
const [currentSessionId, setCurrentSessionId] = (0, import_react8.useState)(null);
|
|
2848
|
+
const [input, setInput] = (0, import_react8.useState)("");
|
|
2849
|
+
const [loadingSessionIds, setLoadingSessionIds] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
|
|
2395
2850
|
const isLoading = currentSessionId !== null && loadingSessionIds.has(currentSessionId);
|
|
2396
|
-
const addLoadingSession = (0,
|
|
2397
|
-
const removeLoadingSession = (0,
|
|
2851
|
+
const addLoadingSession = (0, import_react8.useCallback)((id) => setLoadingSessionIds((prev) => new Set(prev).add(id)), []);
|
|
2852
|
+
const removeLoadingSession = (0, import_react8.useCallback)((id) => setLoadingSessionIds((prev) => {
|
|
2398
2853
|
const next = new Set(prev);
|
|
2399
2854
|
next.delete(id);
|
|
2400
2855
|
return next;
|
|
2401
2856
|
}), []);
|
|
2402
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
2403
|
-
const [sidebarOpen, setSidebarOpen] = (0,
|
|
2404
|
-
const [settingsOpen, setSettingsOpen] = (0,
|
|
2405
|
-
const [quotedText, setQuotedText] = (0,
|
|
2406
|
-
const [selectedAction, setSelectedAction] = (0,
|
|
2407
|
-
const [copiedMessageId, setCopiedMessageId] = (0,
|
|
2408
|
-
const [editingMessageId, setEditingMessageId] = (0,
|
|
2409
|
-
const [personalization, setPersonalization] = (0,
|
|
2857
|
+
const [selectedModel, setSelectedModel] = (0, import_react8.useState)(initialModel || models[0]?.id || "");
|
|
2858
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react8.useState)(true);
|
|
2859
|
+
const [settingsOpen, setSettingsOpen] = (0, import_react8.useState)(false);
|
|
2860
|
+
const [quotedText, setQuotedText] = (0, import_react8.useState)(null);
|
|
2861
|
+
const [selectedAction, setSelectedAction] = (0, import_react8.useState)(null);
|
|
2862
|
+
const [copiedMessageId, setCopiedMessageId] = (0, import_react8.useState)(null);
|
|
2863
|
+
const [editingMessageId, setEditingMessageId] = (0, import_react8.useState)(null);
|
|
2864
|
+
const [personalization, setPersonalization] = (0, import_react8.useState)({
|
|
2410
2865
|
...DEFAULT_PERSONALIZATION,
|
|
2411
2866
|
...initialPersonalization
|
|
2412
2867
|
});
|
|
2413
|
-
const [activeAlternatives, setActiveAlternatives] = (0,
|
|
2414
|
-
const [loadingAlternativeFor, setLoadingAlternativeFor] = (0,
|
|
2415
|
-
const [isSessionsLoading, setIsSessionsLoading] = (0,
|
|
2416
|
-
const [isSessionLoading, setIsSessionLoading] = (0,
|
|
2417
|
-
const [isDeepResearchMode, setIsDeepResearchMode] = (0,
|
|
2418
|
-
const [attachments, setAttachments] = (0,
|
|
2419
|
-
const [isModelsLoading, setIsModelsLoading] = (0,
|
|
2420
|
-
const [loadedModels, setLoadedModels] = (0,
|
|
2421
|
-
const [deepResearchProgress, setDeepResearchProgress] = (0,
|
|
2868
|
+
const [activeAlternatives, setActiveAlternatives] = (0, import_react8.useState)({});
|
|
2869
|
+
const [loadingAlternativeFor, setLoadingAlternativeFor] = (0, import_react8.useState)(null);
|
|
2870
|
+
const [isSessionsLoading, setIsSessionsLoading] = (0, import_react8.useState)(false);
|
|
2871
|
+
const [isSessionLoading, setIsSessionLoading] = (0, import_react8.useState)(false);
|
|
2872
|
+
const [isDeepResearchMode, setIsDeepResearchMode] = (0, import_react8.useState)(false);
|
|
2873
|
+
const [attachments, setAttachments] = (0, import_react8.useState)([]);
|
|
2874
|
+
const [isModelsLoading, setIsModelsLoading] = (0, import_react8.useState)(false);
|
|
2875
|
+
const [loadedModels, setLoadedModels] = (0, import_react8.useState)(null);
|
|
2876
|
+
const [deepResearchProgress, setDeepResearchProgress] = (0, import_react8.useState)(
|
|
2422
2877
|
null
|
|
2423
2878
|
);
|
|
2424
|
-
const sessionsRef = (0,
|
|
2425
|
-
(0,
|
|
2879
|
+
const sessionsRef = (0, import_react8.useRef)(sessions);
|
|
2880
|
+
(0, import_react8.useEffect)(() => {
|
|
2426
2881
|
sessionsRef.current = sessions;
|
|
2427
2882
|
}, [sessions]);
|
|
2428
|
-
const modelsRef = (0,
|
|
2429
|
-
(0,
|
|
2883
|
+
const modelsRef = (0, import_react8.useRef)(models);
|
|
2884
|
+
(0, import_react8.useEffect)(() => {
|
|
2430
2885
|
modelsRef.current = models;
|
|
2431
2886
|
}, [models]);
|
|
2432
|
-
const onSendMessageRef = (0,
|
|
2433
|
-
const onResponseHeadersRef = (0,
|
|
2434
|
-
const onSessionChangeRef = (0,
|
|
2435
|
-
const onErrorRef = (0,
|
|
2436
|
-
const
|
|
2437
|
-
const
|
|
2438
|
-
const
|
|
2439
|
-
const
|
|
2440
|
-
const
|
|
2441
|
-
const
|
|
2442
|
-
const
|
|
2443
|
-
const
|
|
2444
|
-
const
|
|
2445
|
-
const
|
|
2446
|
-
const
|
|
2447
|
-
const
|
|
2448
|
-
const
|
|
2449
|
-
const
|
|
2450
|
-
const
|
|
2451
|
-
const
|
|
2452
|
-
const
|
|
2453
|
-
const
|
|
2454
|
-
(0,
|
|
2887
|
+
const onSendMessageRef = (0, import_react8.useRef)(onSendMessage);
|
|
2888
|
+
const onResponseHeadersRef = (0, import_react8.useRef)(options.onResponseHeaders);
|
|
2889
|
+
const onSessionChangeRef = (0, import_react8.useRef)(onSessionChange);
|
|
2890
|
+
const onErrorRef = (0, import_react8.useRef)(onError);
|
|
2891
|
+
const onAbortRef = (0, import_react8.useRef)(onAbort);
|
|
2892
|
+
const onTokenUsageRef = (0, import_react8.useRef)(onTokenUsage);
|
|
2893
|
+
const onTitleChangeRef = (0, import_react8.useRef)(onTitleChange);
|
|
2894
|
+
const generateTitleRef = (0, import_react8.useRef)(generateTitleCallback);
|
|
2895
|
+
const onPersonalizationChangeRef = (0, import_react8.useRef)(options.onPersonalizationChange);
|
|
2896
|
+
const onPersonalizationSaveRef = (0, import_react8.useRef)(options.onPersonalizationSave);
|
|
2897
|
+
const onLoadSessionsRef = (0, import_react8.useRef)(onLoadSessions);
|
|
2898
|
+
const onCreateSessionRef = (0, import_react8.useRef)(onCreateSession);
|
|
2899
|
+
const onLoadSessionRef = (0, import_react8.useRef)(onLoadSession);
|
|
2900
|
+
const onDeleteSessionCallbackRef = (0, import_react8.useRef)(onDeleteSessionCallback);
|
|
2901
|
+
const onUpdateSessionTitleRef = (0, import_react8.useRef)(onUpdateSessionTitle);
|
|
2902
|
+
const onSaveMessagesRef = (0, import_react8.useRef)(onSaveMessages);
|
|
2903
|
+
const onToolCallRef = (0, import_react8.useRef)(onToolCall);
|
|
2904
|
+
const onSkillCompleteRef = (0, import_react8.useRef)(onSkillComplete);
|
|
2905
|
+
const onSessionContextChangeRef = (0, import_react8.useRef)(onSessionContextChange);
|
|
2906
|
+
const onLoadModelsRef = (0, import_react8.useRef)(onLoadModels);
|
|
2907
|
+
const onUploadImageRef = (0, import_react8.useRef)(onUploadImage);
|
|
2908
|
+
const onImageErrorRef = (0, import_react8.useRef)(onImageError);
|
|
2909
|
+
const fileUploaderRef = (0, import_react8.useRef)(fileUploader);
|
|
2910
|
+
const globalMemoryRef = (0, import_react8.useRef)(null);
|
|
2911
|
+
(0, import_react8.useEffect)(() => {
|
|
2455
2912
|
onSendMessageRef.current = onSendMessage;
|
|
2456
2913
|
onResponseHeadersRef.current = options.onResponseHeaders;
|
|
2457
2914
|
onSessionChangeRef.current = onSessionChange;
|
|
2458
2915
|
onErrorRef.current = onError;
|
|
2916
|
+
onAbortRef.current = onAbort;
|
|
2917
|
+
onTokenUsageRef.current = onTokenUsage;
|
|
2459
2918
|
onTitleChangeRef.current = onTitleChange;
|
|
2460
2919
|
generateTitleRef.current = generateTitleCallback;
|
|
2461
2920
|
onPersonalizationChangeRef.current = options.onPersonalizationChange;
|
|
@@ -2474,15 +2933,19 @@ var useChatUI = (options) => {
|
|
|
2474
2933
|
fileUploaderRef.current = fileUploader;
|
|
2475
2934
|
onLoadModelsRef.current = onLoadModels;
|
|
2476
2935
|
});
|
|
2477
|
-
const abortControllersRef = (0,
|
|
2478
|
-
const
|
|
2479
|
-
const
|
|
2480
|
-
const
|
|
2481
|
-
const
|
|
2482
|
-
const
|
|
2483
|
-
const
|
|
2484
|
-
|
|
2485
|
-
|
|
2936
|
+
const abortControllersRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
2937
|
+
const { streamResponse } = useStreamingFetch({ chunkTimeout: streamChunkTimeout });
|
|
2938
|
+
const pendingInitialLoadRef = (0, import_react8.useRef)(null);
|
|
2939
|
+
const skipNextPollParsingRef = (0, import_react8.useRef)(false);
|
|
2940
|
+
const toolCallDepthRef = (0, import_react8.useRef)(0);
|
|
2941
|
+
const skipNextSkillParsingRef = (0, import_react8.useRef)(false);
|
|
2942
|
+
const sendMessageForChecklistRef = (0, import_react8.useRef)(
|
|
2943
|
+
async () => {
|
|
2944
|
+
console.warn("[ChatUI] sendMessageForChecklistRef called before sendMessage initialized");
|
|
2945
|
+
}
|
|
2946
|
+
);
|
|
2947
|
+
const pendingAttachmentDataRef = (0, import_react8.useRef)(null);
|
|
2948
|
+
const lastExtractionMsgCountRef = (0, import_react8.useRef)(0);
|
|
2486
2949
|
const resolveChecklistRefImage = (item, checklistMessageId, sessionId) => {
|
|
2487
2950
|
const session = sessionsRef.current.find((s) => s.id === sessionId);
|
|
2488
2951
|
if (!session) return null;
|
|
@@ -2549,7 +3012,7 @@ ${hints.join(" ")}` : "";
|
|
|
2549
3012
|
return `${stepLabel}
|
|
2550
3013
|
${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
2551
3014
|
};
|
|
2552
|
-
const memoryOptions = (0,
|
|
3015
|
+
const memoryOptions = (0, import_react8.useMemo)(
|
|
2553
3016
|
() => ({
|
|
2554
3017
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2555
3018
|
storageKey: globalMemoryConfig?.localStorage?.key || `${storageKey}_memory`,
|
|
@@ -2563,11 +3026,11 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2563
3026
|
const globalMemoryRaw = useGlobalMemory(memoryOptions);
|
|
2564
3027
|
const globalMemory = useGlobalMemoryEnabled ? globalMemoryRaw : null;
|
|
2565
3028
|
globalMemoryRef.current = globalMemory;
|
|
2566
|
-
const stableToolCall = (0,
|
|
3029
|
+
const stableToolCall = (0, import_react8.useCallback)(
|
|
2567
3030
|
(name, params) => onToolCallRef.current(name, params),
|
|
2568
3031
|
[]
|
|
2569
3032
|
);
|
|
2570
|
-
const mergedSkills = (0,
|
|
3033
|
+
const mergedSkills = (0, import_react8.useMemo)(() => {
|
|
2571
3034
|
if (!tools || !onToolCall) return skills || {};
|
|
2572
3035
|
const toolSkills = convertToolsToSkills(tools, stableToolCall);
|
|
2573
3036
|
return { ...skills || {}, ...toolSkills };
|
|
@@ -2603,9 +3066,9 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2603
3066
|
onDeleteProjectFile,
|
|
2604
3067
|
onError
|
|
2605
3068
|
});
|
|
2606
|
-
const [projectSettingsOpen, setProjectSettingsOpen] = (0,
|
|
3069
|
+
const [projectSettingsOpen, setProjectSettingsOpen] = (0, import_react8.useState)(false);
|
|
2607
3070
|
const projectMemoryKey = enableProjects && projectHook.currentProjectId ? `${storageKey}_project_memory_${projectHook.currentProjectId}` : `${storageKey}_project_memory_none`;
|
|
2608
|
-
const projectMemoryOptions = (0,
|
|
3071
|
+
const projectMemoryOptions = (0, import_react8.useMemo)(
|
|
2609
3072
|
() => ({
|
|
2610
3073
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2611
3074
|
storageKey: projectMemoryKey,
|
|
@@ -2617,7 +3080,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2617
3080
|
);
|
|
2618
3081
|
const projectMemoryRaw = useGlobalMemory(projectMemoryOptions);
|
|
2619
3082
|
const projectMemory = enableProjects ? projectMemoryRaw : null;
|
|
2620
|
-
const unwrapResponseHeaders = (0,
|
|
3083
|
+
const unwrapResponseHeaders = (0, import_react8.useCallback)((result) => {
|
|
2621
3084
|
if (typeof result === "object" && result !== null && "response" in result && "headers" in result) {
|
|
2622
3085
|
const wrapped = result;
|
|
2623
3086
|
onResponseHeadersRef.current?.(wrapped.headers);
|
|
@@ -2625,7 +3088,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2625
3088
|
}
|
|
2626
3089
|
return result;
|
|
2627
3090
|
}, []);
|
|
2628
|
-
const emitFetchHeaders = (0,
|
|
3091
|
+
const emitFetchHeaders = (0, import_react8.useCallback)((response) => {
|
|
2629
3092
|
if (onResponseHeadersRef.current && response.headers) {
|
|
2630
3093
|
const headerMap = {};
|
|
2631
3094
|
response.headers.forEach((v, k) => {
|
|
@@ -2634,7 +3097,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2634
3097
|
onResponseHeadersRef.current(headerMap);
|
|
2635
3098
|
}
|
|
2636
3099
|
}, []);
|
|
2637
|
-
const callInternalLLM = (0,
|
|
3100
|
+
const callInternalLLM = (0, import_react8.useCallback)(async (prompt, model) => {
|
|
2638
3101
|
if (onSendMessageRef.current) {
|
|
2639
3102
|
const modelConfig = modelsRef.current.find((m) => m.id === model);
|
|
2640
3103
|
const provider = modelConfig?.provider || "ollama";
|
|
@@ -2672,11 +3135,11 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2672
3135
|
const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
|
|
2673
3136
|
const messages = currentSession?.messages.filter((m) => !m.hidden) || [];
|
|
2674
3137
|
const compressionState = currentSession?.compressionState || null;
|
|
2675
|
-
const visibleSessions = (0,
|
|
3138
|
+
const visibleSessions = (0, import_react8.useMemo)(() => {
|
|
2676
3139
|
if (!enableProjects || !projectHook.currentProjectId) return sessions;
|
|
2677
3140
|
return sessions.filter((s) => s.projectId === projectHook.currentProjectId);
|
|
2678
3141
|
}, [sessions, enableProjects, projectHook.currentProjectId]);
|
|
2679
|
-
(0,
|
|
3142
|
+
(0, import_react8.useEffect)(() => {
|
|
2680
3143
|
if (typeof window === "undefined") return;
|
|
2681
3144
|
if (useExternalStorage && onLoadSessionsRef.current) {
|
|
2682
3145
|
setIsSessionsLoading(true);
|
|
@@ -2738,7 +3201,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2738
3201
|
}
|
|
2739
3202
|
}
|
|
2740
3203
|
}, [storageKey, useExternalStorage, initialModel, startWithNewSession]);
|
|
2741
|
-
(0,
|
|
3204
|
+
(0, import_react8.useEffect)(() => {
|
|
2742
3205
|
if (!onLoadModelsRef.current) return;
|
|
2743
3206
|
setIsModelsLoading(true);
|
|
2744
3207
|
onLoadModelsRef.current().then((modelList) => {
|
|
@@ -2753,21 +3216,21 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2753
3216
|
});
|
|
2754
3217
|
}, []);
|
|
2755
3218
|
const effectiveModels = loadedModels || models;
|
|
2756
|
-
(0,
|
|
3219
|
+
(0, import_react8.useEffect)(() => {
|
|
2757
3220
|
if (typeof window === "undefined") return;
|
|
2758
3221
|
if (useExternalStorage) return;
|
|
2759
3222
|
if (sessions.length > 0) {
|
|
2760
3223
|
localStorage.setItem(storageKey, JSON.stringify(sessions));
|
|
2761
3224
|
}
|
|
2762
3225
|
}, [sessions, storageKey, useExternalStorage]);
|
|
2763
|
-
(0,
|
|
3226
|
+
(0, import_react8.useEffect)(() => {
|
|
2764
3227
|
if (typeof window === "undefined") return;
|
|
2765
3228
|
localStorage.setItem(`${storageKey}_personalization`, JSON.stringify(personalization));
|
|
2766
3229
|
}, [personalization, storageKey]);
|
|
2767
|
-
(0,
|
|
3230
|
+
(0, import_react8.useEffect)(() => {
|
|
2768
3231
|
onSessionChangeRef.current?.(currentSession);
|
|
2769
3232
|
}, [currentSession]);
|
|
2770
|
-
const buildSystemPrompt = (0,
|
|
3233
|
+
const buildSystemPrompt = (0, import_react8.useCallback)((session) => {
|
|
2771
3234
|
const parts = [];
|
|
2772
3235
|
const { userProfile, responseStyle, language } = personalization;
|
|
2773
3236
|
const identityName = assistantName || "AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8";
|
|
@@ -3126,7 +3589,7 @@ AI (\uD655\uC815):
|
|
|
3126
3589
|
}
|
|
3127
3590
|
return parts.length > 0 ? parts.join("\n") : "";
|
|
3128
3591
|
}, [personalization, globalMemory, useGlobalMemoryEnabled, enablePoll, enableChecklist, buildSkillsPrompt, enableProjects, projectHook.currentProject, projectMemory, resolvedSkills, assistantName, onBuildSystemPrompt, compactSystemPrompt, selectedModel]);
|
|
3129
|
-
const promoteToSessionContext = (0,
|
|
3592
|
+
const promoteToSessionContext = (0, import_react8.useCallback)((sessionId, skillName, content, metadata, label) => {
|
|
3130
3593
|
const item = createSessionContextItem(skillName, content, metadata, label);
|
|
3131
3594
|
setSessions(
|
|
3132
3595
|
(prev) => prev.map((s) => {
|
|
@@ -3137,7 +3600,7 @@ AI (\uD655\uC815):
|
|
|
3137
3600
|
})
|
|
3138
3601
|
);
|
|
3139
3602
|
}, []);
|
|
3140
|
-
const compressContext = (0,
|
|
3603
|
+
const compressContext = (0, import_react8.useCallback)(async (messagesToCompress, model) => {
|
|
3141
3604
|
const conversationText = messagesToCompress.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3142
3605
|
const summaryPrompt = `\uB2E4\uC74C \uB300\uD654 \uB0B4\uC6A9\uC744 \uD575\uC2EC \uC815\uBCF4\uB9CC \uC720\uC9C0\uD558\uBA74\uC11C \uAC04\uACB0\uD558\uAC8C \uC694\uC57D\uD574\uC8FC\uC138\uC694.
|
|
3143
3606
|
\uC911\uC694\uD55C \uACB0\uC815\uC0AC\uD56D, \uC0AC\uC6A9\uC790 \uC694\uAD6C\uC0AC\uD56D, \uB9E5\uB77D \uC815\uBCF4\uB97C \uBCF4\uC874\uD558\uC138\uC694.
|
|
@@ -3152,7 +3615,7 @@ ${conversationText}
|
|
|
3152
3615
|
return "";
|
|
3153
3616
|
}
|
|
3154
3617
|
}, [callInternalLLM]);
|
|
3155
|
-
const incrementalCompressContext = (0,
|
|
3618
|
+
const incrementalCompressContext = (0, import_react8.useCallback)(
|
|
3156
3619
|
async (existingSummary, newMessages, model) => {
|
|
3157
3620
|
const newConversation = newMessages.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3158
3621
|
const mergePrompt = `\uAE30\uC874 \uB300\uD654 \uC694\uC57D\uACFC \uC0C8\uB85C\uC6B4 \uB300\uD654 \uB0B4\uC6A9\uC744 \uD1B5\uD569\uD558\uC5EC \uD558\uB098\uC758 \uC694\uC57D\uC73C\uB85C \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694.
|
|
@@ -3179,10 +3642,10 @@ ${newConversation}
|
|
|
3179
3642
|
},
|
|
3180
3643
|
[callInternalLLM]
|
|
3181
3644
|
);
|
|
3182
|
-
const estimateTokens = (0,
|
|
3645
|
+
const estimateTokens = (0, import_react8.useCallback)((messages2) => {
|
|
3183
3646
|
return messages2.reduce((sum, m) => sum + Math.ceil(m.content.length / 4), 0);
|
|
3184
3647
|
}, []);
|
|
3185
|
-
const newSession = (0,
|
|
3648
|
+
const newSession = (0, import_react8.useCallback)(async () => {
|
|
3186
3649
|
const projectId = enableProjects ? projectHook.currentProjectId || DEFAULT_PROJECT_ID : void 0;
|
|
3187
3650
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
3188
3651
|
setIsSessionLoading(true);
|
|
@@ -3220,7 +3683,7 @@ ${newConversation}
|
|
|
3220
3683
|
setSessions((prev) => [newSess, ...prev]);
|
|
3221
3684
|
setCurrentSessionId(newSess.id);
|
|
3222
3685
|
}, [selectedModel, useExternalStorage, enableProjects, projectHook.currentProjectId]);
|
|
3223
|
-
const selectSession = (0,
|
|
3686
|
+
const selectSession = (0, import_react8.useCallback)(async (id) => {
|
|
3224
3687
|
if (useExternalStorage && onLoadSessionRef.current) {
|
|
3225
3688
|
setIsSessionLoading(true);
|
|
3226
3689
|
try {
|
|
@@ -3324,13 +3787,13 @@ ${newConversation}
|
|
|
3324
3787
|
setSelectedModel(session.model);
|
|
3325
3788
|
}
|
|
3326
3789
|
}, [sessions, useExternalStorage, storageKey, initialModel]);
|
|
3327
|
-
(0,
|
|
3790
|
+
(0, import_react8.useEffect)(() => {
|
|
3328
3791
|
if (!pendingInitialLoadRef.current || !useExternalStorage) return;
|
|
3329
3792
|
const id = pendingInitialLoadRef.current;
|
|
3330
3793
|
pendingInitialLoadRef.current = null;
|
|
3331
3794
|
selectSession(id);
|
|
3332
3795
|
}, [currentSessionId, selectSession, useExternalStorage]);
|
|
3333
|
-
const deleteSession = (0,
|
|
3796
|
+
const deleteSession = (0, import_react8.useCallback)(async (id) => {
|
|
3334
3797
|
if (useExternalStorage && onDeleteSessionCallbackRef.current) {
|
|
3335
3798
|
try {
|
|
3336
3799
|
await onDeleteSessionCallbackRef.current(id);
|
|
@@ -3358,7 +3821,7 @@ ${newConversation}
|
|
|
3358
3821
|
return filtered;
|
|
3359
3822
|
});
|
|
3360
3823
|
}, [currentSessionId, storageKey, useExternalStorage]);
|
|
3361
|
-
const renameSession = (0,
|
|
3824
|
+
const renameSession = (0, import_react8.useCallback)(async (id, newTitle) => {
|
|
3362
3825
|
if (!newTitle.trim()) return;
|
|
3363
3826
|
if (useExternalStorage && onUpdateSessionTitleRef.current) {
|
|
3364
3827
|
try {
|
|
@@ -3381,7 +3844,7 @@ ${newConversation}
|
|
|
3381
3844
|
);
|
|
3382
3845
|
onTitleChangeRef.current?.(id, newTitle.trim());
|
|
3383
3846
|
}, [useExternalStorage]);
|
|
3384
|
-
const setModel = (0,
|
|
3847
|
+
const setModel = (0, import_react8.useCallback)((model) => {
|
|
3385
3848
|
setSelectedModel(model);
|
|
3386
3849
|
if (currentSessionId) {
|
|
3387
3850
|
setSessions(
|
|
@@ -3389,10 +3852,10 @@ ${newConversation}
|
|
|
3389
3852
|
);
|
|
3390
3853
|
}
|
|
3391
3854
|
}, [currentSessionId]);
|
|
3392
|
-
const toggleSidebar = (0,
|
|
3393
|
-
const openSettings = (0,
|
|
3394
|
-
const closeSettings = (0,
|
|
3395
|
-
const copyMessage = (0,
|
|
3855
|
+
const toggleSidebar = (0, import_react8.useCallback)(() => setSidebarOpen((prev) => !prev), []);
|
|
3856
|
+
const openSettings = (0, import_react8.useCallback)(() => setSettingsOpen(true), []);
|
|
3857
|
+
const closeSettings = (0, import_react8.useCallback)(() => setSettingsOpen(false), []);
|
|
3858
|
+
const copyMessage = (0, import_react8.useCallback)((content, id) => {
|
|
3396
3859
|
if (typeof navigator !== "undefined") {
|
|
3397
3860
|
navigator.clipboard.writeText(content).then(() => {
|
|
3398
3861
|
setCopiedMessageId(id);
|
|
@@ -3400,30 +3863,30 @@ ${newConversation}
|
|
|
3400
3863
|
});
|
|
3401
3864
|
}
|
|
3402
3865
|
}, []);
|
|
3403
|
-
const startEdit = (0,
|
|
3866
|
+
const startEdit = (0, import_react8.useCallback)((message) => {
|
|
3404
3867
|
if (message.role === "user") {
|
|
3405
3868
|
setEditingMessageId(message.id);
|
|
3406
3869
|
}
|
|
3407
3870
|
}, []);
|
|
3408
|
-
const cancelEdit = (0,
|
|
3871
|
+
const cancelEdit = (0, import_react8.useCallback)(() => {
|
|
3409
3872
|
setEditingMessageId(null);
|
|
3410
3873
|
}, []);
|
|
3411
|
-
const stopGeneration = (0,
|
|
3874
|
+
const stopGeneration = (0, import_react8.useCallback)(() => {
|
|
3412
3875
|
if (currentSessionId) {
|
|
3413
3876
|
abortControllersRef.current.get(currentSessionId)?.abort();
|
|
3414
3877
|
}
|
|
3415
3878
|
}, [currentSessionId]);
|
|
3416
|
-
const updatePersonalization = (0,
|
|
3879
|
+
const updatePersonalization = (0, import_react8.useCallback)((config) => {
|
|
3417
3880
|
setPersonalization((prev) => {
|
|
3418
3881
|
const next = { ...prev, ...config };
|
|
3419
3882
|
onPersonalizationChangeRef.current?.(next);
|
|
3420
3883
|
return next;
|
|
3421
3884
|
});
|
|
3422
3885
|
}, []);
|
|
3423
|
-
const savePersonalization = (0,
|
|
3886
|
+
const savePersonalization = (0, import_react8.useCallback)(() => {
|
|
3424
3887
|
onPersonalizationSaveRef.current?.(personalization);
|
|
3425
3888
|
}, [personalization]);
|
|
3426
|
-
const addAttachments = (0,
|
|
3889
|
+
const addAttachments = (0, import_react8.useCallback)((files) => {
|
|
3427
3890
|
const newAttachments = files.map((file) => {
|
|
3428
3891
|
const isImage = file.type.startsWith("image/");
|
|
3429
3892
|
return {
|
|
@@ -3438,7 +3901,7 @@ ${newConversation}
|
|
|
3438
3901
|
});
|
|
3439
3902
|
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
3440
3903
|
}, []);
|
|
3441
|
-
const removeAttachment = (0,
|
|
3904
|
+
const removeAttachment = (0, import_react8.useCallback)((id) => {
|
|
3442
3905
|
setAttachments((prev) => {
|
|
3443
3906
|
const target = prev.find((a) => a.id === id);
|
|
3444
3907
|
if (target?.previewUrl) {
|
|
@@ -3447,17 +3910,17 @@ ${newConversation}
|
|
|
3447
3910
|
return prev.filter((a) => a.id !== id);
|
|
3448
3911
|
});
|
|
3449
3912
|
}, []);
|
|
3450
|
-
const toggleDeepResearchMode = (0,
|
|
3913
|
+
const toggleDeepResearchMode = (0, import_react8.useCallback)(() => {
|
|
3451
3914
|
setIsDeepResearchMode((prev) => !prev);
|
|
3452
3915
|
}, []);
|
|
3453
|
-
const trackBehavior = (0,
|
|
3916
|
+
const trackBehavior = (0, import_react8.useCallback)(async (key, updater) => {
|
|
3454
3917
|
if (!globalMemory) return;
|
|
3455
3918
|
const fullKey = `behavior.${key}`;
|
|
3456
3919
|
const prev = globalMemory.get(fullKey) ?? {};
|
|
3457
3920
|
const next = updater(prev);
|
|
3458
3921
|
await globalMemory.set(fullKey, next, { category: "behavior" });
|
|
3459
3922
|
}, [globalMemory]);
|
|
3460
|
-
const trackMessageLength = (0,
|
|
3923
|
+
const trackMessageLength = (0, import_react8.useCallback)((length) => {
|
|
3461
3924
|
trackBehavior("messageLength", (prev) => {
|
|
3462
3925
|
const samples = (prev.messageLengthSamples || []).slice(-19);
|
|
3463
3926
|
samples.push(length);
|
|
@@ -3465,39 +3928,69 @@ ${newConversation}
|
|
|
3465
3928
|
return { ...prev, messageLengthSamples: samples, avgMessageLength: avg };
|
|
3466
3929
|
});
|
|
3467
3930
|
}, [trackBehavior]);
|
|
3468
|
-
const trackSkillUsage = (0,
|
|
3931
|
+
const trackSkillUsage = (0, import_react8.useCallback)((skillName) => {
|
|
3469
3932
|
trackBehavior("skillUsage", (prev) => ({
|
|
3470
3933
|
...prev,
|
|
3471
3934
|
[skillName]: (prev[skillName] || 0) + 1
|
|
3472
3935
|
}));
|
|
3473
3936
|
}, [trackBehavior]);
|
|
3474
|
-
const trackChecklistSkip = (0,
|
|
3937
|
+
const trackChecklistSkip = (0, import_react8.useCallback)(() => {
|
|
3475
3938
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3476
3939
|
total: (prev.total || 0) + 1,
|
|
3477
3940
|
skipped: (prev.skipped || 0) + 1
|
|
3478
3941
|
}));
|
|
3479
3942
|
}, [trackBehavior]);
|
|
3480
|
-
const trackChecklistComplete = (0,
|
|
3943
|
+
const trackChecklistComplete = (0, import_react8.useCallback)(() => {
|
|
3481
3944
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3482
3945
|
total: (prev.total || 0) + 1,
|
|
3483
3946
|
skipped: prev.skipped || 0
|
|
3484
3947
|
}));
|
|
3485
3948
|
}, [trackBehavior]);
|
|
3486
|
-
const
|
|
3949
|
+
const {
|
|
3950
|
+
activeChecklistRef,
|
|
3951
|
+
pendingChecklistRef,
|
|
3952
|
+
skipNextChecklistParsingRef,
|
|
3953
|
+
handleChecklistStart,
|
|
3954
|
+
handleChecklistAbort,
|
|
3955
|
+
handleChecklistRetry,
|
|
3956
|
+
handleChecklistSkip
|
|
3957
|
+
} = useChecklist({
|
|
3958
|
+
sessionsRef,
|
|
3959
|
+
sessions,
|
|
3960
|
+
setSessions,
|
|
3961
|
+
sendMessage: (...args) => sendMessageForChecklistRef.current(...args),
|
|
3962
|
+
abortControllers: abortControllersRef,
|
|
3963
|
+
removeLoadingSession,
|
|
3964
|
+
pendingAttachmentDataRef,
|
|
3965
|
+
trackChecklistSkip,
|
|
3966
|
+
resolveChecklistRefImage,
|
|
3967
|
+
buildChecklistStepPrompt
|
|
3968
|
+
});
|
|
3969
|
+
const trackRegenerate = (0, import_react8.useCallback)(() => {
|
|
3487
3970
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3488
3971
|
...prev,
|
|
3489
3972
|
regenerated: (prev.regenerated || 0) + 1
|
|
3490
3973
|
}));
|
|
3491
3974
|
}, [trackBehavior]);
|
|
3492
|
-
const trackMessageSent = (0,
|
|
3975
|
+
const trackMessageSent = (0, import_react8.useCallback)(() => {
|
|
3493
3976
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3494
3977
|
total: (prev.total || 0) + 1,
|
|
3495
3978
|
regenerated: prev.regenerated || 0
|
|
3496
3979
|
}));
|
|
3497
3980
|
}, [trackBehavior]);
|
|
3498
|
-
const sendMessage = (0,
|
|
3981
|
+
const sendMessage = (0, import_react8.useCallback)(async (content, options2) => {
|
|
3499
3982
|
const messageContent = content || input;
|
|
3500
3983
|
if (!messageContent.trim() && attachments.length === 0 || isLoading) return;
|
|
3984
|
+
if (options2?.hiddenUserMessage) {
|
|
3985
|
+
toolCallDepthRef.current += 1;
|
|
3986
|
+
if (toolCallDepthRef.current > maxToolCallDepth) {
|
|
3987
|
+
toolCallDepthRef.current = 0;
|
|
3988
|
+
onErrorRef.current?.(new Error(`\uB3C4\uAD6C \uD638\uCD9C \uAE4A\uC774 \uC81C\uD55C \uCD08\uACFC (\uCD5C\uB300 ${maxToolCallDepth}\uD68C)`));
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
} else {
|
|
3992
|
+
toolCallDepthRef.current = 0;
|
|
3993
|
+
}
|
|
3501
3994
|
let sessionId = currentSessionId;
|
|
3502
3995
|
if (!sessionId) {
|
|
3503
3996
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
@@ -3551,6 +4044,7 @@ ${finalContent}`;
|
|
|
3551
4044
|
const actionPrompt = selectedAction?.systemPrompt;
|
|
3552
4045
|
const isHidden = options2?.hiddenUserMessage ?? false;
|
|
3553
4046
|
const currentAttachments = attachments;
|
|
4047
|
+
const uploadedUrlMap = /* @__PURE__ */ new Map();
|
|
3554
4048
|
let userContentParts;
|
|
3555
4049
|
if (currentAttachments.length > 0) {
|
|
3556
4050
|
userContentParts = [];
|
|
@@ -3559,8 +4053,19 @@ ${finalContent}`;
|
|
|
3559
4053
|
}
|
|
3560
4054
|
for (const att of currentAttachments) {
|
|
3561
4055
|
if (att.type === "image" && att.file) {
|
|
3562
|
-
|
|
3563
|
-
|
|
4056
|
+
let imageUrl;
|
|
4057
|
+
try {
|
|
4058
|
+
if (fileUploaderRef.current) {
|
|
4059
|
+
imageUrl = await fileUploaderRef.current(att.file);
|
|
4060
|
+
} else {
|
|
4061
|
+
const dataUri = await fileToDataUri(att.file);
|
|
4062
|
+
imageUrl = onUploadImageRef.current ? await onUploadImageRef.current(dataUri, att.name) : dataUri;
|
|
4063
|
+
}
|
|
4064
|
+
} catch (err) {
|
|
4065
|
+
console.error("[chatllm] image upload failed, falling back to data URI:", err);
|
|
4066
|
+
imageUrl = await fileToDataUri(att.file);
|
|
4067
|
+
}
|
|
4068
|
+
uploadedUrlMap.set(att.id, imageUrl);
|
|
3564
4069
|
userContentParts.push({ type: "image", url: imageUrl, alt: att.name, fileName: att.name });
|
|
3565
4070
|
} else {
|
|
3566
4071
|
userContentParts.push({ type: "file", name: att.name, url: att.previewUrl || "", mimeType: att.mimeType, size: att.size });
|
|
@@ -3659,7 +4164,7 @@ ${finalContent}`;
|
|
|
3659
4164
|
})
|
|
3660
4165
|
);
|
|
3661
4166
|
try {
|
|
3662
|
-
const filesToPass = fileUploaderRef.current ? await
|
|
4167
|
+
const filesToPass = fileUploaderRef.current ? await convertAttachmentsWithUploaderCached(matchedFiles, fileUploaderRef.current, uploadedUrlMap) : skillConfig.autoConvertBase64 ? await convertAttachmentsToBase64(matchedFiles) : matchedFiles;
|
|
3663
4168
|
const result = await skillConfig.execute({ files: filesToPass, userMessage: finalContent });
|
|
3664
4169
|
const attachResultType = result.metadata?.type || result.metadata?.resultType || "text";
|
|
3665
4170
|
const toolResultPart = {
|
|
@@ -3718,7 +4223,7 @@ ${finalContent}`;
|
|
|
3718
4223
|
if (hasImageAttachments && hasUserText) {
|
|
3719
4224
|
const imageAttachments = currentAttachments.filter((a) => a.type === "image");
|
|
3720
4225
|
try {
|
|
3721
|
-
pendingAttachmentDataRef.current = fileUploaderRef.current ? await
|
|
4226
|
+
pendingAttachmentDataRef.current = fileUploaderRef.current ? await convertAttachmentsWithUploaderCached(imageAttachments, fileUploaderRef.current, uploadedUrlMap) : await convertAttachmentsToBase64(imageAttachments);
|
|
3722
4227
|
} catch (err) {
|
|
3723
4228
|
console.error("[chatllm] pendingAttachment conversion failed:", err);
|
|
3724
4229
|
pendingAttachmentDataRef.current = null;
|
|
@@ -3771,10 +4276,11 @@ ${finalContent}`;
|
|
|
3771
4276
|
return;
|
|
3772
4277
|
}
|
|
3773
4278
|
abortControllersRef.current.set(capturedSessionId, new AbortController());
|
|
4279
|
+
let accumulatedContent = "";
|
|
4280
|
+
let lastUsage = null;
|
|
3774
4281
|
try {
|
|
3775
4282
|
const shouldSkipSkillParsing = skipNextSkillParsingRef.current;
|
|
3776
4283
|
skipNextSkillParsingRef.current = false;
|
|
3777
|
-
let accumulatedContent = "";
|
|
3778
4284
|
let checklistStepImageUrl = null;
|
|
3779
4285
|
let messagesToSend = [...existingMessages, userMessage];
|
|
3780
4286
|
const recompressionThreshold = DEFAULT_RECOMPRESSION_THRESHOLD;
|
|
@@ -3980,118 +4486,70 @@ ${attachmentContext}
|
|
|
3980
4486
|
emitFetchHeaders(response);
|
|
3981
4487
|
}
|
|
3982
4488
|
if (response) {
|
|
3983
|
-
if (!response.ok) throw new Error("API error");
|
|
3984
|
-
const reader = response.body?.getReader();
|
|
3985
|
-
if (!reader) throw new Error("No reader");
|
|
3986
|
-
const decoder = new TextDecoder();
|
|
3987
|
-
let buffer = "";
|
|
3988
4489
|
let skillTagDetected = false;
|
|
3989
4490
|
let checklistTagDetected = false;
|
|
3990
4491
|
let toolCallAcc = null;
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4492
|
+
const streamResult = await streamResponse(response, (chunk) => {
|
|
4493
|
+
if (useNativeTools && chunk.delta?.tool_calls) {
|
|
4494
|
+
toolCallAcc = accumulateToolCallDelta(toolCallAcc, chunk.delta);
|
|
4495
|
+
}
|
|
4496
|
+
if (useNativeTools && chunk.finishReason === "tool_calls" && toolCallAcc) {
|
|
4497
|
+
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4498
|
+
skillTagDetected = true;
|
|
4499
|
+
return "break";
|
|
4500
|
+
}
|
|
4501
|
+
const { content: content2, thinking } = chunk;
|
|
4502
|
+
if (content2 || thinking) {
|
|
4503
|
+
if (content2) accumulatedContent += content2;
|
|
4504
|
+
if (!shouldSkipSkillParsing && accumulatedContent.includes("</skill_use>")) {
|
|
4505
|
+
const endIdx = accumulatedContent.indexOf("</skill_use>");
|
|
4506
|
+
accumulatedContent = accumulatedContent.substring(0, endIdx + "</skill_use>".length);
|
|
4507
|
+
skillTagDetected = true;
|
|
4003
4508
|
}
|
|
4004
|
-
|
|
4005
|
-
const
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
if (useNativeTools && delta?.tool_calls) {
|
|
4009
|
-
toolCallAcc = accumulateToolCallDelta(toolCallAcc, delta);
|
|
4010
|
-
}
|
|
4011
|
-
if (useNativeTools && finishReason === "tool_calls" && toolCallAcc) {
|
|
4012
|
-
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4013
|
-
skillTagDetected = true;
|
|
4014
|
-
break;
|
|
4015
|
-
}
|
|
4016
|
-
const content2 = delta?.content || parsed.message?.content || parsed.content || parsed.text || "";
|
|
4017
|
-
const thinking = parsed.message?.thinking || "";
|
|
4018
|
-
if (content2 || thinking) {
|
|
4019
|
-
if (content2) accumulatedContent += content2;
|
|
4020
|
-
if (!shouldSkipSkillParsing && accumulatedContent.includes("</skill_use>")) {
|
|
4021
|
-
const endIdx = accumulatedContent.indexOf("</skill_use>");
|
|
4022
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</skill_use>".length);
|
|
4023
|
-
skillTagDetected = true;
|
|
4024
|
-
}
|
|
4025
|
-
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
4026
|
-
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
4027
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
4028
|
-
checklistTagDetected = true;
|
|
4029
|
-
}
|
|
4030
|
-
const displayContent = skillTagDetected ? accumulatedContent : null;
|
|
4031
|
-
setSessions(
|
|
4032
|
-
(prev) => prev.map((s) => {
|
|
4033
|
-
if (s.id === capturedSessionId) {
|
|
4034
|
-
return {
|
|
4035
|
-
...s,
|
|
4036
|
-
messages: s.messages.map((m) => {
|
|
4037
|
-
if (m.id !== assistantMessageId) return m;
|
|
4038
|
-
if (displayContent) {
|
|
4039
|
-
return { ...m, content: displayContent };
|
|
4040
|
-
}
|
|
4041
|
-
let newContent = m.content;
|
|
4042
|
-
if (thinking) {
|
|
4043
|
-
if (!newContent.includes("<thinking>")) {
|
|
4044
|
-
newContent = "<thinking>" + thinking;
|
|
4045
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
4046
|
-
newContent += thinking;
|
|
4047
|
-
}
|
|
4048
|
-
}
|
|
4049
|
-
if (content2) {
|
|
4050
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
4051
|
-
newContent += "</thinking>\n\n";
|
|
4052
|
-
}
|
|
4053
|
-
newContent += content2;
|
|
4054
|
-
}
|
|
4055
|
-
return { ...m, content: newContent };
|
|
4056
|
-
})
|
|
4057
|
-
};
|
|
4058
|
-
}
|
|
4059
|
-
return s;
|
|
4060
|
-
})
|
|
4061
|
-
);
|
|
4062
|
-
if (skillTagDetected || checklistTagDetected) break;
|
|
4063
|
-
}
|
|
4064
|
-
} catch {
|
|
4509
|
+
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
4510
|
+
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
4511
|
+
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
4512
|
+
checklistTagDetected = true;
|
|
4065
4513
|
}
|
|
4514
|
+
const displayContent = skillTagDetected ? accumulatedContent : null;
|
|
4515
|
+
setSessions(
|
|
4516
|
+
(prev) => prev.map((s) => {
|
|
4517
|
+
if (s.id === capturedSessionId) {
|
|
4518
|
+
return {
|
|
4519
|
+
...s,
|
|
4520
|
+
messages: s.messages.map((m) => {
|
|
4521
|
+
if (m.id !== assistantMessageId) return m;
|
|
4522
|
+
if (displayContent) {
|
|
4523
|
+
return { ...m, content: displayContent };
|
|
4524
|
+
}
|
|
4525
|
+
let newContent = m.content;
|
|
4526
|
+
if (thinking) {
|
|
4527
|
+
if (!newContent.includes("<thinking>")) {
|
|
4528
|
+
newContent = "<thinking>" + thinking;
|
|
4529
|
+
} else if (!newContent.includes("</thinking>")) {
|
|
4530
|
+
newContent += thinking;
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
if (content2) {
|
|
4534
|
+
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
4535
|
+
newContent += "</thinking>\n\n";
|
|
4536
|
+
}
|
|
4537
|
+
newContent += content2;
|
|
4538
|
+
}
|
|
4539
|
+
return { ...m, content: newContent };
|
|
4540
|
+
})
|
|
4541
|
+
};
|
|
4542
|
+
}
|
|
4543
|
+
return s;
|
|
4544
|
+
})
|
|
4545
|
+
);
|
|
4546
|
+
if (skillTagDetected || checklistTagDetected) return "break";
|
|
4066
4547
|
}
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
if (useNativeTools && toolCallAcc && !skillTagDetected) {
|
|
4070
|
-
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4071
|
-
skillTagDetected = true;
|
|
4072
|
-
}
|
|
4073
|
-
if (buffer.trim()) {
|
|
4074
|
-
try {
|
|
4075
|
-
const parsed = JSON.parse(buffer);
|
|
4076
|
-
const content2 = parsed.message?.content || parsed.content || parsed.text;
|
|
4077
|
-
if (content2) {
|
|
4078
|
-
accumulatedContent += content2;
|
|
4079
|
-
setSessions(
|
|
4080
|
-
(prev) => prev.map((s) => {
|
|
4081
|
-
if (s.id === capturedSessionId) {
|
|
4082
|
-
return {
|
|
4083
|
-
...s,
|
|
4084
|
-
messages: s.messages.map(
|
|
4085
|
-
(m) => m.id === assistantMessageId ? { ...m, content: m.content + content2 } : m
|
|
4086
|
-
)
|
|
4087
|
-
};
|
|
4088
|
-
}
|
|
4089
|
-
return s;
|
|
4090
|
-
})
|
|
4091
|
-
);
|
|
4092
|
-
}
|
|
4093
|
-
} catch {
|
|
4094
|
-
}
|
|
4548
|
+
});
|
|
4549
|
+
lastUsage = streamResult.usage;
|
|
4550
|
+
if (useNativeTools && toolCallAcc && !skillTagDetected) {
|
|
4551
|
+
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4552
|
+
skillTagDetected = true;
|
|
4095
4553
|
}
|
|
4096
4554
|
}
|
|
4097
4555
|
const saveMessagesOnEarlyReturn = (overrideContentParts) => {
|
|
@@ -4453,9 +4911,10 @@ ${result.content}
|
|
|
4453
4911
|
}
|
|
4454
4912
|
skipNextSkillParsingRef.current = true;
|
|
4455
4913
|
saveMessagesOnEarlyReturn();
|
|
4914
|
+
const truncatedResult = result.content.length > maxToolResultSize ? result.content.substring(0, maxToolResultSize) + "\n\n...(truncated)" : result.content;
|
|
4456
4915
|
const resultPrompt = `\uC2A4\uD0AC "${detectedSkill.name}" \uC2E4\uD589 \uACB0\uACFC:
|
|
4457
4916
|
|
|
4458
|
-
${
|
|
4917
|
+
${truncatedResult}
|
|
4459
4918
|
|
|
4460
4919
|
\uC704 \uACB0\uACFC\uB97C \uBC14\uD0D5\uC73C\uB85C \uC0AC\uC6A9\uC790\uC758 \uC6D0\uB798 \uC9C8\uBB38\uC5D0 \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
4461
4920
|
setTimeout(() => {
|
|
@@ -4781,6 +5240,32 @@ ${stepSummary}
|
|
|
4781
5240
|
};
|
|
4782
5241
|
})
|
|
4783
5242
|
);
|
|
5243
|
+
if (lastUsage && capturedSessionId) {
|
|
5244
|
+
const usage = lastUsage;
|
|
5245
|
+
setSessions(
|
|
5246
|
+
(prev) => prev.map((s) => {
|
|
5247
|
+
if (s.id !== capturedSessionId) return s;
|
|
5248
|
+
const prevStats = s.tokenStats ?? {
|
|
5249
|
+
totalPromptTokens: 0,
|
|
5250
|
+
totalCompletionTokens: 0,
|
|
5251
|
+
totalTokens: 0,
|
|
5252
|
+
messageCount: 0,
|
|
5253
|
+
lastUpdated: 0
|
|
5254
|
+
};
|
|
5255
|
+
return {
|
|
5256
|
+
...s,
|
|
5257
|
+
tokenStats: {
|
|
5258
|
+
totalPromptTokens: prevStats.totalPromptTokens + usage.promptTokens,
|
|
5259
|
+
totalCompletionTokens: prevStats.totalCompletionTokens + usage.completionTokens,
|
|
5260
|
+
totalTokens: prevStats.totalTokens + usage.totalTokens,
|
|
5261
|
+
messageCount: prevStats.messageCount + 1,
|
|
5262
|
+
lastUpdated: Date.now()
|
|
5263
|
+
}
|
|
5264
|
+
};
|
|
5265
|
+
})
|
|
5266
|
+
);
|
|
5267
|
+
onTokenUsageRef.current?.(capturedSessionId, usage);
|
|
5268
|
+
}
|
|
4784
5269
|
if (useExternalStorage && capturedSessionId) {
|
|
4785
5270
|
const assistantContentForSave = accumulatedContent;
|
|
4786
5271
|
if (assistantContentForSave && onSaveMessagesRef.current) {
|
|
@@ -4820,18 +5305,32 @@ ${stepSummary}
|
|
|
4820
5305
|
observer.processMessages(capturedSessionId, newMessages);
|
|
4821
5306
|
}
|
|
4822
5307
|
} catch (error) {
|
|
4823
|
-
|
|
5308
|
+
const classified = classifyFetchError(error);
|
|
5309
|
+
if (classified.code === "ABORT") {
|
|
5310
|
+
if (accumulatedContent) {
|
|
5311
|
+
setSessions(
|
|
5312
|
+
(prev) => prev.map((s) => {
|
|
5313
|
+
if (s.id !== capturedSessionId) return s;
|
|
5314
|
+
return {
|
|
5315
|
+
...s,
|
|
5316
|
+
messages: s.messages.map(
|
|
5317
|
+
(m) => m.id === assistantMessageId ? { ...m, content: accumulatedContent } : m
|
|
5318
|
+
)
|
|
5319
|
+
};
|
|
5320
|
+
})
|
|
5321
|
+
);
|
|
5322
|
+
onAbortRef.current?.(capturedSessionId, accumulatedContent);
|
|
5323
|
+
}
|
|
4824
5324
|
return;
|
|
4825
5325
|
}
|
|
4826
|
-
|
|
4827
|
-
onErrorRef.current?.(err);
|
|
5326
|
+
onErrorRef.current?.(classified);
|
|
4828
5327
|
setSessions(
|
|
4829
5328
|
(prev) => prev.map((s) => {
|
|
4830
5329
|
if (s.id === capturedSessionId) {
|
|
4831
5330
|
return {
|
|
4832
5331
|
...s,
|
|
4833
5332
|
messages: s.messages.map(
|
|
4834
|
-
(m) => m.id === assistantMessageId ? { ...m, content: "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694." } : m
|
|
5333
|
+
(m) => m.id === assistantMessageId ? { ...m, content: accumulatedContent || "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694." } : m
|
|
4835
5334
|
)
|
|
4836
5335
|
};
|
|
4837
5336
|
}
|
|
@@ -4864,241 +5363,8 @@ ${stepSummary}
|
|
|
4864
5363
|
attachments,
|
|
4865
5364
|
continueAfterToolResult
|
|
4866
5365
|
]);
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
const session = sessionsRef.current.find(
|
|
4870
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4871
|
-
);
|
|
4872
|
-
if (!session) return;
|
|
4873
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4874
|
-
if (!message?.checklistBlock) return;
|
|
4875
|
-
pendingChecklistRef.current = null;
|
|
4876
|
-
activeChecklistRef.current = {
|
|
4877
|
-
messageId,
|
|
4878
|
-
sessionId: session.id,
|
|
4879
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
4880
|
-
currentStep: 0,
|
|
4881
|
-
stepResults: []
|
|
4882
|
-
};
|
|
4883
|
-
setSessions(
|
|
4884
|
-
(prev) => prev.map((s) => {
|
|
4885
|
-
if (s.id !== session.id) return s;
|
|
4886
|
-
return {
|
|
4887
|
-
...s,
|
|
4888
|
-
messages: s.messages.map((m) => {
|
|
4889
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4890
|
-
const updatedItems = m.checklistBlock.items.map((it, idx) => ({
|
|
4891
|
-
...it,
|
|
4892
|
-
status: idx === 0 ? "in_progress" : it.status
|
|
4893
|
-
}));
|
|
4894
|
-
return {
|
|
4895
|
-
...m,
|
|
4896
|
-
checklistBlock: { ...m.checklistBlock, items: updatedItems, currentStep: 0 }
|
|
4897
|
-
};
|
|
4898
|
-
})
|
|
4899
|
-
};
|
|
4900
|
-
})
|
|
4901
|
-
);
|
|
4902
|
-
skipNextChecklistParsingRef.current = true;
|
|
4903
|
-
setTimeout(() => {
|
|
4904
|
-
const items = message.checklistBlock.items;
|
|
4905
|
-
const refUrl = resolveChecklistRefImage(items[0], messageId, session.id);
|
|
4906
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4907
|
-
sendMessage(
|
|
4908
|
-
buildChecklistStepPrompt(0, items.length, items[0], true, refUrl),
|
|
4909
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: 0, title: items[0].title } }
|
|
4910
|
-
);
|
|
4911
|
-
}, 100);
|
|
4912
|
-
},
|
|
4913
|
-
[sendMessage]
|
|
4914
|
-
);
|
|
4915
|
-
const handleChecklistAbort = (0, import_react6.useCallback)(() => {
|
|
4916
|
-
if (!activeChecklistRef.current) return;
|
|
4917
|
-
const checklist = activeChecklistRef.current;
|
|
4918
|
-
const stepIdx = checklist.currentStep;
|
|
4919
|
-
abortControllersRef.current.get(checklist.sessionId)?.abort();
|
|
4920
|
-
setSessions(
|
|
4921
|
-
(prev) => prev.map((s) => {
|
|
4922
|
-
if (s.id !== checklist.sessionId) return s;
|
|
4923
|
-
return {
|
|
4924
|
-
...s,
|
|
4925
|
-
messages: s.messages.map((m) => {
|
|
4926
|
-
if (m.id !== checklist.messageId || !m.checklistBlock) return m;
|
|
4927
|
-
return {
|
|
4928
|
-
...m,
|
|
4929
|
-
checklistBlock: {
|
|
4930
|
-
...m.checklistBlock,
|
|
4931
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4932
|
-
...it,
|
|
4933
|
-
status: idx === stepIdx ? "error" : it.status
|
|
4934
|
-
}))
|
|
4935
|
-
}
|
|
4936
|
-
};
|
|
4937
|
-
})
|
|
4938
|
-
};
|
|
4939
|
-
})
|
|
4940
|
-
);
|
|
4941
|
-
activeChecklistRef.current = null;
|
|
4942
|
-
removeLoadingSession(checklist.sessionId);
|
|
4943
|
-
}, [removeLoadingSession]);
|
|
4944
|
-
const handleChecklistRetry = (0, import_react6.useCallback)(
|
|
4945
|
-
(messageId, stepIndex) => {
|
|
4946
|
-
const session = sessionsRef.current.find(
|
|
4947
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4948
|
-
);
|
|
4949
|
-
if (!session) return;
|
|
4950
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4951
|
-
if (!message?.checklistBlock) return;
|
|
4952
|
-
activeChecklistRef.current = {
|
|
4953
|
-
messageId,
|
|
4954
|
-
sessionId: session.id,
|
|
4955
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
4956
|
-
currentStep: stepIndex,
|
|
4957
|
-
stepResults: message.checklistBlock.items.slice(0, stepIndex).map((it) => it.result || "")
|
|
4958
|
-
};
|
|
4959
|
-
setSessions(
|
|
4960
|
-
(prev) => prev.map((s) => {
|
|
4961
|
-
if (s.id !== session.id) return s;
|
|
4962
|
-
return {
|
|
4963
|
-
...s,
|
|
4964
|
-
messages: s.messages.map((m) => {
|
|
4965
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4966
|
-
return {
|
|
4967
|
-
...m,
|
|
4968
|
-
checklistBlock: {
|
|
4969
|
-
...m.checklistBlock,
|
|
4970
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4971
|
-
...it,
|
|
4972
|
-
status: idx === stepIndex ? "in_progress" : idx > stepIndex ? "pending" : it.status
|
|
4973
|
-
})),
|
|
4974
|
-
currentStep: stepIndex
|
|
4975
|
-
}
|
|
4976
|
-
};
|
|
4977
|
-
})
|
|
4978
|
-
};
|
|
4979
|
-
})
|
|
4980
|
-
);
|
|
4981
|
-
skipNextChecklistParsingRef.current = true;
|
|
4982
|
-
setTimeout(() => {
|
|
4983
|
-
const items = message.checklistBlock.items;
|
|
4984
|
-
const refUrl = resolveChecklistRefImage(items[stepIndex], messageId, session.id);
|
|
4985
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4986
|
-
sendMessage(
|
|
4987
|
-
buildChecklistStepPrompt(stepIndex, items.length, items[stepIndex], stepIndex === 0, refUrl),
|
|
4988
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: stepIndex, title: items[stepIndex].title } }
|
|
4989
|
-
);
|
|
4990
|
-
}, 100);
|
|
4991
|
-
},
|
|
4992
|
-
[sendMessage]
|
|
4993
|
-
);
|
|
4994
|
-
const handleChecklistSkip = (0, import_react6.useCallback)(
|
|
4995
|
-
(messageId, stepIndex) => {
|
|
4996
|
-
const session = sessionsRef.current.find(
|
|
4997
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4998
|
-
);
|
|
4999
|
-
if (!session) return;
|
|
5000
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
5001
|
-
if (!message?.checklistBlock) return;
|
|
5002
|
-
trackChecklistSkip();
|
|
5003
|
-
setSessions(
|
|
5004
|
-
(prev) => prev.map((s) => {
|
|
5005
|
-
if (s.id !== session.id) return s;
|
|
5006
|
-
return {
|
|
5007
|
-
...s,
|
|
5008
|
-
messages: s.messages.map((m) => {
|
|
5009
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5010
|
-
return {
|
|
5011
|
-
...m,
|
|
5012
|
-
checklistBlock: {
|
|
5013
|
-
...m.checklistBlock,
|
|
5014
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
5015
|
-
...it,
|
|
5016
|
-
status: idx === stepIndex ? "done" : it.status,
|
|
5017
|
-
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
5018
|
-
}))
|
|
5019
|
-
}
|
|
5020
|
-
};
|
|
5021
|
-
})
|
|
5022
|
-
};
|
|
5023
|
-
})
|
|
5024
|
-
);
|
|
5025
|
-
const nextPending = message.checklistBlock.items.findIndex(
|
|
5026
|
-
(it, idx) => idx > stepIndex && (it.status === "pending" || it.status === "error")
|
|
5027
|
-
);
|
|
5028
|
-
if (nextPending >= 0) {
|
|
5029
|
-
activeChecklistRef.current = {
|
|
5030
|
-
messageId,
|
|
5031
|
-
sessionId: session.id,
|
|
5032
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
5033
|
-
currentStep: nextPending,
|
|
5034
|
-
stepResults: message.checklistBlock.items.slice(0, nextPending).map((it) => it.result || "(\uAC74\uB108\uB700)")
|
|
5035
|
-
};
|
|
5036
|
-
setSessions(
|
|
5037
|
-
(prev) => prev.map((s) => {
|
|
5038
|
-
if (s.id !== session.id) return s;
|
|
5039
|
-
return {
|
|
5040
|
-
...s,
|
|
5041
|
-
messages: s.messages.map((m) => {
|
|
5042
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5043
|
-
return {
|
|
5044
|
-
...m,
|
|
5045
|
-
checklistBlock: {
|
|
5046
|
-
...m.checklistBlock,
|
|
5047
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
5048
|
-
...it,
|
|
5049
|
-
status: idx === nextPending ? "in_progress" : it.status
|
|
5050
|
-
})),
|
|
5051
|
-
currentStep: nextPending
|
|
5052
|
-
}
|
|
5053
|
-
};
|
|
5054
|
-
})
|
|
5055
|
-
};
|
|
5056
|
-
})
|
|
5057
|
-
);
|
|
5058
|
-
skipNextChecklistParsingRef.current = true;
|
|
5059
|
-
setTimeout(() => {
|
|
5060
|
-
const items = message.checklistBlock.items;
|
|
5061
|
-
const refUrl = resolveChecklistRefImage(items[nextPending], messageId, session.id);
|
|
5062
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
5063
|
-
sendMessage(
|
|
5064
|
-
buildChecklistStepPrompt(nextPending, items.length, items[nextPending], false, refUrl),
|
|
5065
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: nextPending, title: items[nextPending].title } }
|
|
5066
|
-
);
|
|
5067
|
-
}, 100);
|
|
5068
|
-
} else {
|
|
5069
|
-
const allResults = message.checklistBlock.items.map((it, i) => {
|
|
5070
|
-
const result = i === stepIndex ? "(\uAC74\uB108\uB700)" : it.result || "(\uAC74\uB108\uB700)";
|
|
5071
|
-
return `### ${i + 1}. ${it.title}
|
|
5072
|
-
${result}`;
|
|
5073
|
-
}).join("\n\n");
|
|
5074
|
-
setSessions(
|
|
5075
|
-
(prev) => prev.map((s) => {
|
|
5076
|
-
if (s.id !== session.id) return s;
|
|
5077
|
-
return {
|
|
5078
|
-
...s,
|
|
5079
|
-
messages: s.messages.map((m) => {
|
|
5080
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5081
|
-
return { ...m, checklistBlock: { ...m.checklistBlock, completed: true } };
|
|
5082
|
-
})
|
|
5083
|
-
};
|
|
5084
|
-
})
|
|
5085
|
-
);
|
|
5086
|
-
skipNextChecklistParsingRef.current = true;
|
|
5087
|
-
setTimeout(() => {
|
|
5088
|
-
sendMessage(
|
|
5089
|
-
`\uBAA8\uB4E0 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uB2E8\uACC4\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC544\uB798\uB294 \uAC01 \uB2E8\uACC4\uBCC4 \uACB0\uACFC\uC785\uB2C8\uB2E4:
|
|
5090
|
-
|
|
5091
|
-
${allResults}
|
|
5092
|
-
|
|
5093
|
-
\uC704 \uACB0\uACFC\uB97C \uC885\uD569\uD558\uC5EC \uCD5C\uC885 \uACB0\uACFC\uBB3C\uC744 \uC644\uC131\uD574\uC8FC\uC138\uC694. checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`,
|
|
5094
|
-
{ hiddenUserMessage: true, isChecklistExecution: true }
|
|
5095
|
-
);
|
|
5096
|
-
}, 100);
|
|
5097
|
-
}
|
|
5098
|
-
},
|
|
5099
|
-
[sendMessage]
|
|
5100
|
-
);
|
|
5101
|
-
const handlePollSubmit = (0, import_react6.useCallback)(
|
|
5366
|
+
sendMessageForChecklistRef.current = sendMessage;
|
|
5367
|
+
const handlePollSubmit = (0, import_react8.useCallback)(
|
|
5102
5368
|
(messageId, responses) => {
|
|
5103
5369
|
const currentSess = sessions.find((s) => s.id === currentSessionId);
|
|
5104
5370
|
const targetMessage = currentSess?.messages.find((m) => m.id === messageId);
|
|
@@ -5168,7 +5434,7 @@ ${formattedParts.join("\n")}
|
|
|
5168
5434
|
},
|
|
5169
5435
|
[sessions, currentSessionId, selectedModel, sendMessage]
|
|
5170
5436
|
);
|
|
5171
|
-
const saveEdit = (0,
|
|
5437
|
+
const saveEdit = (0, import_react8.useCallback)(async (content) => {
|
|
5172
5438
|
if (!editingMessageId || !currentSession || !currentSessionId) return;
|
|
5173
5439
|
const messageIndex = currentSession.messages.findIndex((m) => m.id === editingMessageId);
|
|
5174
5440
|
if (messageIndex === -1) return;
|
|
@@ -5185,7 +5451,7 @@ ${formattedParts.join("\n")}
|
|
|
5185
5451
|
setEditingMessageId(null);
|
|
5186
5452
|
await sendMessage(content);
|
|
5187
5453
|
}, [editingMessageId, currentSession, currentSessionId, sendMessage]);
|
|
5188
|
-
const regenerate = (0,
|
|
5454
|
+
const regenerate = (0, import_react8.useCallback)(async (messageId) => {
|
|
5189
5455
|
console.log("[ChatUI] Regenerate called:", { messageId, currentSessionId, isLoading });
|
|
5190
5456
|
if (!currentSession || !currentSessionId || isLoading) {
|
|
5191
5457
|
console.log("[ChatUI] Regenerate early return - missing session or loading");
|
|
@@ -5224,137 +5490,76 @@ ${formattedParts.join("\n")}
|
|
|
5224
5490
|
...s.messages,
|
|
5225
5491
|
{
|
|
5226
5492
|
id: assistantMessageId,
|
|
5227
|
-
role: "assistant",
|
|
5228
|
-
content: "",
|
|
5229
|
-
model: selectedModel,
|
|
5230
|
-
timestamp: Date.now()
|
|
5231
|
-
}
|
|
5232
|
-
]
|
|
5233
|
-
};
|
|
5234
|
-
}
|
|
5235
|
-
return s;
|
|
5236
|
-
})
|
|
5237
|
-
);
|
|
5238
|
-
const messagesUpToUser = currentSession.messages.slice(0, assistantIndex);
|
|
5239
|
-
const chatMessages = messagesUpToUser.map((m) => ({
|
|
5240
|
-
role: m.role,
|
|
5241
|
-
content: m.content,
|
|
5242
|
-
...m.contentParts && { contentParts: m.contentParts }
|
|
5243
|
-
}));
|
|
5244
|
-
const baseSystemPrompt = buildSystemPrompt(currentSession);
|
|
5245
|
-
const messagesForApi = baseSystemPrompt ? [{ role: "system", content: baseSystemPrompt }, ...chatMessages] : chatMessages;
|
|
5246
|
-
const modelConfig = models.find((m) => m.id === selectedModel);
|
|
5247
|
-
const provider = modelConfig?.provider || "ollama";
|
|
5248
|
-
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
5249
|
-
const requestBody = isOllama ? { model: selectedModel, messages: messagesForApi, stream: true } : {
|
|
5250
|
-
messages: messagesForApi,
|
|
5251
|
-
model: selectedModel,
|
|
5252
|
-
provider,
|
|
5253
|
-
apiKey: provider === "devdive" ? apiKey : void 0,
|
|
5254
|
-
stream: true
|
|
5255
|
-
};
|
|
5256
|
-
console.log("[ChatUI] Regenerate fetch:", { apiEndpoint, isOllama, model: selectedModel });
|
|
5257
|
-
const response = await fetch(apiEndpoint, {
|
|
5258
|
-
method: "POST",
|
|
5259
|
-
headers: { "Content-Type": "application/json" },
|
|
5260
|
-
body: JSON.stringify(requestBody),
|
|
5261
|
-
signal: abortControllersRef.current.get(capturedSessionId).signal
|
|
5262
|
-
});
|
|
5263
|
-
console.log("[ChatUI] Regenerate response status:", response.status);
|
|
5264
|
-
if (!response.ok) throw new Error(`API error: ${response.status}`);
|
|
5265
|
-
const reader = response.body?.getReader();
|
|
5266
|
-
if (!reader) throw new Error("No reader");
|
|
5267
|
-
const decoder = new TextDecoder();
|
|
5268
|
-
let buffer = "";
|
|
5269
|
-
while (true) {
|
|
5270
|
-
const { done, value } = await reader.read();
|
|
5271
|
-
if (done) break;
|
|
5272
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5273
|
-
const lines = buffer.split("\n");
|
|
5274
|
-
buffer = lines.pop() || "";
|
|
5275
|
-
for (const line of lines) {
|
|
5276
|
-
if (!line.trim()) continue;
|
|
5277
|
-
let data = line;
|
|
5278
|
-
if (line.startsWith("data: ")) {
|
|
5279
|
-
data = line.slice(6);
|
|
5280
|
-
if (data === "[DONE]") continue;
|
|
5281
|
-
}
|
|
5282
|
-
try {
|
|
5283
|
-
const parsed = JSON.parse(data);
|
|
5284
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5285
|
-
const thinking = parsed.message?.thinking || "";
|
|
5286
|
-
if (content || thinking) {
|
|
5287
|
-
setSessions(
|
|
5288
|
-
(prev) => prev.map((s) => {
|
|
5289
|
-
if (s.id === capturedSessionId) {
|
|
5290
|
-
return {
|
|
5291
|
-
...s,
|
|
5292
|
-
messages: s.messages.map((m) => {
|
|
5293
|
-
if (m.id !== assistantMessageId) return m;
|
|
5294
|
-
let newContent = m.content;
|
|
5295
|
-
if (thinking) {
|
|
5296
|
-
if (!newContent.includes("<thinking>")) {
|
|
5297
|
-
newContent = "<thinking>" + thinking;
|
|
5298
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5299
|
-
newContent += thinking;
|
|
5300
|
-
}
|
|
5301
|
-
}
|
|
5302
|
-
if (content) {
|
|
5303
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5304
|
-
newContent += "</thinking>\n\n";
|
|
5305
|
-
}
|
|
5306
|
-
newContent += content;
|
|
5307
|
-
}
|
|
5308
|
-
return { ...m, content: newContent };
|
|
5309
|
-
})
|
|
5310
|
-
};
|
|
5311
|
-
}
|
|
5312
|
-
return s;
|
|
5313
|
-
})
|
|
5314
|
-
);
|
|
5315
|
-
}
|
|
5316
|
-
} catch {
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
}
|
|
5320
|
-
if (buffer.trim()) {
|
|
5321
|
-
try {
|
|
5322
|
-
const parsed = JSON.parse(buffer);
|
|
5323
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5324
|
-
const thinking = parsed.message?.thinking || "";
|
|
5325
|
-
if (content || thinking) {
|
|
5326
|
-
setSessions(
|
|
5327
|
-
(prev) => prev.map((s) => {
|
|
5328
|
-
if (s.id === capturedSessionId) {
|
|
5329
|
-
return {
|
|
5330
|
-
...s,
|
|
5331
|
-
messages: s.messages.map((m) => {
|
|
5332
|
-
if (m.id !== assistantMessageId) return m;
|
|
5333
|
-
let newContent = m.content;
|
|
5334
|
-
if (thinking) {
|
|
5335
|
-
if (!newContent.includes("<thinking>")) {
|
|
5336
|
-
newContent = "<thinking>" + thinking;
|
|
5337
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5338
|
-
newContent += thinking;
|
|
5339
|
-
}
|
|
5340
|
-
}
|
|
5341
|
-
if (content) {
|
|
5342
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5343
|
-
newContent += "</thinking>\n\n";
|
|
5344
|
-
}
|
|
5345
|
-
newContent += content;
|
|
5346
|
-
}
|
|
5347
|
-
return { ...m, content: newContent };
|
|
5348
|
-
})
|
|
5349
|
-
};
|
|
5493
|
+
role: "assistant",
|
|
5494
|
+
content: "",
|
|
5495
|
+
model: selectedModel,
|
|
5496
|
+
timestamp: Date.now()
|
|
5350
5497
|
}
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
);
|
|
5498
|
+
]
|
|
5499
|
+
};
|
|
5354
5500
|
}
|
|
5355
|
-
|
|
5501
|
+
return s;
|
|
5502
|
+
})
|
|
5503
|
+
);
|
|
5504
|
+
const messagesUpToUser = currentSession.messages.slice(0, assistantIndex);
|
|
5505
|
+
const chatMessages = messagesUpToUser.map((m) => ({
|
|
5506
|
+
role: m.role,
|
|
5507
|
+
content: m.content,
|
|
5508
|
+
...m.contentParts && { contentParts: m.contentParts }
|
|
5509
|
+
}));
|
|
5510
|
+
const baseSystemPrompt = buildSystemPrompt(currentSession);
|
|
5511
|
+
const messagesForApi = baseSystemPrompt ? [{ role: "system", content: baseSystemPrompt }, ...chatMessages] : chatMessages;
|
|
5512
|
+
const modelConfig = models.find((m) => m.id === selectedModel);
|
|
5513
|
+
const provider = modelConfig?.provider || "ollama";
|
|
5514
|
+
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
5515
|
+
const requestBody = isOllama ? { model: selectedModel, messages: messagesForApi, stream: true } : {
|
|
5516
|
+
messages: messagesForApi,
|
|
5517
|
+
model: selectedModel,
|
|
5518
|
+
provider,
|
|
5519
|
+
apiKey: provider === "devdive" ? apiKey : void 0,
|
|
5520
|
+
stream: true
|
|
5521
|
+
};
|
|
5522
|
+
console.log("[ChatUI] Regenerate fetch:", { apiEndpoint, isOllama, model: selectedModel });
|
|
5523
|
+
const response = await fetch(apiEndpoint, {
|
|
5524
|
+
method: "POST",
|
|
5525
|
+
headers: { "Content-Type": "application/json" },
|
|
5526
|
+
body: JSON.stringify(requestBody),
|
|
5527
|
+
signal: abortControllersRef.current.get(capturedSessionId).signal
|
|
5528
|
+
});
|
|
5529
|
+
console.log("[ChatUI] Regenerate response status:", response.status);
|
|
5530
|
+
await streamResponse(response, (chunk) => {
|
|
5531
|
+
const { content, thinking } = chunk;
|
|
5532
|
+
if (content || thinking) {
|
|
5533
|
+
setSessions(
|
|
5534
|
+
(prev) => prev.map((s) => {
|
|
5535
|
+
if (s.id === capturedSessionId) {
|
|
5536
|
+
return {
|
|
5537
|
+
...s,
|
|
5538
|
+
messages: s.messages.map((m) => {
|
|
5539
|
+
if (m.id !== assistantMessageId) return m;
|
|
5540
|
+
let newContent = m.content;
|
|
5541
|
+
if (thinking) {
|
|
5542
|
+
if (!newContent.includes("<thinking>")) {
|
|
5543
|
+
newContent = "<thinking>" + thinking;
|
|
5544
|
+
} else if (!newContent.includes("</thinking>")) {
|
|
5545
|
+
newContent += thinking;
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
if (content) {
|
|
5549
|
+
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5550
|
+
newContent += "</thinking>\n\n";
|
|
5551
|
+
}
|
|
5552
|
+
newContent += content;
|
|
5553
|
+
}
|
|
5554
|
+
return { ...m, content: newContent };
|
|
5555
|
+
})
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
return s;
|
|
5559
|
+
})
|
|
5560
|
+
);
|
|
5356
5561
|
}
|
|
5357
|
-
}
|
|
5562
|
+
}, { noTimeout: true });
|
|
5358
5563
|
} catch (error) {
|
|
5359
5564
|
if (error instanceof Error && error.name === "AbortError") {
|
|
5360
5565
|
return;
|
|
@@ -5365,7 +5570,7 @@ ${formattedParts.join("\n")}
|
|
|
5365
5570
|
removeLoadingSession(capturedSessionId);
|
|
5366
5571
|
}
|
|
5367
5572
|
}, [currentSession, currentSessionId, loadingSessionIds, selectedModel, models, apiEndpoint, apiKey, buildSystemPrompt]);
|
|
5368
|
-
const askOtherModel = (0,
|
|
5573
|
+
const askOtherModel = (0, import_react8.useCallback)(async (messageId, targetModel) => {
|
|
5369
5574
|
if (!currentSession || !currentSessionId || isLoading) return;
|
|
5370
5575
|
const assistantIndex = currentSession.messages.findIndex((m) => m.id === messageId);
|
|
5371
5576
|
if (assistantIndex === -1) return;
|
|
@@ -5420,30 +5625,7 @@ ${currentSession.contextSummary}` },
|
|
|
5420
5625
|
responseContent = result.content;
|
|
5421
5626
|
responseSources = result.sources;
|
|
5422
5627
|
} else {
|
|
5423
|
-
|
|
5424
|
-
const decoder = new TextDecoder();
|
|
5425
|
-
let buffer = "";
|
|
5426
|
-
while (true) {
|
|
5427
|
-
const { done, value } = await reader.read();
|
|
5428
|
-
if (done) break;
|
|
5429
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5430
|
-
const lines = buffer.split("\n");
|
|
5431
|
-
buffer = lines.pop() || "";
|
|
5432
|
-
for (const line of lines) {
|
|
5433
|
-
if (line.startsWith("data: ")) {
|
|
5434
|
-
const data = line.slice(6);
|
|
5435
|
-
if (data === "[DONE]") continue;
|
|
5436
|
-
try {
|
|
5437
|
-
const parsed = JSON.parse(data);
|
|
5438
|
-
{
|
|
5439
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5440
|
-
if (chunk) responseContent += chunk;
|
|
5441
|
-
}
|
|
5442
|
-
} catch {
|
|
5443
|
-
}
|
|
5444
|
-
}
|
|
5445
|
-
}
|
|
5446
|
-
}
|
|
5628
|
+
responseContent = await parseSSEResponse(new Response(result));
|
|
5447
5629
|
}
|
|
5448
5630
|
} else {
|
|
5449
5631
|
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
@@ -5461,32 +5643,9 @@ ${currentSession.contextSummary}` },
|
|
|
5461
5643
|
signal: abortControllersRef.current.get(currentSessionId).signal
|
|
5462
5644
|
});
|
|
5463
5645
|
emitFetchHeaders(response);
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
const decoder = new TextDecoder();
|
|
5468
|
-
let buffer = "";
|
|
5469
|
-
while (true) {
|
|
5470
|
-
const { done, value } = await reader.read();
|
|
5471
|
-
if (done) break;
|
|
5472
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5473
|
-
const lines = buffer.split("\n");
|
|
5474
|
-
buffer = lines.pop() || "";
|
|
5475
|
-
for (const line of lines) {
|
|
5476
|
-
if (line.startsWith("data: ")) {
|
|
5477
|
-
const data = line.slice(6);
|
|
5478
|
-
if (data === "[DONE]") continue;
|
|
5479
|
-
try {
|
|
5480
|
-
const parsed = JSON.parse(data);
|
|
5481
|
-
{
|
|
5482
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5483
|
-
if (chunk) responseContent += chunk;
|
|
5484
|
-
}
|
|
5485
|
-
} catch {
|
|
5486
|
-
}
|
|
5487
|
-
}
|
|
5488
|
-
}
|
|
5489
|
-
}
|
|
5646
|
+
await streamResponse(response, (chunk) => {
|
|
5647
|
+
if (chunk.content) responseContent += chunk.content;
|
|
5648
|
+
}, { noTimeout: true });
|
|
5490
5649
|
}
|
|
5491
5650
|
const alternative = {
|
|
5492
5651
|
id: generateId5("alt"),
|
|
@@ -5546,13 +5705,13 @@ ${currentSession.contextSummary}` },
|
|
|
5546
5705
|
onSendMessage,
|
|
5547
5706
|
onError
|
|
5548
5707
|
]);
|
|
5549
|
-
const setActiveAlternative = (0,
|
|
5708
|
+
const setActiveAlternative = (0, import_react8.useCallback)((assistantMessageId, index) => {
|
|
5550
5709
|
setActiveAlternatives((prev) => ({
|
|
5551
5710
|
...prev,
|
|
5552
5711
|
[assistantMessageId]: index
|
|
5553
5712
|
}));
|
|
5554
5713
|
}, []);
|
|
5555
|
-
const getActiveAlternative = (0,
|
|
5714
|
+
const getActiveAlternative = (0, import_react8.useCallback)((assistantMessageId) => {
|
|
5556
5715
|
return activeAlternatives[assistantMessageId] ?? 0;
|
|
5557
5716
|
}, [activeAlternatives]);
|
|
5558
5717
|
return {
|
|
@@ -5610,6 +5769,10 @@ ${currentSession.contextSummary}` },
|
|
|
5610
5769
|
}));
|
|
5611
5770
|
await infoExtraction.extractInfo(recentMessages);
|
|
5612
5771
|
},
|
|
5772
|
+
getTokenStats: (0, import_react8.useCallback)((sessionId) => {
|
|
5773
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
5774
|
+
return session?.tokenStats ?? null;
|
|
5775
|
+
}, [sessions]),
|
|
5613
5776
|
// External Storage Loading States
|
|
5614
5777
|
/**
|
|
5615
5778
|
* @description 세션 목록 로딩 상태
|
|
@@ -5765,12 +5928,12 @@ ${result.content}
|
|
|
5765
5928
|
};
|
|
5766
5929
|
|
|
5767
5930
|
// src/react/contexts/ImageErrorContext.ts
|
|
5768
|
-
var
|
|
5769
|
-
var ImageErrorContext = (0,
|
|
5770
|
-
var useImageError = () => (0,
|
|
5931
|
+
var import_react9 = require("react");
|
|
5932
|
+
var ImageErrorContext = (0, import_react9.createContext)(null);
|
|
5933
|
+
var useImageError = () => (0, import_react9.useContext)(ImageErrorContext);
|
|
5771
5934
|
|
|
5772
5935
|
// src/react/components/ChatSidebar.tsx
|
|
5773
|
-
var
|
|
5936
|
+
var import_react13 = require("react");
|
|
5774
5937
|
|
|
5775
5938
|
// src/react/components/Icon.tsx
|
|
5776
5939
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -5870,10 +6033,10 @@ var IconSvg = ({
|
|
|
5870
6033
|
};
|
|
5871
6034
|
|
|
5872
6035
|
// src/react/components/MarkdownRenderer.tsx
|
|
5873
|
-
var
|
|
6036
|
+
var import_react11 = __toESM(require("react"));
|
|
5874
6037
|
|
|
5875
6038
|
// src/react/components/LinkChip.tsx
|
|
5876
|
-
var
|
|
6039
|
+
var import_react10 = require("react");
|
|
5877
6040
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
5878
6041
|
var getDomain = (url) => {
|
|
5879
6042
|
try {
|
|
@@ -5927,7 +6090,7 @@ var LinkChip = ({
|
|
|
5927
6090
|
index,
|
|
5928
6091
|
style
|
|
5929
6092
|
}) => {
|
|
5930
|
-
const [isHovered, setIsHovered] = (0,
|
|
6093
|
+
const [isHovered, setIsHovered] = (0, import_react10.useState)(false);
|
|
5931
6094
|
const domain = getDomain(url);
|
|
5932
6095
|
const shortName = getShortName(domain);
|
|
5933
6096
|
const domainColor = getDomainColor(domain);
|
|
@@ -6297,8 +6460,8 @@ var parseTableRow = (row) => {
|
|
|
6297
6460
|
return row.split("|").slice(1, -1).map((cell) => cell.trim());
|
|
6298
6461
|
};
|
|
6299
6462
|
var MarkdownTable = ({ data }) => {
|
|
6300
|
-
const [copied, setCopied] =
|
|
6301
|
-
const [isHovered, setIsHovered] =
|
|
6463
|
+
const [copied, setCopied] = import_react11.default.useState(false);
|
|
6464
|
+
const [isHovered, setIsHovered] = import_react11.default.useState(false);
|
|
6302
6465
|
const handleCopy = async () => {
|
|
6303
6466
|
const headerLine = data.headers.join(" ");
|
|
6304
6467
|
const bodyLines = data.rows.map((row) => row.join(" "));
|
|
@@ -6410,7 +6573,7 @@ var ThinkingSpinner = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
|
6410
6573
|
}
|
|
6411
6574
|
);
|
|
6412
6575
|
var ThinkingBlock = ({ content, defaultOpen = false }) => {
|
|
6413
|
-
const [isOpen, setIsOpen] =
|
|
6576
|
+
const [isOpen, setIsOpen] = import_react11.default.useState(defaultOpen);
|
|
6414
6577
|
const isStreaming = content.trim().endsWith("...");
|
|
6415
6578
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
6416
6579
|
"details",
|
|
@@ -6485,7 +6648,7 @@ var ThinkingBlock = ({ content, defaultOpen = false }) => {
|
|
|
6485
6648
|
);
|
|
6486
6649
|
};
|
|
6487
6650
|
var CodeBlock = ({ language, code }) => {
|
|
6488
|
-
const [copied, setCopied] =
|
|
6651
|
+
const [copied, setCopied] = import_react11.default.useState(false);
|
|
6489
6652
|
const handleCopy = async () => {
|
|
6490
6653
|
try {
|
|
6491
6654
|
await navigator.clipboard.writeText(code);
|
|
@@ -6568,10 +6731,10 @@ var CodeBlock = ({ language, code }) => {
|
|
|
6568
6731
|
);
|
|
6569
6732
|
};
|
|
6570
6733
|
var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
6571
|
-
const [isHovered, setIsHovered] =
|
|
6572
|
-
const [imgSrc, setImgSrc] =
|
|
6734
|
+
const [isHovered, setIsHovered] = import_react11.default.useState(false);
|
|
6735
|
+
const [imgSrc, setImgSrc] = import_react11.default.useState(src);
|
|
6573
6736
|
const onImageError = useImageError();
|
|
6574
|
-
const handleImageError =
|
|
6737
|
+
const handleImageError = import_react11.default.useCallback(async () => {
|
|
6575
6738
|
if (onImageError) {
|
|
6576
6739
|
const newUrl = await onImageError(imgSrc);
|
|
6577
6740
|
if (newUrl) {
|
|
@@ -6580,8 +6743,8 @@ var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
|
6580
6743
|
}
|
|
6581
6744
|
}
|
|
6582
6745
|
}, [onImageError, imgSrc]);
|
|
6583
|
-
const [copyState, setCopyState] =
|
|
6584
|
-
const imgRef =
|
|
6746
|
+
const [copyState, setCopyState] = import_react11.default.useState("idle");
|
|
6747
|
+
const imgRef = import_react11.default.useRef(null);
|
|
6585
6748
|
const getImageBlob = async () => {
|
|
6586
6749
|
const img = imgRef.current;
|
|
6587
6750
|
if (img && img.complete && img.naturalWidth > 0) {
|
|
@@ -6797,7 +6960,7 @@ var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
|
6797
6960
|
);
|
|
6798
6961
|
};
|
|
6799
6962
|
var ChoiceButtons = ({ choices, title, onChoiceClick }) => {
|
|
6800
|
-
const [hoveredIndex, setHoveredIndex] =
|
|
6963
|
+
const [hoveredIndex, setHoveredIndex] = import_react11.default.useState(null);
|
|
6801
6964
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
6802
6965
|
"div",
|
|
6803
6966
|
{
|
|
@@ -6927,11 +7090,11 @@ var MarkdownRenderer = ({
|
|
|
6927
7090
|
sources,
|
|
6928
7091
|
inline = false
|
|
6929
7092
|
}) => {
|
|
6930
|
-
const inlineRendered = (0,
|
|
7093
|
+
const inlineRendered = (0, import_react11.useMemo)(() => {
|
|
6931
7094
|
if (!inline) return null;
|
|
6932
7095
|
return parseInlineElements(content, "inline-md", sources);
|
|
6933
7096
|
}, [inline, content, sources]);
|
|
6934
|
-
const rendered = (0,
|
|
7097
|
+
const rendered = (0, import_react11.useMemo)(() => {
|
|
6935
7098
|
if (inline) return null;
|
|
6936
7099
|
const elements = [];
|
|
6937
7100
|
let processedContent = content;
|
|
@@ -7084,7 +7247,7 @@ var MarkdownRenderer = ({
|
|
|
7084
7247
|
borderRadius: "0 8px 8px 0",
|
|
7085
7248
|
color: "var(--chatllm-text, #374151)"
|
|
7086
7249
|
},
|
|
7087
|
-
children: blockquoteLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
7250
|
+
children: blockquoteLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react11.default.Fragment, { children: [
|
|
7088
7251
|
parseInlineElements(line, `bq-line-${i}`, sources),
|
|
7089
7252
|
i < blockquoteLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {})
|
|
7090
7253
|
] }, i))
|
|
@@ -7328,7 +7491,7 @@ var MarkdownRenderer = ({
|
|
|
7328
7491
|
};
|
|
7329
7492
|
|
|
7330
7493
|
// src/react/components/ProjectSelector.tsx
|
|
7331
|
-
var
|
|
7494
|
+
var import_react12 = require("react");
|
|
7332
7495
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
7333
7496
|
var ProjectSelector = ({
|
|
7334
7497
|
projects,
|
|
@@ -7337,10 +7500,10 @@ var ProjectSelector = ({
|
|
|
7337
7500
|
onNewProject,
|
|
7338
7501
|
onProjectSettings
|
|
7339
7502
|
}) => {
|
|
7340
|
-
const [isOpen, setIsOpen] = (0,
|
|
7341
|
-
const dropdownRef = (0,
|
|
7503
|
+
const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
|
|
7504
|
+
const dropdownRef = (0, import_react12.useRef)(null);
|
|
7342
7505
|
const currentProject = projects.find((p) => p.id === currentProjectId);
|
|
7343
|
-
(0,
|
|
7506
|
+
(0, import_react12.useEffect)(() => {
|
|
7344
7507
|
const handleClickOutside = (e) => {
|
|
7345
7508
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
7346
7509
|
setIsOpen(false);
|
|
@@ -7574,10 +7737,10 @@ var ChatSidebar = ({
|
|
|
7574
7737
|
isLoading = false
|
|
7575
7738
|
}) => {
|
|
7576
7739
|
const sidebarWidth = typeof widthProp === "number" ? `${widthProp}px` : widthProp || "288px";
|
|
7577
|
-
const [editingId, setEditingId] = (0,
|
|
7578
|
-
const [editingTitle, setEditingTitle] = (0,
|
|
7579
|
-
const inputRef = (0,
|
|
7580
|
-
(0,
|
|
7740
|
+
const [editingId, setEditingId] = (0, import_react13.useState)(null);
|
|
7741
|
+
const [editingTitle, setEditingTitle] = (0, import_react13.useState)("");
|
|
7742
|
+
const inputRef = (0, import_react13.useRef)(null);
|
|
7743
|
+
(0, import_react13.useEffect)(() => {
|
|
7581
7744
|
if (editingId && inputRef.current) {
|
|
7582
7745
|
inputRef.current.focus();
|
|
7583
7746
|
inputRef.current.select();
|
|
@@ -7949,7 +8112,7 @@ var ChatSidebar = ({
|
|
|
7949
8112
|
};
|
|
7950
8113
|
|
|
7951
8114
|
// src/react/components/ChatHeader.tsx
|
|
7952
|
-
var
|
|
8115
|
+
var import_react14 = require("react");
|
|
7953
8116
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
7954
8117
|
var ChatHeader = ({
|
|
7955
8118
|
title,
|
|
@@ -7963,7 +8126,7 @@ var ChatHeader = ({
|
|
|
7963
8126
|
showSettings = true,
|
|
7964
8127
|
renderHeaderExtra
|
|
7965
8128
|
}) => {
|
|
7966
|
-
const [modelDropdownOpen, setModelDropdownOpen] = (0,
|
|
8129
|
+
const [modelDropdownOpen, setModelDropdownOpen] = (0, import_react14.useState)(false);
|
|
7967
8130
|
const currentModel = models.find((m) => m.id === model);
|
|
7968
8131
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
7969
8132
|
"header",
|
|
@@ -8218,7 +8381,7 @@ var ChatHeader = ({
|
|
|
8218
8381
|
};
|
|
8219
8382
|
|
|
8220
8383
|
// src/react/components/ChatInput.tsx
|
|
8221
|
-
var
|
|
8384
|
+
var import_react15 = __toESM(require("react"));
|
|
8222
8385
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
8223
8386
|
var ChatInput = ({
|
|
8224
8387
|
value,
|
|
@@ -8246,20 +8409,20 @@ var ChatInput = ({
|
|
|
8246
8409
|
onDisclaimerClick,
|
|
8247
8410
|
inline = false
|
|
8248
8411
|
}) => {
|
|
8249
|
-
const [mainMenuOpen, setMainMenuOpen] =
|
|
8250
|
-
const textareaRef = (0,
|
|
8251
|
-
const fileInputRef = (0,
|
|
8252
|
-
const [actionMenuOpen, setActionMenuOpen] = (0,
|
|
8253
|
-
const [isDragOver, setIsDragOver] = (0,
|
|
8254
|
-
const mainMenuRef = (0,
|
|
8255
|
-
const actionMenuRef = (0,
|
|
8256
|
-
(0,
|
|
8412
|
+
const [mainMenuOpen, setMainMenuOpen] = import_react15.default.useState(false);
|
|
8413
|
+
const textareaRef = (0, import_react15.useRef)(null);
|
|
8414
|
+
const fileInputRef = (0, import_react15.useRef)(null);
|
|
8415
|
+
const [actionMenuOpen, setActionMenuOpen] = (0, import_react15.useState)(false);
|
|
8416
|
+
const [isDragOver, setIsDragOver] = (0, import_react15.useState)(false);
|
|
8417
|
+
const mainMenuRef = (0, import_react15.useRef)(null);
|
|
8418
|
+
const actionMenuRef = (0, import_react15.useRef)(null);
|
|
8419
|
+
(0, import_react15.useEffect)(() => {
|
|
8257
8420
|
if (textareaRef.current) {
|
|
8258
8421
|
textareaRef.current.style.height = "auto";
|
|
8259
8422
|
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
|
|
8260
8423
|
}
|
|
8261
8424
|
}, [value]);
|
|
8262
|
-
(0,
|
|
8425
|
+
(0, import_react15.useEffect)(() => {
|
|
8263
8426
|
const handleClickOutside = (event) => {
|
|
8264
8427
|
if (mainMenuRef.current && !mainMenuRef.current.contains(event.target)) {
|
|
8265
8428
|
setMainMenuOpen(false);
|
|
@@ -8981,13 +9144,13 @@ var iconButtonStyle = {
|
|
|
8981
9144
|
};
|
|
8982
9145
|
|
|
8983
9146
|
// src/react/components/MessageList.tsx
|
|
8984
|
-
var
|
|
9147
|
+
var import_react23 = __toESM(require("react"));
|
|
8985
9148
|
|
|
8986
9149
|
// src/react/components/MessageBubble.tsx
|
|
8987
|
-
var
|
|
9150
|
+
var import_react22 = require("react");
|
|
8988
9151
|
|
|
8989
9152
|
// src/react/components/DeepResearchProgressUI.tsx
|
|
8990
|
-
var
|
|
9153
|
+
var import_react16 = __toESM(require("react"));
|
|
8991
9154
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
8992
9155
|
var StatusIcon = ({ status }) => {
|
|
8993
9156
|
switch (status) {
|
|
@@ -9051,7 +9214,7 @@ var PhaseProgress = ({ phase }) => {
|
|
|
9051
9214
|
gap: "8px",
|
|
9052
9215
|
marginBottom: "16px"
|
|
9053
9216
|
},
|
|
9054
|
-
children: phases.slice(0, 4).map((p, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
9217
|
+
children: phases.slice(0, 4).map((p, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react16.default.Fragment, { children: [
|
|
9055
9218
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
9056
9219
|
"div",
|
|
9057
9220
|
{
|
|
@@ -9270,7 +9433,7 @@ var DeepResearchProgressUI = ({ progress }) => {
|
|
|
9270
9433
|
};
|
|
9271
9434
|
|
|
9272
9435
|
// src/react/components/PollCard.tsx
|
|
9273
|
-
var
|
|
9436
|
+
var import_react17 = require("react");
|
|
9274
9437
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
9275
9438
|
var renderInlineMarkdown = (text) => {
|
|
9276
9439
|
const parts = [];
|
|
@@ -9309,12 +9472,12 @@ var PollCard = ({
|
|
|
9309
9472
|
onSubmit,
|
|
9310
9473
|
onSkip
|
|
9311
9474
|
}) => {
|
|
9312
|
-
const [activeTab, setActiveTab] = (0,
|
|
9313
|
-
const [selections, setSelections] = (0,
|
|
9314
|
-
const [otherTexts, setOtherTexts] = (0,
|
|
9315
|
-
const [otherSelected, setOtherSelected] = (0,
|
|
9475
|
+
const [activeTab, setActiveTab] = (0, import_react17.useState)(0);
|
|
9476
|
+
const [selections, setSelections] = (0, import_react17.useState)({});
|
|
9477
|
+
const [otherTexts, setOtherTexts] = (0, import_react17.useState)({});
|
|
9478
|
+
const [otherSelected, setOtherSelected] = (0, import_react17.useState)({});
|
|
9316
9479
|
const currentQuestion = questions[activeTab];
|
|
9317
|
-
const handleOptionToggle = (0,
|
|
9480
|
+
const handleOptionToggle = (0, import_react17.useCallback)(
|
|
9318
9481
|
(questionId, optionId, multiSelect) => {
|
|
9319
9482
|
setSelections((prev) => {
|
|
9320
9483
|
const current = prev[questionId] || /* @__PURE__ */ new Set();
|
|
@@ -9336,7 +9499,7 @@ var PollCard = ({
|
|
|
9336
9499
|
},
|
|
9337
9500
|
[]
|
|
9338
9501
|
);
|
|
9339
|
-
const handleOtherToggle = (0,
|
|
9502
|
+
const handleOtherToggle = (0, import_react17.useCallback)((questionId, multiSelect) => {
|
|
9340
9503
|
if (multiSelect) {
|
|
9341
9504
|
setOtherSelected((prev) => ({ ...prev, [questionId]: !prev[questionId] }));
|
|
9342
9505
|
} else {
|
|
@@ -9344,7 +9507,7 @@ var PollCard = ({
|
|
|
9344
9507
|
setOtherSelected((prev) => ({ ...prev, [questionId]: true }));
|
|
9345
9508
|
}
|
|
9346
9509
|
}, []);
|
|
9347
|
-
const handleSubmit = (0,
|
|
9510
|
+
const handleSubmit = (0, import_react17.useCallback)(() => {
|
|
9348
9511
|
const responses = questions.map((q) => {
|
|
9349
9512
|
const selected = selections[q.id] || /* @__PURE__ */ new Set();
|
|
9350
9513
|
const other = otherSelected[q.id] && otherTexts[q.id]?.trim();
|
|
@@ -9357,7 +9520,7 @@ var PollCard = ({
|
|
|
9357
9520
|
});
|
|
9358
9521
|
onSubmit(responses);
|
|
9359
9522
|
}, [questions, selections, otherSelected, otherTexts, onSubmit]);
|
|
9360
|
-
const handleSkip = (0,
|
|
9523
|
+
const handleSkip = (0, import_react17.useCallback)(() => {
|
|
9361
9524
|
const responses = questions.map((q) => ({
|
|
9362
9525
|
questionId: q.id,
|
|
9363
9526
|
selectedOptions: [],
|
|
@@ -9366,7 +9529,7 @@ var PollCard = ({
|
|
|
9366
9529
|
onSubmit(responses);
|
|
9367
9530
|
onSkip?.();
|
|
9368
9531
|
}, [questions, onSubmit, onSkip]);
|
|
9369
|
-
(0,
|
|
9532
|
+
(0, import_react17.useEffect)(() => {
|
|
9370
9533
|
if (typeof window === "undefined") return;
|
|
9371
9534
|
const handleKeyDown = (e) => {
|
|
9372
9535
|
if (e.key !== "Escape") return;
|
|
@@ -9385,9 +9548,9 @@ var PollCard = ({
|
|
|
9385
9548
|
const currentHasSelection = getSelectionCount(currentQuestion.id) > 0;
|
|
9386
9549
|
const isLastTab = activeTab === questions.length - 1;
|
|
9387
9550
|
const totalSelected = questions.reduce((sum, q) => sum + getSelectionCount(q.id), 0);
|
|
9388
|
-
const [visibleCount, setVisibleCount] = (0,
|
|
9389
|
-
const [cardVisible, setCardVisible] = (0,
|
|
9390
|
-
(0,
|
|
9551
|
+
const [visibleCount, setVisibleCount] = (0, import_react17.useState)(0);
|
|
9552
|
+
const [cardVisible, setCardVisible] = (0, import_react17.useState)(false);
|
|
9553
|
+
(0, import_react17.useEffect)(() => {
|
|
9391
9554
|
const fadeTimer = setTimeout(() => setCardVisible(true), 50);
|
|
9392
9555
|
const totalItems = currentQuestion.options.length + 1;
|
|
9393
9556
|
const timers = [fadeTimer];
|
|
@@ -9396,7 +9559,7 @@ var PollCard = ({
|
|
|
9396
9559
|
}
|
|
9397
9560
|
return () => timers.forEach(clearTimeout);
|
|
9398
9561
|
}, [currentQuestion.options.length]);
|
|
9399
|
-
(0,
|
|
9562
|
+
(0, import_react17.useEffect)(() => {
|
|
9400
9563
|
setVisibleCount(0);
|
|
9401
9564
|
const totalItems = currentQuestion.options.length + 1;
|
|
9402
9565
|
const timers = [];
|
|
@@ -9405,7 +9568,7 @@ var PollCard = ({
|
|
|
9405
9568
|
}
|
|
9406
9569
|
return () => timers.forEach(clearTimeout);
|
|
9407
9570
|
}, [activeTab, currentQuestion.options.length]);
|
|
9408
|
-
const handleNext = (0,
|
|
9571
|
+
const handleNext = (0, import_react17.useCallback)(() => {
|
|
9409
9572
|
if (!isLastTab) {
|
|
9410
9573
|
setActiveTab((prev) => prev + 1);
|
|
9411
9574
|
} else {
|
|
@@ -9823,15 +9986,15 @@ var SkillProgressUI = ({
|
|
|
9823
9986
|
};
|
|
9824
9987
|
|
|
9825
9988
|
// src/react/components/ImageContentCard.tsx
|
|
9826
|
-
var
|
|
9989
|
+
var import_react18 = require("react");
|
|
9827
9990
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
9828
9991
|
var ImageContentCard = ({ part }) => {
|
|
9829
|
-
const [isExpanded, setIsExpanded] = (0,
|
|
9830
|
-
const [isLoaded, setIsLoaded] = (0,
|
|
9831
|
-
const [hasError, setHasError] = (0,
|
|
9832
|
-
const [imgSrc, setImgSrc] = (0,
|
|
9992
|
+
const [isExpanded, setIsExpanded] = (0, import_react18.useState)(false);
|
|
9993
|
+
const [isLoaded, setIsLoaded] = (0, import_react18.useState)(false);
|
|
9994
|
+
const [hasError, setHasError] = (0, import_react18.useState)(false);
|
|
9995
|
+
const [imgSrc, setImgSrc] = (0, import_react18.useState)(part.url);
|
|
9833
9996
|
const onImageError = useImageError();
|
|
9834
|
-
const handleImageError = (0,
|
|
9997
|
+
const handleImageError = (0, import_react18.useCallback)(async () => {
|
|
9835
9998
|
if (onImageError) {
|
|
9836
9999
|
const newUrl = await onImageError(imgSrc, part.fileName);
|
|
9837
10000
|
if (newUrl) {
|
|
@@ -10044,7 +10207,7 @@ var FileContentCard = ({ part }) => {
|
|
|
10044
10207
|
};
|
|
10045
10208
|
|
|
10046
10209
|
// src/react/components/ToolStatusCard.tsx
|
|
10047
|
-
var
|
|
10210
|
+
var import_react19 = require("react");
|
|
10048
10211
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
10049
10212
|
var mapIcon2 = (icon) => {
|
|
10050
10213
|
const iconMap = {
|
|
@@ -10077,7 +10240,7 @@ var ToolStatusCard = ({
|
|
|
10077
10240
|
sources,
|
|
10078
10241
|
errorMessage
|
|
10079
10242
|
}) => {
|
|
10080
|
-
const [isExpanded, setIsExpanded] = (0,
|
|
10243
|
+
const [isExpanded, setIsExpanded] = (0, import_react19.useState)(false);
|
|
10081
10244
|
const displayLabel = label || getDefaultLabel(toolName);
|
|
10082
10245
|
const statusText = getStatusSuffix(displayLabel, status);
|
|
10083
10246
|
const hasSources = sources && sources.length > 0;
|
|
@@ -10208,7 +10371,7 @@ var ToolStatusCard = ({
|
|
|
10208
10371
|
};
|
|
10209
10372
|
|
|
10210
10373
|
// src/react/components/ArtifactCard.tsx
|
|
10211
|
-
var
|
|
10374
|
+
var import_react20 = require("react");
|
|
10212
10375
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
10213
10376
|
var getThemeStyles = () => {
|
|
10214
10377
|
const root = document.documentElement;
|
|
@@ -10249,10 +10412,10 @@ var getAutoResizeScript = (id) => `
|
|
|
10249
10412
|
});
|
|
10250
10413
|
</script>`;
|
|
10251
10414
|
var HtmlArtifact = ({ code }) => {
|
|
10252
|
-
const iframeRef = (0,
|
|
10253
|
-
const [height, setHeight] = (0,
|
|
10254
|
-
const artifactId = (0,
|
|
10255
|
-
(0,
|
|
10415
|
+
const iframeRef = (0, import_react20.useRef)(null);
|
|
10416
|
+
const [height, setHeight] = (0, import_react20.useState)(200);
|
|
10417
|
+
const artifactId = (0, import_react20.useRef)(`artifact-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`);
|
|
10418
|
+
(0, import_react20.useEffect)(() => {
|
|
10256
10419
|
const id = artifactId.current;
|
|
10257
10420
|
const handleMessage = (e) => {
|
|
10258
10421
|
if (e.data?.type === "artifact-resize" && e.data.id === id && typeof e.data.height === "number") {
|
|
@@ -10298,10 +10461,10 @@ var SvgArtifact = ({ code }) => {
|
|
|
10298
10461
|
);
|
|
10299
10462
|
};
|
|
10300
10463
|
var MermaidArtifact = ({ code }) => {
|
|
10301
|
-
const containerRef = (0,
|
|
10302
|
-
const [svgHtml, setSvgHtml] = (0,
|
|
10303
|
-
const [error, setError] = (0,
|
|
10304
|
-
const renderMermaid = (0,
|
|
10464
|
+
const containerRef = (0, import_react20.useRef)(null);
|
|
10465
|
+
const [svgHtml, setSvgHtml] = (0, import_react20.useState)(null);
|
|
10466
|
+
const [error, setError] = (0, import_react20.useState)(null);
|
|
10467
|
+
const renderMermaid = (0, import_react20.useCallback)(async () => {
|
|
10305
10468
|
try {
|
|
10306
10469
|
let mermaid;
|
|
10307
10470
|
try {
|
|
@@ -10317,7 +10480,7 @@ var MermaidArtifact = ({ code }) => {
|
|
|
10317
10480
|
setError(e instanceof Error ? e.message : "Mermaid \uB80C\uB354\uB9C1 \uC2E4\uD328");
|
|
10318
10481
|
}
|
|
10319
10482
|
}, [code]);
|
|
10320
|
-
(0,
|
|
10483
|
+
(0, import_react20.useEffect)(() => {
|
|
10321
10484
|
renderMermaid();
|
|
10322
10485
|
}, [renderMermaid]);
|
|
10323
10486
|
if (error) {
|
|
@@ -10393,9 +10556,9 @@ var svgToPngBlob = (svgString, scale = 2) => {
|
|
|
10393
10556
|
});
|
|
10394
10557
|
};
|
|
10395
10558
|
var ArtifactActions = ({ code, language, containerRef }) => {
|
|
10396
|
-
const [showCode, setShowCode] = (0,
|
|
10397
|
-
const [copied, setCopied] = (0,
|
|
10398
|
-
const [saving, setSaving] = (0,
|
|
10559
|
+
const [showCode, setShowCode] = (0, import_react20.useState)(false);
|
|
10560
|
+
const [copied, setCopied] = (0, import_react20.useState)(false);
|
|
10561
|
+
const [saving, setSaving] = (0, import_react20.useState)(false);
|
|
10399
10562
|
const handleCopy = async () => {
|
|
10400
10563
|
try {
|
|
10401
10564
|
await navigator.clipboard.writeText(code);
|
|
@@ -10560,7 +10723,7 @@ var ArtifactActions = ({ code, language, containerRef }) => {
|
|
|
10560
10723
|
] });
|
|
10561
10724
|
};
|
|
10562
10725
|
var ArtifactCard = ({ part, index = 0 }) => {
|
|
10563
|
-
const contentRef = (0,
|
|
10726
|
+
const contentRef = (0, import_react20.useRef)(null);
|
|
10564
10727
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
10565
10728
|
"div",
|
|
10566
10729
|
{
|
|
@@ -10732,7 +10895,7 @@ var ContentPartRenderer = ({
|
|
|
10732
10895
|
};
|
|
10733
10896
|
|
|
10734
10897
|
// src/react/components/ChecklistCard.tsx
|
|
10735
|
-
var
|
|
10898
|
+
var import_react21 = require("react");
|
|
10736
10899
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
10737
10900
|
var ChecklistCard = ({
|
|
10738
10901
|
items,
|
|
@@ -10742,8 +10905,8 @@ var ChecklistCard = ({
|
|
|
10742
10905
|
onSkipStep,
|
|
10743
10906
|
onStart
|
|
10744
10907
|
}) => {
|
|
10745
|
-
const [expandedItems, setExpandedItems] = (0,
|
|
10746
|
-
const { doneCount, isRunning, hasError, isWaiting } = (0,
|
|
10908
|
+
const [expandedItems, setExpandedItems] = (0, import_react21.useState)(/* @__PURE__ */ new Set());
|
|
10909
|
+
const { doneCount, isRunning, hasError, isWaiting } = (0, import_react21.useMemo)(() => {
|
|
10747
10910
|
const done = items.filter((it) => it.status === "done").length;
|
|
10748
10911
|
const running = items.some((it) => it.status === "in_progress");
|
|
10749
10912
|
const waiting = items.every((it) => it.status === "pending");
|
|
@@ -11240,9 +11403,9 @@ var MessageBubble = ({
|
|
|
11240
11403
|
onToggleChecklistPanel,
|
|
11241
11404
|
onChecklistStart
|
|
11242
11405
|
}) => {
|
|
11243
|
-
const [showActions, setShowActions] = (0,
|
|
11244
|
-
const [showModelMenu, setShowModelMenu] = (0,
|
|
11245
|
-
const [stepExpanded, setStepExpanded] = (0,
|
|
11406
|
+
const [showActions, setShowActions] = (0, import_react22.useState)(false);
|
|
11407
|
+
const [showModelMenu, setShowModelMenu] = (0, import_react22.useState)(false);
|
|
11408
|
+
const [stepExpanded, setStepExpanded] = (0, import_react22.useState)(false);
|
|
11246
11409
|
const isUser = message.role === "user";
|
|
11247
11410
|
const isAssistant = message.role === "assistant";
|
|
11248
11411
|
const relevantAlternatives = isUser ? alternatives : message.alternatives;
|
|
@@ -11993,14 +12156,14 @@ var MessageList = ({
|
|
|
11993
12156
|
onExport,
|
|
11994
12157
|
onToggleChecklistPanel
|
|
11995
12158
|
}) => {
|
|
11996
|
-
const messagesEndRef = (0,
|
|
11997
|
-
const containerRef = (0,
|
|
11998
|
-
const [selectedText, setSelectedText] = (0,
|
|
11999
|
-
const [selectionPosition, setSelectionPosition] = (0,
|
|
12000
|
-
const [showScrollButton, setShowScrollButton] = (0,
|
|
12001
|
-
const userScrollLockRef = (0,
|
|
12159
|
+
const messagesEndRef = (0, import_react23.useRef)(null);
|
|
12160
|
+
const containerRef = (0, import_react23.useRef)(null);
|
|
12161
|
+
const [selectedText, setSelectedText] = (0, import_react23.useState)("");
|
|
12162
|
+
const [selectionPosition, setSelectionPosition] = (0, import_react23.useState)(null);
|
|
12163
|
+
const [showScrollButton, setShowScrollButton] = (0, import_react23.useState)(false);
|
|
12164
|
+
const userScrollLockRef = (0, import_react23.useRef)(false);
|
|
12002
12165
|
const SCROLL_THRESHOLD = 100;
|
|
12003
|
-
(0,
|
|
12166
|
+
(0, import_react23.useEffect)(() => {
|
|
12004
12167
|
const container = containerRef.current;
|
|
12005
12168
|
if (!container) return;
|
|
12006
12169
|
const handleWheel = (e) => {
|
|
@@ -12037,7 +12200,7 @@ var MessageList = ({
|
|
|
12037
12200
|
container.removeEventListener("touchmove", handleTouchMove);
|
|
12038
12201
|
};
|
|
12039
12202
|
}, []);
|
|
12040
|
-
const handleScroll = (0,
|
|
12203
|
+
const handleScroll = (0, import_react23.useCallback)(() => {
|
|
12041
12204
|
if (!containerRef.current) return;
|
|
12042
12205
|
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
|
12043
12206
|
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
@@ -12046,14 +12209,14 @@ var MessageList = ({
|
|
|
12046
12209
|
setShowScrollButton(false);
|
|
12047
12210
|
}
|
|
12048
12211
|
}, []);
|
|
12049
|
-
(0,
|
|
12212
|
+
(0, import_react23.useEffect)(() => {
|
|
12050
12213
|
if (userScrollLockRef.current) return;
|
|
12051
12214
|
containerRef.current?.scrollTo({
|
|
12052
12215
|
top: containerRef.current.scrollHeight,
|
|
12053
12216
|
behavior: "smooth"
|
|
12054
12217
|
});
|
|
12055
12218
|
}, [messages]);
|
|
12056
|
-
const scrollToBottom = (0,
|
|
12219
|
+
const scrollToBottom = (0, import_react23.useCallback)(() => {
|
|
12057
12220
|
userScrollLockRef.current = false;
|
|
12058
12221
|
setShowScrollButton(false);
|
|
12059
12222
|
containerRef.current?.scrollTo({
|
|
@@ -12061,7 +12224,7 @@ var MessageList = ({
|
|
|
12061
12224
|
behavior: "smooth"
|
|
12062
12225
|
});
|
|
12063
12226
|
}, []);
|
|
12064
|
-
const handleMouseUp = (0,
|
|
12227
|
+
const handleMouseUp = (0, import_react23.useCallback)(() => {
|
|
12065
12228
|
const selection = typeof window !== "undefined" ? window.getSelection() : null;
|
|
12066
12229
|
const text = selection?.toString().trim();
|
|
12067
12230
|
if (text && text.length > 0) {
|
|
@@ -12162,7 +12325,7 @@ var MessageList = ({
|
|
|
12162
12325
|
},
|
|
12163
12326
|
message.id
|
|
12164
12327
|
);
|
|
12165
|
-
return renderMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
12328
|
+
return renderMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react23.default.Fragment, { children: renderMessage(message, bubbleElement) }, message.id) : bubbleElement;
|
|
12166
12329
|
}),
|
|
12167
12330
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: messagesEndRef })
|
|
12168
12331
|
]
|
|
@@ -12256,7 +12419,7 @@ var MessageList = ({
|
|
|
12256
12419
|
};
|
|
12257
12420
|
|
|
12258
12421
|
// src/react/components/SettingsModal.tsx
|
|
12259
|
-
var
|
|
12422
|
+
var import_react24 = require("react");
|
|
12260
12423
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
12261
12424
|
var DEFAULT_IMPORT_MEMORY_PROMPT = `Export all of my stored memories and any context you've learned about me from past conversations. Preserve my words verbatim where possible, especially for instructions and preferences.
|
|
12262
12425
|
|
|
@@ -12321,9 +12484,9 @@ var SettingsModal = ({
|
|
|
12321
12484
|
onImportMemory,
|
|
12322
12485
|
importMemoryPrompt
|
|
12323
12486
|
}) => {
|
|
12324
|
-
const [activeTab, setActiveTab] = (0,
|
|
12325
|
-
const [localApiKey, setLocalApiKey] = (0,
|
|
12326
|
-
(0,
|
|
12487
|
+
const [activeTab, setActiveTab] = (0, import_react24.useState)("general");
|
|
12488
|
+
const [localApiKey, setLocalApiKey] = (0, import_react24.useState)(apiKey);
|
|
12489
|
+
(0, import_react24.useEffect)(() => {
|
|
12327
12490
|
setLocalApiKey(apiKey);
|
|
12328
12491
|
}, [apiKey]);
|
|
12329
12492
|
if (!isOpen) return null;
|
|
@@ -12799,11 +12962,11 @@ var memoryCategoryColors = {
|
|
|
12799
12962
|
preference: "#8b5cf6"
|
|
12800
12963
|
};
|
|
12801
12964
|
var MemoryTabContent = ({ items, contextSummary, onDelete, onClearAll, title = "AI \uBA54\uBAA8\uB9AC", emptyMessage = "\uC800\uC7A5\uB41C \uBA54\uBAA8\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4", emptyDescription = "\uB300\uD654\uAC00 \uC9C4\uD589\uB418\uBA74 AI\uAC00 \uC790\uB3D9\uC73C\uB85C \uC815\uBCF4\uB97C \uD559\uC2B5\uD569\uB2C8\uB2E4", onImportMemory, importMemoryPrompt }) => {
|
|
12802
|
-
const [activeFilter, setActiveFilter] = (0,
|
|
12803
|
-
const [expandedId, setExpandedId] = (0,
|
|
12804
|
-
const [importModalOpen, setImportModalOpen] = (0,
|
|
12805
|
-
const [importText, setImportText] = (0,
|
|
12806
|
-
const [promptCopied, setPromptCopied] = (0,
|
|
12965
|
+
const [activeFilter, setActiveFilter] = (0, import_react24.useState)("all");
|
|
12966
|
+
const [expandedId, setExpandedId] = (0, import_react24.useState)(null);
|
|
12967
|
+
const [importModalOpen, setImportModalOpen] = (0, import_react24.useState)(false);
|
|
12968
|
+
const [importText, setImportText] = (0, import_react24.useState)("");
|
|
12969
|
+
const [promptCopied, setPromptCopied] = (0, import_react24.useState)(false);
|
|
12807
12970
|
const filteredItems = activeFilter === "all" ? items : items.filter((item) => item.category === activeFilter);
|
|
12808
12971
|
const formatDate = (timestamp) => {
|
|
12809
12972
|
const date = new Date(timestamp);
|
|
@@ -13260,7 +13423,7 @@ var MemoryTabContent = ({ items, contextSummary, onDelete, onClearAll, title = "
|
|
|
13260
13423
|
};
|
|
13261
13424
|
|
|
13262
13425
|
// src/react/components/ProjectSettingsModal.tsx
|
|
13263
|
-
var
|
|
13426
|
+
var import_react25 = require("react");
|
|
13264
13427
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
13265
13428
|
var COLOR_PRESETS = [
|
|
13266
13429
|
"#3584FA",
|
|
@@ -13281,12 +13444,12 @@ var ProjectSettingsModal = ({
|
|
|
13281
13444
|
onDeleteFile,
|
|
13282
13445
|
onDeleteProject
|
|
13283
13446
|
}) => {
|
|
13284
|
-
const [activeTab, setActiveTab] = (0,
|
|
13285
|
-
const [title, setTitle] = (0,
|
|
13286
|
-
const [description, setDescription] = (0,
|
|
13287
|
-
const [instructions, setInstructions] = (0,
|
|
13288
|
-
const [color, setColor] = (0,
|
|
13289
|
-
(0,
|
|
13447
|
+
const [activeTab, setActiveTab] = (0, import_react25.useState)("general");
|
|
13448
|
+
const [title, setTitle] = (0, import_react25.useState)("");
|
|
13449
|
+
const [description, setDescription] = (0, import_react25.useState)("");
|
|
13450
|
+
const [instructions, setInstructions] = (0, import_react25.useState)("");
|
|
13451
|
+
const [color, setColor] = (0, import_react25.useState)("#3584FA");
|
|
13452
|
+
(0, import_react25.useEffect)(() => {
|
|
13290
13453
|
if (project) {
|
|
13291
13454
|
setTitle(project.title);
|
|
13292
13455
|
setDescription(project.description || "");
|
|
@@ -14751,28 +14914,28 @@ var ChatUIView = ({
|
|
|
14751
14914
|
handleChecklistSkip,
|
|
14752
14915
|
activeChecklistMessage
|
|
14753
14916
|
} = state;
|
|
14754
|
-
const [disclaimerOpen, setDisclaimerOpen] =
|
|
14755
|
-
const [isMobile, setIsMobile] =
|
|
14756
|
-
|
|
14917
|
+
const [disclaimerOpen, setDisclaimerOpen] = import_react26.default.useState(false);
|
|
14918
|
+
const [isMobile, setIsMobile] = import_react26.default.useState(false);
|
|
14919
|
+
import_react26.default.useEffect(() => {
|
|
14757
14920
|
if (typeof window === "undefined") return;
|
|
14758
14921
|
const check = () => setIsMobile(window.innerWidth < 768);
|
|
14759
14922
|
check();
|
|
14760
14923
|
window.addEventListener("resize", check);
|
|
14761
14924
|
return () => window.removeEventListener("resize", check);
|
|
14762
14925
|
}, []);
|
|
14763
|
-
const [checklistPanelDismissed, setChecklistPanelDismissed] =
|
|
14926
|
+
const [checklistPanelDismissed, setChecklistPanelDismissed] = import_react26.default.useState(false);
|
|
14764
14927
|
const isChecklistPanelOpen = !!activeChecklistMessage && !checklistPanelDismissed;
|
|
14765
|
-
const prevChecklistIdRef =
|
|
14766
|
-
|
|
14928
|
+
const prevChecklistIdRef = import_react26.default.useRef(void 0);
|
|
14929
|
+
import_react26.default.useEffect(() => {
|
|
14767
14930
|
const currentId = activeChecklistMessage?.checklistBlock?.id;
|
|
14768
14931
|
if (currentId && currentId !== prevChecklistIdRef.current && !activeChecklistMessage?.checklistBlock?.completed) {
|
|
14769
14932
|
setChecklistPanelDismissed(false);
|
|
14770
14933
|
}
|
|
14771
14934
|
prevChecklistIdRef.current = currentId;
|
|
14772
14935
|
}, [activeChecklistMessage?.checklistBlock?.id, activeChecklistMessage?.checklistBlock?.completed]);
|
|
14773
|
-
const [welcomeExiting, setWelcomeExiting] =
|
|
14774
|
-
const prevMessageCountRef =
|
|
14775
|
-
|
|
14936
|
+
const [welcomeExiting, setWelcomeExiting] = import_react26.default.useState(false);
|
|
14937
|
+
const prevMessageCountRef = import_react26.default.useRef(messages.length);
|
|
14938
|
+
import_react26.default.useEffect(() => {
|
|
14776
14939
|
let timer;
|
|
14777
14940
|
if (prevMessageCountRef.current === 0 && messages.length > 0) {
|
|
14778
14941
|
setWelcomeExiting(true);
|
|
@@ -14796,7 +14959,7 @@ var ChatUIView = ({
|
|
|
14796
14959
|
const handleChoiceClick = (choice) => {
|
|
14797
14960
|
setInput(choice.text);
|
|
14798
14961
|
};
|
|
14799
|
-
const memoryItems =
|
|
14962
|
+
const memoryItems = import_react26.default.useMemo(() => {
|
|
14800
14963
|
if (!globalMemory?.state.entries) return [];
|
|
14801
14964
|
const items = [];
|
|
14802
14965
|
for (const [key, entry] of globalMemory.state.entries) {
|
|
@@ -14810,7 +14973,7 @@ var ChatUIView = ({
|
|
|
14810
14973
|
}
|
|
14811
14974
|
return items;
|
|
14812
14975
|
}, [globalMemory?.state.entries]);
|
|
14813
|
-
const projectMemoryItems =
|
|
14976
|
+
const projectMemoryItems = import_react26.default.useMemo(() => {
|
|
14814
14977
|
if (!projectMemory?.state.entries) return [];
|
|
14815
14978
|
const items = [];
|
|
14816
14979
|
for (const [key, entry] of projectMemory.state.entries) {
|
|
@@ -15332,13 +15495,13 @@ var ChatUI = (props) => {
|
|
|
15332
15495
|
};
|
|
15333
15496
|
|
|
15334
15497
|
// src/react/ChatFloatingWidget.tsx
|
|
15335
|
-
var
|
|
15498
|
+
var import_react34 = require("react");
|
|
15336
15499
|
|
|
15337
15500
|
// src/react/hooks/useFloatingWidget.ts
|
|
15338
|
-
var
|
|
15501
|
+
var import_react28 = require("react");
|
|
15339
15502
|
|
|
15340
15503
|
// src/react/hooks/useDragResize.ts
|
|
15341
|
-
var
|
|
15504
|
+
var import_react27 = require("react");
|
|
15342
15505
|
var DRAG_THRESHOLD = 5;
|
|
15343
15506
|
var FAB_SIZE = 56;
|
|
15344
15507
|
var EDGE_MARGIN = 24;
|
|
@@ -15406,8 +15569,8 @@ var useDragResize = (options) => {
|
|
|
15406
15569
|
maxWidth = DEFAULT_MAX_WIDTH,
|
|
15407
15570
|
minHeight = DEFAULT_MIN_HEIGHT
|
|
15408
15571
|
} = options;
|
|
15409
|
-
const [isMobile, setIsMobile] = (0,
|
|
15410
|
-
(0,
|
|
15572
|
+
const [isMobile, setIsMobile] = (0, import_react27.useState)(false);
|
|
15573
|
+
(0, import_react27.useEffect)(() => {
|
|
15411
15574
|
if (typeof window === "undefined") return;
|
|
15412
15575
|
const mql = window.matchMedia("(max-width: 767px)");
|
|
15413
15576
|
setIsMobile(mql.matches);
|
|
@@ -15422,19 +15585,19 @@ var useDragResize = (options) => {
|
|
|
15422
15585
|
panelWidth: initialWidth,
|
|
15423
15586
|
panelHeight: initialHeight
|
|
15424
15587
|
});
|
|
15425
|
-
const [fabPos, setFabPos] = (0,
|
|
15426
|
-
const [panelSize, setPanelSize] = (0,
|
|
15427
|
-
const [isDragging, setIsDragging] = (0,
|
|
15428
|
-
const [isResizing, setIsResizing] = (0,
|
|
15429
|
-
const [isDizzy, setIsDizzy] = (0,
|
|
15430
|
-
const [viewport, setViewport] = (0,
|
|
15588
|
+
const [fabPos, setFabPos] = (0, import_react27.useState)({ x: persisted.fabX, y: persisted.fabY });
|
|
15589
|
+
const [panelSize, setPanelSize] = (0, import_react27.useState)({ width: persisted.panelWidth, height: persisted.panelHeight });
|
|
15590
|
+
const [isDragging, setIsDragging] = (0, import_react27.useState)(false);
|
|
15591
|
+
const [isResizing, setIsResizing] = (0, import_react27.useState)(false);
|
|
15592
|
+
const [isDizzy, setIsDizzy] = (0, import_react27.useState)(false);
|
|
15593
|
+
const [viewport, setViewport] = (0, import_react27.useState)(() => ({
|
|
15431
15594
|
width: typeof window !== "undefined" ? window.innerWidth : 1024,
|
|
15432
15595
|
height: typeof window !== "undefined" ? window.innerHeight : 768
|
|
15433
15596
|
}));
|
|
15434
15597
|
const vw = viewport.width;
|
|
15435
15598
|
const vh = viewport.height;
|
|
15436
|
-
const initializedRef = (0,
|
|
15437
|
-
(0,
|
|
15599
|
+
const initializedRef = (0, import_react27.useRef)(false);
|
|
15600
|
+
(0, import_react27.useEffect)(() => {
|
|
15438
15601
|
if (initializedRef.current || typeof window === "undefined") return;
|
|
15439
15602
|
initializedRef.current = true;
|
|
15440
15603
|
const correctFab = getInitialFabPosition(initialPosition);
|
|
@@ -15447,40 +15610,40 @@ var useDragResize = (options) => {
|
|
|
15447
15610
|
setFabPos({ x: loaded.fabX, y: loaded.fabY });
|
|
15448
15611
|
setPanelSize({ width: loaded.panelWidth, height: loaded.panelHeight });
|
|
15449
15612
|
}, [initialPosition, storageKey, initialWidth, initialHeight]);
|
|
15450
|
-
const dragStartRef = (0,
|
|
15451
|
-
const isDraggingRef = (0,
|
|
15452
|
-
const wasDragRef = (0,
|
|
15453
|
-
const userDraggedRef = (0,
|
|
15454
|
-
const dragDeltaRef = (0,
|
|
15455
|
-
const fabElRef = (0,
|
|
15456
|
-
const dragCleanupRef = (0,
|
|
15457
|
-
const snapTimerRef = (0,
|
|
15458
|
-
const prevDragRef = (0,
|
|
15459
|
-
const shakeScoreRef = (0,
|
|
15460
|
-
const prevVelocityRef = (0,
|
|
15461
|
-
const isDizzyRef = (0,
|
|
15462
|
-
const dizzyTimerRef = (0,
|
|
15463
|
-
const lastVelocityRef = (0,
|
|
15464
|
-
const dragRafRef = (0,
|
|
15465
|
-
const animCleanupRef = (0,
|
|
15466
|
-
const snapEnabledRef = (0,
|
|
15467
|
-
const storageKeyRef = (0,
|
|
15468
|
-
const panelSizeRef = (0,
|
|
15469
|
-
const fabPosRef = (0,
|
|
15470
|
-
(0,
|
|
15613
|
+
const dragStartRef = (0, import_react27.useRef)(null);
|
|
15614
|
+
const isDraggingRef = (0, import_react27.useRef)(false);
|
|
15615
|
+
const wasDragRef = (0, import_react27.useRef)(false);
|
|
15616
|
+
const userDraggedRef = (0, import_react27.useRef)(false);
|
|
15617
|
+
const dragDeltaRef = (0, import_react27.useRef)({ dx: 0, dy: 0 });
|
|
15618
|
+
const fabElRef = (0, import_react27.useRef)(null);
|
|
15619
|
+
const dragCleanupRef = (0, import_react27.useRef)(null);
|
|
15620
|
+
const snapTimerRef = (0, import_react27.useRef)(null);
|
|
15621
|
+
const prevDragRef = (0, import_react27.useRef)(null);
|
|
15622
|
+
const shakeScoreRef = (0, import_react27.useRef)(0);
|
|
15623
|
+
const prevVelocityRef = (0, import_react27.useRef)(null);
|
|
15624
|
+
const isDizzyRef = (0, import_react27.useRef)(false);
|
|
15625
|
+
const dizzyTimerRef = (0, import_react27.useRef)(null);
|
|
15626
|
+
const lastVelocityRef = (0, import_react27.useRef)({ vx: 0, vy: 0, speed: 0 });
|
|
15627
|
+
const dragRafRef = (0, import_react27.useRef)(null);
|
|
15628
|
+
const animCleanupRef = (0, import_react27.useRef)(null);
|
|
15629
|
+
const snapEnabledRef = (0, import_react27.useRef)(snapEnabled);
|
|
15630
|
+
const storageKeyRef = (0, import_react27.useRef)(storageKey);
|
|
15631
|
+
const panelSizeRef = (0, import_react27.useRef)(panelSize);
|
|
15632
|
+
const fabPosRef = (0, import_react27.useRef)(fabPos);
|
|
15633
|
+
(0, import_react27.useEffect)(() => {
|
|
15471
15634
|
snapEnabledRef.current = snapEnabled;
|
|
15472
15635
|
}, [snapEnabled]);
|
|
15473
|
-
(0,
|
|
15636
|
+
(0, import_react27.useEffect)(() => {
|
|
15474
15637
|
storageKeyRef.current = storageKey;
|
|
15475
15638
|
}, [storageKey]);
|
|
15476
|
-
(0,
|
|
15639
|
+
(0, import_react27.useEffect)(() => {
|
|
15477
15640
|
panelSizeRef.current = panelSize;
|
|
15478
15641
|
}, [panelSize]);
|
|
15479
|
-
(0,
|
|
15642
|
+
(0, import_react27.useEffect)(() => {
|
|
15480
15643
|
fabPosRef.current = fabPos;
|
|
15481
15644
|
}, [fabPos]);
|
|
15482
|
-
const resizeStartRef = (0,
|
|
15483
|
-
(0,
|
|
15645
|
+
const resizeStartRef = (0, import_react27.useRef)(null);
|
|
15646
|
+
(0, import_react27.useEffect)(() => {
|
|
15484
15647
|
return () => {
|
|
15485
15648
|
dragCleanupRef.current?.();
|
|
15486
15649
|
if (snapTimerRef.current) clearTimeout(snapTimerRef.current);
|
|
@@ -15488,7 +15651,7 @@ var useDragResize = (options) => {
|
|
|
15488
15651
|
if (dragRafRef.current) cancelAnimationFrame(dragRafRef.current);
|
|
15489
15652
|
};
|
|
15490
15653
|
}, []);
|
|
15491
|
-
const handleFabPointerDown = (0,
|
|
15654
|
+
const handleFabPointerDown = (0, import_react27.useCallback)((e) => {
|
|
15492
15655
|
if (disabled || isMobile) return;
|
|
15493
15656
|
dragCleanupRef.current?.();
|
|
15494
15657
|
const el = e.currentTarget;
|
|
@@ -15670,7 +15833,7 @@ var useDragResize = (options) => {
|
|
|
15670
15833
|
}, [disabled, isMobile]);
|
|
15671
15834
|
const fabCenterX = fabPos.x + FAB_SIZE / 2;
|
|
15672
15835
|
const panelDirection = fabCenterX > vw / 2 ? "left" : "right";
|
|
15673
|
-
const panelPositionStyle = (0,
|
|
15836
|
+
const panelPositionStyle = (0, import_react27.useMemo)(() => {
|
|
15674
15837
|
if (isMobile || disabled) return {};
|
|
15675
15838
|
const fabIsTop = fabPos.y < vh / 2;
|
|
15676
15839
|
let panelBottom;
|
|
@@ -15702,7 +15865,7 @@ var useDragResize = (options) => {
|
|
|
15702
15865
|
borderRadius: "var(--floating-panel-radius, 16px)"
|
|
15703
15866
|
};
|
|
15704
15867
|
}, [isMobile, disabled, fabPos.x, fabPos.y, panelSize.width, panelSize.height, vw, vh, panelDirection, minHeight]);
|
|
15705
|
-
const handleResizePointerDown = (0,
|
|
15868
|
+
const handleResizePointerDown = (0, import_react27.useCallback)((edge, e) => {
|
|
15706
15869
|
if (disabled || isMobile) return;
|
|
15707
15870
|
e.preventDefault();
|
|
15708
15871
|
e.stopPropagation();
|
|
@@ -15718,7 +15881,7 @@ var useDragResize = (options) => {
|
|
|
15718
15881
|
};
|
|
15719
15882
|
setIsResizing(true);
|
|
15720
15883
|
}, [disabled, isMobile]);
|
|
15721
|
-
const handleResizePointerMove = (0,
|
|
15884
|
+
const handleResizePointerMove = (0, import_react27.useCallback)((e) => {
|
|
15722
15885
|
if (!resizeStartRef.current) return;
|
|
15723
15886
|
const { startX, startY, width, height, edge, fabY } = resizeStartRef.current;
|
|
15724
15887
|
const dx = e.clientX - startX;
|
|
@@ -15742,7 +15905,7 @@ var useDragResize = (options) => {
|
|
|
15742
15905
|
newHeight = Math.max(minHeight, Math.min(maxH, newHeight));
|
|
15743
15906
|
setPanelSize({ width: newWidth, height: newHeight });
|
|
15744
15907
|
}, [minWidth, maxWidth, minHeight]);
|
|
15745
|
-
const handleResizePointerUp = (0,
|
|
15908
|
+
const handleResizePointerUp = (0, import_react27.useCallback)((e) => {
|
|
15746
15909
|
if (!resizeStartRef.current) return;
|
|
15747
15910
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
15748
15911
|
resizeStartRef.current = null;
|
|
@@ -15751,7 +15914,7 @@ var useDragResize = (options) => {
|
|
|
15751
15914
|
const fp = fabPosRef.current;
|
|
15752
15915
|
persistState(storageKeyRef.current, { fabX: fp.x, fabY: fp.y, panelWidth: ps.width, panelHeight: ps.height });
|
|
15753
15916
|
}, []);
|
|
15754
|
-
(0,
|
|
15917
|
+
(0, import_react27.useEffect)(() => {
|
|
15755
15918
|
if (typeof window === "undefined") return;
|
|
15756
15919
|
const handleResize = () => {
|
|
15757
15920
|
const newVw = window.innerWidth;
|
|
@@ -15813,9 +15976,9 @@ var useFloatingWidget = (options) => {
|
|
|
15813
15976
|
maxWidth,
|
|
15814
15977
|
minHeight
|
|
15815
15978
|
} = options || {};
|
|
15816
|
-
const [isOpen, setIsOpen] = (0,
|
|
15817
|
-
const [activeTab, setActiveTab] = (0,
|
|
15818
|
-
const panelRef = (0,
|
|
15979
|
+
const [isOpen, setIsOpen] = (0, import_react28.useState)(defaultOpen);
|
|
15980
|
+
const [activeTab, setActiveTab] = (0, import_react28.useState)(defaultTab);
|
|
15981
|
+
const panelRef = (0, import_react28.useRef)(null);
|
|
15819
15982
|
const dragResize = useDragResize({
|
|
15820
15983
|
initialPosition: position,
|
|
15821
15984
|
initialWidth: width,
|
|
@@ -15827,27 +15990,27 @@ var useFloatingWidget = (options) => {
|
|
|
15827
15990
|
maxWidth,
|
|
15828
15991
|
minHeight
|
|
15829
15992
|
});
|
|
15830
|
-
const onOpenRef = (0,
|
|
15831
|
-
const onCloseRef = (0,
|
|
15832
|
-
const onTabChangeRef = (0,
|
|
15833
|
-
(0,
|
|
15993
|
+
const onOpenRef = (0, import_react28.useRef)(onOpen);
|
|
15994
|
+
const onCloseRef = (0, import_react28.useRef)(onClose);
|
|
15995
|
+
const onTabChangeRef = (0, import_react28.useRef)(onTabChange);
|
|
15996
|
+
(0, import_react28.useEffect)(() => {
|
|
15834
15997
|
onOpenRef.current = onOpen;
|
|
15835
15998
|
}, [onOpen]);
|
|
15836
|
-
(0,
|
|
15999
|
+
(0, import_react28.useEffect)(() => {
|
|
15837
16000
|
onCloseRef.current = onClose;
|
|
15838
16001
|
}, [onClose]);
|
|
15839
|
-
(0,
|
|
16002
|
+
(0, import_react28.useEffect)(() => {
|
|
15840
16003
|
onTabChangeRef.current = onTabChange;
|
|
15841
16004
|
}, [onTabChange]);
|
|
15842
|
-
const open = (0,
|
|
16005
|
+
const open = (0, import_react28.useCallback)(() => {
|
|
15843
16006
|
setIsOpen(true);
|
|
15844
16007
|
onOpenRef.current?.();
|
|
15845
16008
|
}, []);
|
|
15846
|
-
const close = (0,
|
|
16009
|
+
const close = (0, import_react28.useCallback)(() => {
|
|
15847
16010
|
setIsOpen(false);
|
|
15848
16011
|
onCloseRef.current?.();
|
|
15849
16012
|
}, []);
|
|
15850
|
-
const toggle = (0,
|
|
16013
|
+
const toggle = (0, import_react28.useCallback)(() => {
|
|
15851
16014
|
setIsOpen((prev) => {
|
|
15852
16015
|
const next = !prev;
|
|
15853
16016
|
if (next) onOpenRef.current?.();
|
|
@@ -15855,15 +16018,15 @@ var useFloatingWidget = (options) => {
|
|
|
15855
16018
|
return next;
|
|
15856
16019
|
});
|
|
15857
16020
|
}, []);
|
|
15858
|
-
const setTab = (0,
|
|
16021
|
+
const setTab = (0, import_react28.useCallback)((tabKey) => {
|
|
15859
16022
|
setActiveTab(tabKey);
|
|
15860
16023
|
onTabChangeRef.current?.(tabKey);
|
|
15861
16024
|
}, []);
|
|
15862
|
-
const handleFabInteraction = (0,
|
|
16025
|
+
const handleFabInteraction = (0, import_react28.useCallback)(() => {
|
|
15863
16026
|
if (dragResize.isDragging) return;
|
|
15864
16027
|
toggle();
|
|
15865
16028
|
}, [dragResize.isDragging, toggle]);
|
|
15866
|
-
(0,
|
|
16029
|
+
(0, import_react28.useEffect)(() => {
|
|
15867
16030
|
if (!isOpen) return;
|
|
15868
16031
|
const handleKeyDown = (e) => {
|
|
15869
16032
|
if (e.key === "Escape") {
|
|
@@ -15873,7 +16036,7 @@ var useFloatingWidget = (options) => {
|
|
|
15873
16036
|
document.addEventListener("keydown", handleKeyDown);
|
|
15874
16037
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
15875
16038
|
}, [isOpen, close]);
|
|
15876
|
-
(0,
|
|
16039
|
+
(0, import_react28.useEffect)(() => {
|
|
15877
16040
|
if (!isOpen) return;
|
|
15878
16041
|
const handleClickOutside = (e) => {
|
|
15879
16042
|
if (dragResize.isDragging || dragResize.isResizing) return;
|
|
@@ -15889,10 +16052,10 @@ var useFloatingWidget = (options) => {
|
|
|
15889
16052
|
};
|
|
15890
16053
|
|
|
15891
16054
|
// src/react/components/floating/FloatingFab.tsx
|
|
15892
|
-
var
|
|
16055
|
+
var import_react30 = require("react");
|
|
15893
16056
|
|
|
15894
16057
|
// src/react/components/floating/DevDiveCharacter.tsx
|
|
15895
|
-
var
|
|
16058
|
+
var import_react29 = require("react");
|
|
15896
16059
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
15897
16060
|
var THEMES = {
|
|
15898
16061
|
dark: { body: "#2ecc71", stroke: "#27ae60", highlight: "#3ddc84", face: "#1a1a2e", eyeLight: "#fff" },
|
|
@@ -15913,32 +16076,32 @@ var DevDiveFabCharacter = ({
|
|
|
15913
16076
|
isComplete = false
|
|
15914
16077
|
}) => {
|
|
15915
16078
|
const c = THEMES[theme];
|
|
15916
|
-
const [eyeOffset, setEyeOffset] = (0,
|
|
15917
|
-
const [isBlinking, setIsBlinking] = (0,
|
|
15918
|
-
const [isHappy, setIsHappy] = (0,
|
|
15919
|
-
const [isPressed, setIsPressed] = (0,
|
|
15920
|
-
const [mouthOpen, setMouthOpen] = (0,
|
|
15921
|
-
const [isSleepy, setIsSleepy] = (0,
|
|
15922
|
-
const [isSurprised, setIsSurprised] = (0,
|
|
15923
|
-
const [isWinking, setIsWinking] = (0,
|
|
15924
|
-
const [isCatMouth, setIsCatMouth] = (0,
|
|
15925
|
-
const [isEntering, setIsEntering] = (0,
|
|
15926
|
-
const svgRef = (0,
|
|
15927
|
-
const cleanupRef = (0,
|
|
15928
|
-
const lastActivityRef = (0,
|
|
15929
|
-
const isSleepyRef = (0,
|
|
15930
|
-
const markActivity = (0,
|
|
16079
|
+
const [eyeOffset, setEyeOffset] = (0, import_react29.useState)({ x: 0, y: 0 });
|
|
16080
|
+
const [isBlinking, setIsBlinking] = (0, import_react29.useState)(false);
|
|
16081
|
+
const [isHappy, setIsHappy] = (0, import_react29.useState)(false);
|
|
16082
|
+
const [isPressed, setIsPressed] = (0, import_react29.useState)(false);
|
|
16083
|
+
const [mouthOpen, setMouthOpen] = (0, import_react29.useState)(false);
|
|
16084
|
+
const [isSleepy, setIsSleepy] = (0, import_react29.useState)(false);
|
|
16085
|
+
const [isSurprised, setIsSurprised] = (0, import_react29.useState)(false);
|
|
16086
|
+
const [isWinking, setIsWinking] = (0, import_react29.useState)(false);
|
|
16087
|
+
const [isCatMouth, setIsCatMouth] = (0, import_react29.useState)(false);
|
|
16088
|
+
const [isEntering, setIsEntering] = (0, import_react29.useState)(true);
|
|
16089
|
+
const svgRef = (0, import_react29.useRef)(null);
|
|
16090
|
+
const cleanupRef = (0, import_react29.useRef)(null);
|
|
16091
|
+
const lastActivityRef = (0, import_react29.useRef)(Date.now());
|
|
16092
|
+
const isSleepyRef = (0, import_react29.useRef)(false);
|
|
16093
|
+
const markActivity = (0, import_react29.useCallback)(() => {
|
|
15931
16094
|
lastActivityRef.current = Date.now();
|
|
15932
16095
|
if (isSleepyRef.current) {
|
|
15933
16096
|
isSleepyRef.current = false;
|
|
15934
16097
|
setIsSleepy(false);
|
|
15935
16098
|
}
|
|
15936
16099
|
}, []);
|
|
15937
|
-
(0,
|
|
16100
|
+
(0, import_react29.useEffect)(() => {
|
|
15938
16101
|
const t = setTimeout(() => setIsEntering(false), 600);
|
|
15939
16102
|
return () => clearTimeout(t);
|
|
15940
16103
|
}, []);
|
|
15941
|
-
(0,
|
|
16104
|
+
(0, import_react29.useEffect)(() => {
|
|
15942
16105
|
let timer;
|
|
15943
16106
|
let blinkTimer;
|
|
15944
16107
|
const scheduleBlink = () => {
|
|
@@ -15954,7 +16117,7 @@ var DevDiveFabCharacter = ({
|
|
|
15954
16117
|
clearTimeout(blinkTimer);
|
|
15955
16118
|
};
|
|
15956
16119
|
}, []);
|
|
15957
|
-
(0,
|
|
16120
|
+
(0, import_react29.useEffect)(() => {
|
|
15958
16121
|
if (!isTalking) {
|
|
15959
16122
|
setMouthOpen(false);
|
|
15960
16123
|
return;
|
|
@@ -15967,7 +16130,7 @@ var DevDiveFabCharacter = ({
|
|
|
15967
16130
|
toggle();
|
|
15968
16131
|
return () => clearTimeout(timer);
|
|
15969
16132
|
}, [isTalking]);
|
|
15970
|
-
(0,
|
|
16133
|
+
(0, import_react29.useEffect)(() => {
|
|
15971
16134
|
if (typeof window === "undefined") return;
|
|
15972
16135
|
let rafId = 0;
|
|
15973
16136
|
const onMouseMove = (e) => {
|
|
@@ -15991,7 +16154,7 @@ var DevDiveFabCharacter = ({
|
|
|
15991
16154
|
cancelAnimationFrame(rafId);
|
|
15992
16155
|
};
|
|
15993
16156
|
}, []);
|
|
15994
|
-
(0,
|
|
16157
|
+
(0, import_react29.useEffect)(() => {
|
|
15995
16158
|
let upTimer;
|
|
15996
16159
|
let surpriseTimer;
|
|
15997
16160
|
const rafId = requestAnimationFrame(() => {
|
|
@@ -16034,10 +16197,10 @@ var DevDiveFabCharacter = ({
|
|
|
16034
16197
|
cleanupRef.current?.();
|
|
16035
16198
|
};
|
|
16036
16199
|
}, [markActivity]);
|
|
16037
|
-
(0,
|
|
16200
|
+
(0, import_react29.useEffect)(() => {
|
|
16038
16201
|
markActivity();
|
|
16039
16202
|
}, [isOpen, isTalking, markActivity]);
|
|
16040
|
-
(0,
|
|
16203
|
+
(0, import_react29.useEffect)(() => {
|
|
16041
16204
|
if (isOpen || isTalking || isDizzy) return;
|
|
16042
16205
|
const check = setInterval(() => {
|
|
16043
16206
|
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS && !isSleepyRef.current) {
|
|
@@ -16047,8 +16210,8 @@ var DevDiveFabCharacter = ({
|
|
|
16047
16210
|
}, 5e3);
|
|
16048
16211
|
return () => clearInterval(check);
|
|
16049
16212
|
}, [isOpen, isTalking, isDizzy]);
|
|
16050
|
-
const prevCompleteRef = (0,
|
|
16051
|
-
(0,
|
|
16213
|
+
const prevCompleteRef = (0, import_react29.useRef)(false);
|
|
16214
|
+
(0, import_react29.useEffect)(() => {
|
|
16052
16215
|
const wasComplete = prevCompleteRef.current;
|
|
16053
16216
|
prevCompleteRef.current = isComplete;
|
|
16054
16217
|
if (wasComplete || !isComplete) return;
|
|
@@ -16056,7 +16219,7 @@ var DevDiveFabCharacter = ({
|
|
|
16056
16219
|
const t = setTimeout(() => setIsWinking(false), WINK_DURATION_MS);
|
|
16057
16220
|
return () => clearTimeout(t);
|
|
16058
16221
|
}, [isComplete]);
|
|
16059
|
-
(0,
|
|
16222
|
+
(0, import_react29.useEffect)(() => {
|
|
16060
16223
|
if (isOpen || isTalking || isDizzy || isError || isSleepy) {
|
|
16061
16224
|
setIsCatMouth(false);
|
|
16062
16225
|
return;
|
|
@@ -16248,12 +16411,12 @@ var FloatingFab = ({
|
|
|
16248
16411
|
}) => {
|
|
16249
16412
|
const isRight = position.includes("right");
|
|
16250
16413
|
const isBottom = position.includes("bottom");
|
|
16251
|
-
const fabRef = (0,
|
|
16414
|
+
const fabRef = (0, import_react30.useRef)(null);
|
|
16252
16415
|
const isCharacterMode = !icon;
|
|
16253
|
-
const [bubbleOnRight, setBubbleOnRight] = (0,
|
|
16416
|
+
const [bubbleOnRight, setBubbleOnRight] = (0, import_react30.useState)(!isRight);
|
|
16254
16417
|
const posLeft = positionStyle?.left;
|
|
16255
16418
|
const posTop = positionStyle?.top;
|
|
16256
|
-
(0,
|
|
16419
|
+
(0, import_react30.useEffect)(() => {
|
|
16257
16420
|
if (!fabRef.current || typeof window === "undefined") {
|
|
16258
16421
|
setBubbleOnRight(!isRight);
|
|
16259
16422
|
return;
|
|
@@ -16261,14 +16424,14 @@ var FloatingFab = ({
|
|
|
16261
16424
|
const rect = fabRef.current.getBoundingClientRect();
|
|
16262
16425
|
setBubbleOnRight(rect.left + rect.width / 2 < window.innerWidth / 2);
|
|
16263
16426
|
}, [isRight, posLeft, posTop]);
|
|
16264
|
-
const [bubbleText, setBubbleText] = (0,
|
|
16265
|
-
const [bubbleExiting, setBubbleExiting] = (0,
|
|
16266
|
-
const bubbleTextRef = (0,
|
|
16427
|
+
const [bubbleText, setBubbleText] = (0, import_react30.useState)(null);
|
|
16428
|
+
const [bubbleExiting, setBubbleExiting] = (0, import_react30.useState)(false);
|
|
16429
|
+
const bubbleTextRef = (0, import_react30.useRef)(bubbleText);
|
|
16267
16430
|
bubbleTextRef.current = bubbleText;
|
|
16268
|
-
const [displayText, setDisplayText] = (0,
|
|
16269
|
-
const [isTyping, setIsTyping] = (0,
|
|
16270
|
-
const typingTimerRef = (0,
|
|
16271
|
-
(0,
|
|
16431
|
+
const [displayText, setDisplayText] = (0, import_react30.useState)(null);
|
|
16432
|
+
const [isTyping, setIsTyping] = (0, import_react30.useState)(false);
|
|
16433
|
+
const typingTimerRef = (0, import_react30.useRef)(null);
|
|
16434
|
+
(0, import_react30.useEffect)(() => {
|
|
16272
16435
|
if (notification) {
|
|
16273
16436
|
setBubbleText(notification);
|
|
16274
16437
|
setBubbleExiting(false);
|
|
@@ -16304,9 +16467,9 @@ var FloatingFab = ({
|
|
|
16304
16467
|
}, 300);
|
|
16305
16468
|
return () => clearTimeout(timer);
|
|
16306
16469
|
}, [notification]);
|
|
16307
|
-
const notifContentRef = (0,
|
|
16308
|
-
const [needsMarquee, setNeedsMarquee] = (0,
|
|
16309
|
-
(0,
|
|
16470
|
+
const notifContentRef = (0, import_react30.useRef)(null);
|
|
16471
|
+
const [needsMarquee, setNeedsMarquee] = (0, import_react30.useState)(false);
|
|
16472
|
+
(0, import_react30.useEffect)(() => {
|
|
16310
16473
|
if (isTyping || !notification) {
|
|
16311
16474
|
setNeedsMarquee(false);
|
|
16312
16475
|
return;
|
|
@@ -16448,7 +16611,7 @@ var FloatingFab = ({
|
|
|
16448
16611
|
};
|
|
16449
16612
|
|
|
16450
16613
|
// src/react/components/floating/FloatingPanel.tsx
|
|
16451
|
-
var
|
|
16614
|
+
var import_react31 = require("react");
|
|
16452
16615
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
16453
16616
|
var FloatingPanel = ({
|
|
16454
16617
|
isOpen,
|
|
@@ -16466,8 +16629,8 @@ var FloatingPanel = ({
|
|
|
16466
16629
|
}) => {
|
|
16467
16630
|
const isRight = position.includes("right");
|
|
16468
16631
|
const themeClass = theme?.mode === "dark" ? "chatllm-dark" : "";
|
|
16469
|
-
const [isMobile, setIsMobile] = (0,
|
|
16470
|
-
(0,
|
|
16632
|
+
const [isMobile, setIsMobile] = (0, import_react31.useState)(false);
|
|
16633
|
+
(0, import_react31.useEffect)(() => {
|
|
16471
16634
|
if (typeof window === "undefined") return;
|
|
16472
16635
|
const mql = window.matchMedia("(max-width: 767px)");
|
|
16473
16636
|
setIsMobile(mql.matches);
|
|
@@ -16475,10 +16638,10 @@ var FloatingPanel = ({
|
|
|
16475
16638
|
mql.addEventListener("change", handler);
|
|
16476
16639
|
return () => mql.removeEventListener("change", handler);
|
|
16477
16640
|
}, []);
|
|
16478
|
-
const [shouldRender, setShouldRender] = (0,
|
|
16479
|
-
const [isVisible, setIsVisible] = (0,
|
|
16480
|
-
const rafRef = (0,
|
|
16481
|
-
(0,
|
|
16641
|
+
const [shouldRender, setShouldRender] = (0, import_react31.useState)(isOpen);
|
|
16642
|
+
const [isVisible, setIsVisible] = (0, import_react31.useState)(isOpen);
|
|
16643
|
+
const rafRef = (0, import_react31.useRef)(0);
|
|
16644
|
+
(0, import_react31.useEffect)(() => {
|
|
16482
16645
|
if (isOpen) {
|
|
16483
16646
|
setShouldRender(true);
|
|
16484
16647
|
rafRef.current = requestAnimationFrame(() => {
|
|
@@ -16493,7 +16656,7 @@ var FloatingPanel = ({
|
|
|
16493
16656
|
}
|
|
16494
16657
|
return () => cancelAnimationFrame(rafRef.current);
|
|
16495
16658
|
}, [isOpen]);
|
|
16496
|
-
(0,
|
|
16659
|
+
(0, import_react31.useEffect)(() => {
|
|
16497
16660
|
if (!isOpen || !isMobile) return;
|
|
16498
16661
|
const prev = document.body.style.overflow;
|
|
16499
16662
|
document.body.style.overflow = "hidden";
|
|
@@ -16654,10 +16817,10 @@ var FloatingTabBar = ({
|
|
|
16654
16817
|
};
|
|
16655
16818
|
|
|
16656
16819
|
// src/react/components/floating/CompactChatView.tsx
|
|
16657
|
-
var
|
|
16820
|
+
var import_react33 = require("react");
|
|
16658
16821
|
|
|
16659
16822
|
// src/react/components/floating/CompactSessionMenu.tsx
|
|
16660
|
-
var
|
|
16823
|
+
var import_react32 = require("react");
|
|
16661
16824
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
16662
16825
|
var CompactSessionMenu = ({
|
|
16663
16826
|
sessions,
|
|
@@ -16669,13 +16832,13 @@ var CompactSessionMenu = ({
|
|
|
16669
16832
|
onClose,
|
|
16670
16833
|
isLoading = false
|
|
16671
16834
|
}) => {
|
|
16672
|
-
const menuRef = (0,
|
|
16673
|
-
const inputRef = (0,
|
|
16674
|
-
const onCloseRef = (0,
|
|
16835
|
+
const menuRef = (0, import_react32.useRef)(null);
|
|
16836
|
+
const inputRef = (0, import_react32.useRef)(null);
|
|
16837
|
+
const onCloseRef = (0, import_react32.useRef)(onClose);
|
|
16675
16838
|
onCloseRef.current = onClose;
|
|
16676
|
-
const [editingId, setEditingId] = (0,
|
|
16677
|
-
const [editingTitle, setEditingTitle] = (0,
|
|
16678
|
-
(0,
|
|
16839
|
+
const [editingId, setEditingId] = (0, import_react32.useState)(null);
|
|
16840
|
+
const [editingTitle, setEditingTitle] = (0, import_react32.useState)("");
|
|
16841
|
+
(0, import_react32.useEffect)(() => {
|
|
16679
16842
|
const handleMouseDown = (e) => {
|
|
16680
16843
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
16681
16844
|
onCloseRef.current();
|
|
@@ -16684,7 +16847,7 @@ var CompactSessionMenu = ({
|
|
|
16684
16847
|
document.addEventListener("mousedown", handleMouseDown);
|
|
16685
16848
|
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
16686
16849
|
}, []);
|
|
16687
|
-
(0,
|
|
16850
|
+
(0, import_react32.useEffect)(() => {
|
|
16688
16851
|
if (editingId && inputRef.current) {
|
|
16689
16852
|
inputRef.current.focus();
|
|
16690
16853
|
inputRef.current.select();
|
|
@@ -16999,16 +17162,16 @@ var CompactChatView = ({
|
|
|
16999
17162
|
const handleChoiceClick = (choice) => {
|
|
17000
17163
|
setInput(choice.text);
|
|
17001
17164
|
};
|
|
17002
|
-
const [showSessionMenu, setShowSessionMenu] = (0,
|
|
17003
|
-
const handleSessionSelect = (0,
|
|
17165
|
+
const [showSessionMenu, setShowSessionMenu] = (0, import_react33.useState)(false);
|
|
17166
|
+
const handleSessionSelect = (0, import_react33.useCallback)((id) => {
|
|
17004
17167
|
selectSession(id);
|
|
17005
17168
|
setShowSessionMenu(false);
|
|
17006
17169
|
}, [selectSession]);
|
|
17007
|
-
const handleNewSession = (0,
|
|
17170
|
+
const handleNewSession = (0, import_react33.useCallback)(() => {
|
|
17008
17171
|
newSession();
|
|
17009
17172
|
setShowSessionMenu(false);
|
|
17010
17173
|
}, [newSession]);
|
|
17011
|
-
const handleCloseMenu = (0,
|
|
17174
|
+
const handleCloseMenu = (0, import_react33.useCallback)(() => {
|
|
17012
17175
|
setShowSessionMenu(false);
|
|
17013
17176
|
}, []);
|
|
17014
17177
|
const greeting = personalization?.userProfile?.nickname ? `${personalization.userProfile.nickname}\uB2D8, \uBB34\uC5C7\uC774\uB4E0 \uBB3C\uC5B4\uBCF4\uC138\uC694` : "\uBB34\uC5C7\uC774\uB4E0 \uBB3C\uC5B4\uBCF4\uC138\uC694";
|
|
@@ -17328,11 +17491,11 @@ var ChatFloatingWidget = ({
|
|
|
17328
17491
|
maxWidth,
|
|
17329
17492
|
minHeight
|
|
17330
17493
|
});
|
|
17331
|
-
const notifObj = (0,
|
|
17494
|
+
const notifObj = (0, import_react34.useMemo)(() => {
|
|
17332
17495
|
if (!notification) return null;
|
|
17333
17496
|
return typeof notification === "string" ? { text: notification } : notification;
|
|
17334
17497
|
}, [notification]);
|
|
17335
|
-
const handleFabClick = (0,
|
|
17498
|
+
const handleFabClick = (0, import_react34.useCallback)(() => {
|
|
17336
17499
|
if (notifObj?.onClick) {
|
|
17337
17500
|
notifObj.onClick();
|
|
17338
17501
|
if (!isOpen) handleFabInteraction();
|
|
@@ -17346,7 +17509,7 @@ var ChatFloatingWidget = ({
|
|
|
17346
17509
|
}
|
|
17347
17510
|
handleFabInteraction();
|
|
17348
17511
|
}, [notifObj, isOpen, handleFabInteraction, setTab, tabs]);
|
|
17349
|
-
const allTabs = (0,
|
|
17512
|
+
const allTabs = (0, import_react34.useMemo)(() => {
|
|
17350
17513
|
const chatTab = {
|
|
17351
17514
|
key: "chat",
|
|
17352
17515
|
label: "\uCC44\uD305",
|
|
@@ -17360,16 +17523,16 @@ var ChatFloatingWidget = ({
|
|
|
17360
17523
|
}));
|
|
17361
17524
|
return [chatTab, ...customTabs];
|
|
17362
17525
|
}, [tabs]);
|
|
17363
|
-
const [unreadBadge, setUnreadBadge] = (0,
|
|
17364
|
-
const seenMessageIdsRef = (0,
|
|
17365
|
-
(0,
|
|
17526
|
+
const [unreadBadge, setUnreadBadge] = (0, import_react34.useState)(0);
|
|
17527
|
+
const seenMessageIdsRef = (0, import_react34.useRef)(/* @__PURE__ */ new Set());
|
|
17528
|
+
(0, import_react34.useEffect)(() => {
|
|
17366
17529
|
if (seenMessageIdsRef.current.size === 0 && chatState.messages.length > 0) {
|
|
17367
17530
|
for (const m of chatState.messages) {
|
|
17368
17531
|
seenMessageIdsRef.current.add(m.id);
|
|
17369
17532
|
}
|
|
17370
17533
|
}
|
|
17371
17534
|
}, [chatState.messages]);
|
|
17372
|
-
(0,
|
|
17535
|
+
(0, import_react34.useEffect)(() => {
|
|
17373
17536
|
if (isOpen) {
|
|
17374
17537
|
for (const m of chatState.messages) {
|
|
17375
17538
|
seenMessageIdsRef.current.add(m.id);
|
|
@@ -17386,7 +17549,7 @@ var ChatFloatingWidget = ({
|
|
|
17386
17549
|
}
|
|
17387
17550
|
}
|
|
17388
17551
|
}, [chatState.messages, isOpen]);
|
|
17389
|
-
(0,
|
|
17552
|
+
(0, import_react34.useEffect)(() => {
|
|
17390
17553
|
if (isOpen) {
|
|
17391
17554
|
setUnreadBadge(0);
|
|
17392
17555
|
for (const m of chatState.messages) {
|
|
@@ -17394,13 +17557,13 @@ var ChatFloatingWidget = ({
|
|
|
17394
17557
|
}
|
|
17395
17558
|
}
|
|
17396
17559
|
}, [isOpen, chatState.messages]);
|
|
17397
|
-
const totalBadge = (0,
|
|
17560
|
+
const totalBadge = (0, import_react34.useMemo)(() => {
|
|
17398
17561
|
return tabs.reduce((sum, t) => sum + (t.badge || 0), 0) + unreadBadge;
|
|
17399
17562
|
}, [tabs, unreadBadge]);
|
|
17400
|
-
const isTalking = (0,
|
|
17401
|
-
const prevLoadingRef = (0,
|
|
17402
|
-
const [isComplete, setIsComplete] = (0,
|
|
17403
|
-
(0,
|
|
17563
|
+
const isTalking = (0, import_react34.useMemo)(() => !!notification || chatState.isLoading, [notification, chatState.isLoading]);
|
|
17564
|
+
const prevLoadingRef = (0, import_react34.useRef)(chatState.isLoading);
|
|
17565
|
+
const [isComplete, setIsComplete] = (0, import_react34.useState)(false);
|
|
17566
|
+
(0, import_react34.useEffect)(() => {
|
|
17404
17567
|
const wasLoading = prevLoadingRef.current;
|
|
17405
17568
|
prevLoadingRef.current = chatState.isLoading;
|
|
17406
17569
|
if (!wasLoading || chatState.isLoading || chatState.messages.length === 0) return;
|
|
@@ -17516,7 +17679,7 @@ var ChatFloatingWidget = ({
|
|
|
17516
17679
|
};
|
|
17517
17680
|
|
|
17518
17681
|
// src/react/hooks/useDeepResearch.ts
|
|
17519
|
-
var
|
|
17682
|
+
var import_react35 = require("react");
|
|
17520
17683
|
var REPORT_GENERATION_PROMPT2 = `\uB2F9\uC2E0\uC740 \uB9AC\uC11C\uCE58 \uBCF4\uACE0\uC11C \uC791\uC131 \uC804\uBB38\uAC00\uC785\uB2C8\uB2E4.
|
|
17521
17684
|
|
|
17522
17685
|
<collected_sources>
|
|
@@ -17567,10 +17730,10 @@ var QUERY_ANALYSIS_PROMPT2 = `\uC0AC\uC6A9\uC790 \uC9C8\uBB38\uC744 \uBD84\uC11D
|
|
|
17567
17730
|
- JSON \uC678 \uB2E4\uB978 \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uAE08\uC9C0`;
|
|
17568
17731
|
var useDeepResearch = (options) => {
|
|
17569
17732
|
const { onWebSearch, onExtractContent, apiEndpoint, apiKey, model, provider } = options;
|
|
17570
|
-
const [isResearching, setIsResearching] = (0,
|
|
17571
|
-
const [progress, setProgress] = (0,
|
|
17572
|
-
const abortControllerRef = (0,
|
|
17573
|
-
const callLLM2 = (0,
|
|
17733
|
+
const [isResearching, setIsResearching] = (0, import_react35.useState)(false);
|
|
17734
|
+
const [progress, setProgress] = (0, import_react35.useState)(null);
|
|
17735
|
+
const abortControllerRef = (0, import_react35.useRef)(null);
|
|
17736
|
+
const callLLM2 = (0, import_react35.useCallback)(
|
|
17574
17737
|
async (prompt, stream = false) => {
|
|
17575
17738
|
const response = await fetch(apiEndpoint, {
|
|
17576
17739
|
method: "POST",
|
|
@@ -17597,7 +17760,7 @@ var useDeepResearch = (options) => {
|
|
|
17597
17760
|
},
|
|
17598
17761
|
[apiEndpoint, apiKey, model, provider]
|
|
17599
17762
|
);
|
|
17600
|
-
const analyzeQuery2 = (0,
|
|
17763
|
+
const analyzeQuery2 = (0, import_react35.useCallback)(
|
|
17601
17764
|
async (query) => {
|
|
17602
17765
|
const prompt = QUERY_ANALYSIS_PROMPT2.replace("{question}", query);
|
|
17603
17766
|
const response = await callLLM2(prompt);
|
|
@@ -17616,7 +17779,7 @@ var useDeepResearch = (options) => {
|
|
|
17616
17779
|
},
|
|
17617
17780
|
[callLLM2]
|
|
17618
17781
|
);
|
|
17619
|
-
const runSubAgent2 = (0,
|
|
17782
|
+
const runSubAgent2 = (0, import_react35.useCallback)(
|
|
17620
17783
|
async (topic, queries, agentId, updateProgress) => {
|
|
17621
17784
|
updateProgress({ status: "searching", searchCount: 0, resultsCount: 0 });
|
|
17622
17785
|
const allResults = [];
|
|
@@ -17655,7 +17818,7 @@ var useDeepResearch = (options) => {
|
|
|
17655
17818
|
},
|
|
17656
17819
|
[onWebSearch, onExtractContent]
|
|
17657
17820
|
);
|
|
17658
|
-
const generateReport2 = (0,
|
|
17821
|
+
const generateReport2 = (0, import_react35.useCallback)(
|
|
17659
17822
|
async (query, results, onStreamContent) => {
|
|
17660
17823
|
const allSources = [];
|
|
17661
17824
|
const sourcesForPrompt = [];
|
|
@@ -17713,7 +17876,7 @@ var useDeepResearch = (options) => {
|
|
|
17713
17876
|
},
|
|
17714
17877
|
[callLLM2]
|
|
17715
17878
|
);
|
|
17716
|
-
const runDeepResearch = (0,
|
|
17879
|
+
const runDeepResearch = (0, import_react35.useCallback)(
|
|
17717
17880
|
async (query, onStreamContent) => {
|
|
17718
17881
|
abortControllerRef.current = new AbortController();
|
|
17719
17882
|
setIsResearching(true);
|
|
@@ -17816,7 +17979,7 @@ var useDeepResearch = (options) => {
|
|
|
17816
17979
|
},
|
|
17817
17980
|
[analyzeQuery2, runSubAgent2, generateReport2]
|
|
17818
17981
|
);
|
|
17819
|
-
const stopResearch = (0,
|
|
17982
|
+
const stopResearch = (0, import_react35.useCallback)(() => {
|
|
17820
17983
|
abortControllerRef.current?.abort();
|
|
17821
17984
|
setIsResearching(false);
|
|
17822
17985
|
setProgress(null);
|
|
@@ -17829,6 +17992,122 @@ var useDeepResearch = (options) => {
|
|
|
17829
17992
|
};
|
|
17830
17993
|
};
|
|
17831
17994
|
|
|
17995
|
+
// src/react/hooks/useContentParsers.ts
|
|
17996
|
+
var import_react36 = require("react");
|
|
17997
|
+
var useContentParsers = ({
|
|
17998
|
+
sessionsRef,
|
|
17999
|
+
setSessions
|
|
18000
|
+
}) => {
|
|
18001
|
+
const applyPollParsing = (0, import_react36.useCallback)(
|
|
18002
|
+
(sessionId, messageId, content, skipParsing) => {
|
|
18003
|
+
const { pollBlock, cleanContent } = parsePollFromContent(content);
|
|
18004
|
+
setSessions(
|
|
18005
|
+
(prev) => prev.map((s) => {
|
|
18006
|
+
if (s.id !== sessionId) return s;
|
|
18007
|
+
return {
|
|
18008
|
+
...s,
|
|
18009
|
+
messages: s.messages.map((m) => {
|
|
18010
|
+
if (m.id !== messageId) return m;
|
|
18011
|
+
if (skipParsing) {
|
|
18012
|
+
return { ...m, content: cleanContent };
|
|
18013
|
+
}
|
|
18014
|
+
if (pollBlock) {
|
|
18015
|
+
return { ...m, content: cleanContent, pollBlock };
|
|
18016
|
+
}
|
|
18017
|
+
return m;
|
|
18018
|
+
})
|
|
18019
|
+
};
|
|
18020
|
+
})
|
|
18021
|
+
);
|
|
18022
|
+
return skipParsing ? null : pollBlock;
|
|
18023
|
+
},
|
|
18024
|
+
[setSessions]
|
|
18025
|
+
);
|
|
18026
|
+
const applyChecklistParsing = (0, import_react36.useCallback)(
|
|
18027
|
+
(sessionId, messageId, content, skipParsing) => {
|
|
18028
|
+
if (skipParsing) return null;
|
|
18029
|
+
const { checklistBlock, cleanContent } = parseChecklistFromContent(content);
|
|
18030
|
+
if (!checklistBlock) return null;
|
|
18031
|
+
setSessions(
|
|
18032
|
+
(prev) => prev.map((s) => {
|
|
18033
|
+
if (s.id !== sessionId) return s;
|
|
18034
|
+
return {
|
|
18035
|
+
...s,
|
|
18036
|
+
messages: s.messages.map((m) => {
|
|
18037
|
+
if (m.id !== messageId) return m;
|
|
18038
|
+
return { ...m, content: cleanContent, checklistBlock };
|
|
18039
|
+
})
|
|
18040
|
+
};
|
|
18041
|
+
})
|
|
18042
|
+
);
|
|
18043
|
+
return checklistBlock;
|
|
18044
|
+
},
|
|
18045
|
+
[setSessions]
|
|
18046
|
+
);
|
|
18047
|
+
const applyArtifactParsing = (0, import_react36.useCallback)(
|
|
18048
|
+
(sessionId, messageId, content) => {
|
|
18049
|
+
if (!hasArtifactTag(content)) return false;
|
|
18050
|
+
let found = false;
|
|
18051
|
+
setSessions(
|
|
18052
|
+
(prev) => prev.map((s) => {
|
|
18053
|
+
if (s.id !== sessionId) return s;
|
|
18054
|
+
return {
|
|
18055
|
+
...s,
|
|
18056
|
+
messages: s.messages.map((m) => {
|
|
18057
|
+
if (m.id !== messageId) return m;
|
|
18058
|
+
const { artifacts, cleanContent } = parseArtifactsFromContent(m.content);
|
|
18059
|
+
if (artifacts.length === 0) return m;
|
|
18060
|
+
found = true;
|
|
18061
|
+
const existingParts = m.contentParts || [];
|
|
18062
|
+
const textPart = cleanContent.trim() ? [{ type: "text", content: cleanContent }] : [];
|
|
18063
|
+
return {
|
|
18064
|
+
...m,
|
|
18065
|
+
content: cleanContent,
|
|
18066
|
+
contentParts: [
|
|
18067
|
+
...textPart,
|
|
18068
|
+
...existingParts.filter(
|
|
18069
|
+
(p) => p.type !== "text" && p.type !== "artifact"
|
|
18070
|
+
),
|
|
18071
|
+
...artifacts
|
|
18072
|
+
]
|
|
18073
|
+
};
|
|
18074
|
+
})
|
|
18075
|
+
};
|
|
18076
|
+
})
|
|
18077
|
+
);
|
|
18078
|
+
return found;
|
|
18079
|
+
},
|
|
18080
|
+
[setSessions]
|
|
18081
|
+
);
|
|
18082
|
+
const parseContextReferences = (0, import_react36.useCallback)(
|
|
18083
|
+
(content, sessionContext) => {
|
|
18084
|
+
const { refs, cleanContent } = parseContextRefs(content);
|
|
18085
|
+
if (refs.length === 0 || !sessionContext) {
|
|
18086
|
+
return { refs, cleanContent, refContents: null };
|
|
18087
|
+
}
|
|
18088
|
+
const assembled = refs.map((refId) => {
|
|
18089
|
+
const item = sessionContext.references.find(
|
|
18090
|
+
(r) => r.id === refId
|
|
18091
|
+
);
|
|
18092
|
+
return item ? `[${item.label || item.skillName}]
|
|
18093
|
+
${item.summary}` : null;
|
|
18094
|
+
}).filter(Boolean).join("\n\n");
|
|
18095
|
+
return {
|
|
18096
|
+
refs,
|
|
18097
|
+
cleanContent,
|
|
18098
|
+
refContents: assembled || null
|
|
18099
|
+
};
|
|
18100
|
+
},
|
|
18101
|
+
[]
|
|
18102
|
+
);
|
|
18103
|
+
return {
|
|
18104
|
+
applyPollParsing,
|
|
18105
|
+
applyChecklistParsing,
|
|
18106
|
+
applyArtifactParsing,
|
|
18107
|
+
parseContextReferences
|
|
18108
|
+
};
|
|
18109
|
+
};
|
|
18110
|
+
|
|
17832
18111
|
// src/react/utils/conversationSearchAdapter.ts
|
|
17833
18112
|
var formatSearchResults = (results) => {
|
|
17834
18113
|
if (results.length === 0) {
|
|
@@ -18019,7 +18298,7 @@ var EmptyState = ({
|
|
|
18019
18298
|
};
|
|
18020
18299
|
|
|
18021
18300
|
// src/react/components/MemoryPanel.tsx
|
|
18022
|
-
var
|
|
18301
|
+
var import_react37 = require("react");
|
|
18023
18302
|
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
18024
18303
|
var categoryLabels = {
|
|
18025
18304
|
fact: "\uC0AC\uC6A9\uC790 \uC815\uBCF4",
|
|
@@ -18039,8 +18318,8 @@ var MemoryPanel = ({
|
|
|
18039
18318
|
isOpen,
|
|
18040
18319
|
onToggle
|
|
18041
18320
|
}) => {
|
|
18042
|
-
const [expandedId, setExpandedId] = (0,
|
|
18043
|
-
const [activeTab, setActiveTab] = (0,
|
|
18321
|
+
const [expandedId, setExpandedId] = (0, import_react37.useState)(null);
|
|
18322
|
+
const [activeTab, setActiveTab] = (0, import_react37.useState)("all");
|
|
18044
18323
|
const filteredItems = activeTab === "all" ? items : items.filter((item) => item.category === activeTab);
|
|
18045
18324
|
const formatDate = (timestamp) => {
|
|
18046
18325
|
const date = new Date(timestamp);
|
|
@@ -18356,9 +18635,53 @@ var MemoryPanel = ({
|
|
|
18356
18635
|
}
|
|
18357
18636
|
);
|
|
18358
18637
|
};
|
|
18638
|
+
|
|
18639
|
+
// src/react/utils/retry.ts
|
|
18640
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
18641
|
+
maxRetries: 3,
|
|
18642
|
+
initialDelayMs: 1e3,
|
|
18643
|
+
maxDelayMs: 3e4,
|
|
18644
|
+
backoffMultiplier: 2
|
|
18645
|
+
};
|
|
18646
|
+
var calculateDelay = (attempt, config) => {
|
|
18647
|
+
const exponential = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
18648
|
+
const capped = Math.min(exponential, config.maxDelayMs);
|
|
18649
|
+
const jitter = 0.5 + Math.random() * 0.5;
|
|
18650
|
+
return Math.floor(capped * jitter);
|
|
18651
|
+
};
|
|
18652
|
+
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
18653
|
+
if (signal?.aborted) {
|
|
18654
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
18655
|
+
return;
|
|
18656
|
+
}
|
|
18657
|
+
const timer = setTimeout(resolve, ms);
|
|
18658
|
+
signal?.addEventListener("abort", () => {
|
|
18659
|
+
clearTimeout(timer);
|
|
18660
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
18661
|
+
}, { once: true });
|
|
18662
|
+
});
|
|
18663
|
+
var withRetry = async (fn, config, signal) => {
|
|
18664
|
+
const merged = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
18665
|
+
let lastError;
|
|
18666
|
+
for (let attempt = 0; attempt <= merged.maxRetries; attempt++) {
|
|
18667
|
+
try {
|
|
18668
|
+
return await fn();
|
|
18669
|
+
} catch (error) {
|
|
18670
|
+
lastError = error;
|
|
18671
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
18672
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
18673
|
+
if (signal?.aborted) throw error;
|
|
18674
|
+
if (attempt >= merged.maxRetries) throw error;
|
|
18675
|
+
if (error instanceof ChatError && !error.retryable) throw error;
|
|
18676
|
+
await sleep(calculateDelay(attempt, merged), signal);
|
|
18677
|
+
}
|
|
18678
|
+
}
|
|
18679
|
+
throw lastError;
|
|
18680
|
+
};
|
|
18359
18681
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18360
18682
|
0 && (module.exports = {
|
|
18361
18683
|
ArtifactCard,
|
|
18684
|
+
ChatError,
|
|
18362
18685
|
ChatFloatingWidget,
|
|
18363
18686
|
ChatHeader,
|
|
18364
18687
|
ChatInput,
|
|
@@ -18371,6 +18694,7 @@ var MemoryPanel = ({
|
|
|
18371
18694
|
ContentPartRenderer,
|
|
18372
18695
|
DEFAULT_PROJECT_ID,
|
|
18373
18696
|
DEFAULT_PROJECT_TITLE,
|
|
18697
|
+
DEFAULT_RETRY_CONFIG,
|
|
18374
18698
|
DeepResearchProgressUI,
|
|
18375
18699
|
DevDiveAvatar,
|
|
18376
18700
|
DevDiveFabCharacter,
|
|
@@ -18394,20 +18718,28 @@ var MemoryPanel = ({
|
|
|
18394
18718
|
ResizeHandles,
|
|
18395
18719
|
SettingsModal,
|
|
18396
18720
|
SkillProgressUI,
|
|
18721
|
+
classifyFetchError,
|
|
18397
18722
|
convertSkillsToOpenAITools,
|
|
18398
18723
|
convertToolsToSkills,
|
|
18399
18724
|
createAdvancedResearchSkill,
|
|
18400
18725
|
createConversationSearchSkill,
|
|
18401
18726
|
createDeepResearchSkill,
|
|
18727
|
+
createTimeoutError,
|
|
18402
18728
|
migrateSessionsToProjects,
|
|
18729
|
+
parseSSELine,
|
|
18730
|
+
parseSSEResponse,
|
|
18403
18731
|
useChatUI,
|
|
18732
|
+
useChecklist,
|
|
18733
|
+
useContentParsers,
|
|
18404
18734
|
useDeepResearch,
|
|
18405
18735
|
useDragResize,
|
|
18406
18736
|
useFloatingWidget,
|
|
18407
18737
|
useImageError,
|
|
18408
18738
|
useObserver,
|
|
18409
18739
|
useProject,
|
|
18410
|
-
useSkills
|
|
18740
|
+
useSkills,
|
|
18741
|
+
useStreamingFetch,
|
|
18742
|
+
withRetry
|
|
18411
18743
|
});
|
|
18412
18744
|
/**
|
|
18413
18745
|
* @description localStorage 기반 메모리 저장소 어댑터
|