@gendive/chatllm 0.22.1 → 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 +1254 -941
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1053 -750
- 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;
|
|
@@ -2334,6 +2776,12 @@ var useChatUI = (options) => {
|
|
|
2334
2776
|
onSendMessage,
|
|
2335
2777
|
onSessionChange,
|
|
2336
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,
|
|
2337
2785
|
onTitleChange,
|
|
2338
2786
|
generateTitle: generateTitleCallback,
|
|
2339
2787
|
// Memory options
|
|
@@ -2395,74 +2843,78 @@ var useChatUI = (options) => {
|
|
|
2395
2843
|
onChecklistStepModel
|
|
2396
2844
|
} = options;
|
|
2397
2845
|
const enableAutoExtraction = enableAutoExtractionProp ?? !useExternalStorage;
|
|
2398
|
-
const [sessions, setSessions] = (0,
|
|
2399
|
-
const [currentSessionId, setCurrentSessionId] = (0,
|
|
2400
|
-
const [input, setInput] = (0,
|
|
2401
|
-
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());
|
|
2402
2850
|
const isLoading = currentSessionId !== null && loadingSessionIds.has(currentSessionId);
|
|
2403
|
-
const addLoadingSession = (0,
|
|
2404
|
-
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) => {
|
|
2405
2853
|
const next = new Set(prev);
|
|
2406
2854
|
next.delete(id);
|
|
2407
2855
|
return next;
|
|
2408
2856
|
}), []);
|
|
2409
|
-
const [selectedModel, setSelectedModel] = (0,
|
|
2410
|
-
const [sidebarOpen, setSidebarOpen] = (0,
|
|
2411
|
-
const [settingsOpen, setSettingsOpen] = (0,
|
|
2412
|
-
const [quotedText, setQuotedText] = (0,
|
|
2413
|
-
const [selectedAction, setSelectedAction] = (0,
|
|
2414
|
-
const [copiedMessageId, setCopiedMessageId] = (0,
|
|
2415
|
-
const [editingMessageId, setEditingMessageId] = (0,
|
|
2416
|
-
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)({
|
|
2417
2865
|
...DEFAULT_PERSONALIZATION,
|
|
2418
2866
|
...initialPersonalization
|
|
2419
2867
|
});
|
|
2420
|
-
const [activeAlternatives, setActiveAlternatives] = (0,
|
|
2421
|
-
const [loadingAlternativeFor, setLoadingAlternativeFor] = (0,
|
|
2422
|
-
const [isSessionsLoading, setIsSessionsLoading] = (0,
|
|
2423
|
-
const [isSessionLoading, setIsSessionLoading] = (0,
|
|
2424
|
-
const [isDeepResearchMode, setIsDeepResearchMode] = (0,
|
|
2425
|
-
const [attachments, setAttachments] = (0,
|
|
2426
|
-
const [isModelsLoading, setIsModelsLoading] = (0,
|
|
2427
|
-
const [loadedModels, setLoadedModels] = (0,
|
|
2428
|
-
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)(
|
|
2429
2877
|
null
|
|
2430
2878
|
);
|
|
2431
|
-
const sessionsRef = (0,
|
|
2432
|
-
(0,
|
|
2879
|
+
const sessionsRef = (0, import_react8.useRef)(sessions);
|
|
2880
|
+
(0, import_react8.useEffect)(() => {
|
|
2433
2881
|
sessionsRef.current = sessions;
|
|
2434
2882
|
}, [sessions]);
|
|
2435
|
-
const modelsRef = (0,
|
|
2436
|
-
(0,
|
|
2883
|
+
const modelsRef = (0, import_react8.useRef)(models);
|
|
2884
|
+
(0, import_react8.useEffect)(() => {
|
|
2437
2885
|
modelsRef.current = models;
|
|
2438
2886
|
}, [models]);
|
|
2439
|
-
const onSendMessageRef = (0,
|
|
2440
|
-
const onResponseHeadersRef = (0,
|
|
2441
|
-
const onSessionChangeRef = (0,
|
|
2442
|
-
const onErrorRef = (0,
|
|
2443
|
-
const
|
|
2444
|
-
const
|
|
2445
|
-
const
|
|
2446
|
-
const
|
|
2447
|
-
const
|
|
2448
|
-
const
|
|
2449
|
-
const
|
|
2450
|
-
const
|
|
2451
|
-
const
|
|
2452
|
-
const
|
|
2453
|
-
const
|
|
2454
|
-
const
|
|
2455
|
-
const
|
|
2456
|
-
const
|
|
2457
|
-
const
|
|
2458
|
-
const
|
|
2459
|
-
const
|
|
2460
|
-
const
|
|
2461
|
-
(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)(() => {
|
|
2462
2912
|
onSendMessageRef.current = onSendMessage;
|
|
2463
2913
|
onResponseHeadersRef.current = options.onResponseHeaders;
|
|
2464
2914
|
onSessionChangeRef.current = onSessionChange;
|
|
2465
2915
|
onErrorRef.current = onError;
|
|
2916
|
+
onAbortRef.current = onAbort;
|
|
2917
|
+
onTokenUsageRef.current = onTokenUsage;
|
|
2466
2918
|
onTitleChangeRef.current = onTitleChange;
|
|
2467
2919
|
generateTitleRef.current = generateTitleCallback;
|
|
2468
2920
|
onPersonalizationChangeRef.current = options.onPersonalizationChange;
|
|
@@ -2481,15 +2933,19 @@ var useChatUI = (options) => {
|
|
|
2481
2933
|
fileUploaderRef.current = fileUploader;
|
|
2482
2934
|
onLoadModelsRef.current = onLoadModels;
|
|
2483
2935
|
});
|
|
2484
|
-
const abortControllersRef = (0,
|
|
2485
|
-
const
|
|
2486
|
-
const
|
|
2487
|
-
const
|
|
2488
|
-
const
|
|
2489
|
-
const
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2492
|
-
|
|
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);
|
|
2493
2949
|
const resolveChecklistRefImage = (item, checklistMessageId, sessionId) => {
|
|
2494
2950
|
const session = sessionsRef.current.find((s) => s.id === sessionId);
|
|
2495
2951
|
if (!session) return null;
|
|
@@ -2556,7 +3012,7 @@ ${hints.join(" ")}` : "";
|
|
|
2556
3012
|
return `${stepLabel}
|
|
2557
3013
|
${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
2558
3014
|
};
|
|
2559
|
-
const memoryOptions = (0,
|
|
3015
|
+
const memoryOptions = (0, import_react8.useMemo)(
|
|
2560
3016
|
() => ({
|
|
2561
3017
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2562
3018
|
storageKey: globalMemoryConfig?.localStorage?.key || `${storageKey}_memory`,
|
|
@@ -2570,11 +3026,11 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2570
3026
|
const globalMemoryRaw = useGlobalMemory(memoryOptions);
|
|
2571
3027
|
const globalMemory = useGlobalMemoryEnabled ? globalMemoryRaw : null;
|
|
2572
3028
|
globalMemoryRef.current = globalMemory;
|
|
2573
|
-
const stableToolCall = (0,
|
|
3029
|
+
const stableToolCall = (0, import_react8.useCallback)(
|
|
2574
3030
|
(name, params) => onToolCallRef.current(name, params),
|
|
2575
3031
|
[]
|
|
2576
3032
|
);
|
|
2577
|
-
const mergedSkills = (0,
|
|
3033
|
+
const mergedSkills = (0, import_react8.useMemo)(() => {
|
|
2578
3034
|
if (!tools || !onToolCall) return skills || {};
|
|
2579
3035
|
const toolSkills = convertToolsToSkills(tools, stableToolCall);
|
|
2580
3036
|
return { ...skills || {}, ...toolSkills };
|
|
@@ -2610,9 +3066,9 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2610
3066
|
onDeleteProjectFile,
|
|
2611
3067
|
onError
|
|
2612
3068
|
});
|
|
2613
|
-
const [projectSettingsOpen, setProjectSettingsOpen] = (0,
|
|
3069
|
+
const [projectSettingsOpen, setProjectSettingsOpen] = (0, import_react8.useState)(false);
|
|
2614
3070
|
const projectMemoryKey = enableProjects && projectHook.currentProjectId ? `${storageKey}_project_memory_${projectHook.currentProjectId}` : `${storageKey}_project_memory_none`;
|
|
2615
|
-
const projectMemoryOptions = (0,
|
|
3071
|
+
const projectMemoryOptions = (0, import_react8.useMemo)(
|
|
2616
3072
|
() => ({
|
|
2617
3073
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2618
3074
|
storageKey: projectMemoryKey,
|
|
@@ -2624,7 +3080,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2624
3080
|
);
|
|
2625
3081
|
const projectMemoryRaw = useGlobalMemory(projectMemoryOptions);
|
|
2626
3082
|
const projectMemory = enableProjects ? projectMemoryRaw : null;
|
|
2627
|
-
const unwrapResponseHeaders = (0,
|
|
3083
|
+
const unwrapResponseHeaders = (0, import_react8.useCallback)((result) => {
|
|
2628
3084
|
if (typeof result === "object" && result !== null && "response" in result && "headers" in result) {
|
|
2629
3085
|
const wrapped = result;
|
|
2630
3086
|
onResponseHeadersRef.current?.(wrapped.headers);
|
|
@@ -2632,7 +3088,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2632
3088
|
}
|
|
2633
3089
|
return result;
|
|
2634
3090
|
}, []);
|
|
2635
|
-
const emitFetchHeaders = (0,
|
|
3091
|
+
const emitFetchHeaders = (0, import_react8.useCallback)((response) => {
|
|
2636
3092
|
if (onResponseHeadersRef.current && response.headers) {
|
|
2637
3093
|
const headerMap = {};
|
|
2638
3094
|
response.headers.forEach((v, k) => {
|
|
@@ -2641,7 +3097,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2641
3097
|
onResponseHeadersRef.current(headerMap);
|
|
2642
3098
|
}
|
|
2643
3099
|
}, []);
|
|
2644
|
-
const callInternalLLM = (0,
|
|
3100
|
+
const callInternalLLM = (0, import_react8.useCallback)(async (prompt, model) => {
|
|
2645
3101
|
if (onSendMessageRef.current) {
|
|
2646
3102
|
const modelConfig = modelsRef.current.find((m) => m.id === model);
|
|
2647
3103
|
const provider = modelConfig?.provider || "ollama";
|
|
@@ -2679,11 +3135,11 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2679
3135
|
const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
|
|
2680
3136
|
const messages = currentSession?.messages.filter((m) => !m.hidden) || [];
|
|
2681
3137
|
const compressionState = currentSession?.compressionState || null;
|
|
2682
|
-
const visibleSessions = (0,
|
|
3138
|
+
const visibleSessions = (0, import_react8.useMemo)(() => {
|
|
2683
3139
|
if (!enableProjects || !projectHook.currentProjectId) return sessions;
|
|
2684
3140
|
return sessions.filter((s) => s.projectId === projectHook.currentProjectId);
|
|
2685
3141
|
}, [sessions, enableProjects, projectHook.currentProjectId]);
|
|
2686
|
-
(0,
|
|
3142
|
+
(0, import_react8.useEffect)(() => {
|
|
2687
3143
|
if (typeof window === "undefined") return;
|
|
2688
3144
|
if (useExternalStorage && onLoadSessionsRef.current) {
|
|
2689
3145
|
setIsSessionsLoading(true);
|
|
@@ -2745,7 +3201,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2745
3201
|
}
|
|
2746
3202
|
}
|
|
2747
3203
|
}, [storageKey, useExternalStorage, initialModel, startWithNewSession]);
|
|
2748
|
-
(0,
|
|
3204
|
+
(0, import_react8.useEffect)(() => {
|
|
2749
3205
|
if (!onLoadModelsRef.current) return;
|
|
2750
3206
|
setIsModelsLoading(true);
|
|
2751
3207
|
onLoadModelsRef.current().then((modelList) => {
|
|
@@ -2760,21 +3216,21 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2760
3216
|
});
|
|
2761
3217
|
}, []);
|
|
2762
3218
|
const effectiveModels = loadedModels || models;
|
|
2763
|
-
(0,
|
|
3219
|
+
(0, import_react8.useEffect)(() => {
|
|
2764
3220
|
if (typeof window === "undefined") return;
|
|
2765
3221
|
if (useExternalStorage) return;
|
|
2766
3222
|
if (sessions.length > 0) {
|
|
2767
3223
|
localStorage.setItem(storageKey, JSON.stringify(sessions));
|
|
2768
3224
|
}
|
|
2769
3225
|
}, [sessions, storageKey, useExternalStorage]);
|
|
2770
|
-
(0,
|
|
3226
|
+
(0, import_react8.useEffect)(() => {
|
|
2771
3227
|
if (typeof window === "undefined") return;
|
|
2772
3228
|
localStorage.setItem(`${storageKey}_personalization`, JSON.stringify(personalization));
|
|
2773
3229
|
}, [personalization, storageKey]);
|
|
2774
|
-
(0,
|
|
3230
|
+
(0, import_react8.useEffect)(() => {
|
|
2775
3231
|
onSessionChangeRef.current?.(currentSession);
|
|
2776
3232
|
}, [currentSession]);
|
|
2777
|
-
const buildSystemPrompt = (0,
|
|
3233
|
+
const buildSystemPrompt = (0, import_react8.useCallback)((session) => {
|
|
2778
3234
|
const parts = [];
|
|
2779
3235
|
const { userProfile, responseStyle, language } = personalization;
|
|
2780
3236
|
const identityName = assistantName || "AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8";
|
|
@@ -3133,7 +3589,7 @@ AI (\uD655\uC815):
|
|
|
3133
3589
|
}
|
|
3134
3590
|
return parts.length > 0 ? parts.join("\n") : "";
|
|
3135
3591
|
}, [personalization, globalMemory, useGlobalMemoryEnabled, enablePoll, enableChecklist, buildSkillsPrompt, enableProjects, projectHook.currentProject, projectMemory, resolvedSkills, assistantName, onBuildSystemPrompt, compactSystemPrompt, selectedModel]);
|
|
3136
|
-
const promoteToSessionContext = (0,
|
|
3592
|
+
const promoteToSessionContext = (0, import_react8.useCallback)((sessionId, skillName, content, metadata, label) => {
|
|
3137
3593
|
const item = createSessionContextItem(skillName, content, metadata, label);
|
|
3138
3594
|
setSessions(
|
|
3139
3595
|
(prev) => prev.map((s) => {
|
|
@@ -3144,7 +3600,7 @@ AI (\uD655\uC815):
|
|
|
3144
3600
|
})
|
|
3145
3601
|
);
|
|
3146
3602
|
}, []);
|
|
3147
|
-
const compressContext = (0,
|
|
3603
|
+
const compressContext = (0, import_react8.useCallback)(async (messagesToCompress, model) => {
|
|
3148
3604
|
const conversationText = messagesToCompress.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3149
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.
|
|
3150
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.
|
|
@@ -3159,7 +3615,7 @@ ${conversationText}
|
|
|
3159
3615
|
return "";
|
|
3160
3616
|
}
|
|
3161
3617
|
}, [callInternalLLM]);
|
|
3162
|
-
const incrementalCompressContext = (0,
|
|
3618
|
+
const incrementalCompressContext = (0, import_react8.useCallback)(
|
|
3163
3619
|
async (existingSummary, newMessages, model) => {
|
|
3164
3620
|
const newConversation = newMessages.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3165
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.
|
|
@@ -3186,10 +3642,10 @@ ${newConversation}
|
|
|
3186
3642
|
},
|
|
3187
3643
|
[callInternalLLM]
|
|
3188
3644
|
);
|
|
3189
|
-
const estimateTokens = (0,
|
|
3645
|
+
const estimateTokens = (0, import_react8.useCallback)((messages2) => {
|
|
3190
3646
|
return messages2.reduce((sum, m) => sum + Math.ceil(m.content.length / 4), 0);
|
|
3191
3647
|
}, []);
|
|
3192
|
-
const newSession = (0,
|
|
3648
|
+
const newSession = (0, import_react8.useCallback)(async () => {
|
|
3193
3649
|
const projectId = enableProjects ? projectHook.currentProjectId || DEFAULT_PROJECT_ID : void 0;
|
|
3194
3650
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
3195
3651
|
setIsSessionLoading(true);
|
|
@@ -3227,7 +3683,7 @@ ${newConversation}
|
|
|
3227
3683
|
setSessions((prev) => [newSess, ...prev]);
|
|
3228
3684
|
setCurrentSessionId(newSess.id);
|
|
3229
3685
|
}, [selectedModel, useExternalStorage, enableProjects, projectHook.currentProjectId]);
|
|
3230
|
-
const selectSession = (0,
|
|
3686
|
+
const selectSession = (0, import_react8.useCallback)(async (id) => {
|
|
3231
3687
|
if (useExternalStorage && onLoadSessionRef.current) {
|
|
3232
3688
|
setIsSessionLoading(true);
|
|
3233
3689
|
try {
|
|
@@ -3331,13 +3787,13 @@ ${newConversation}
|
|
|
3331
3787
|
setSelectedModel(session.model);
|
|
3332
3788
|
}
|
|
3333
3789
|
}, [sessions, useExternalStorage, storageKey, initialModel]);
|
|
3334
|
-
(0,
|
|
3790
|
+
(0, import_react8.useEffect)(() => {
|
|
3335
3791
|
if (!pendingInitialLoadRef.current || !useExternalStorage) return;
|
|
3336
3792
|
const id = pendingInitialLoadRef.current;
|
|
3337
3793
|
pendingInitialLoadRef.current = null;
|
|
3338
3794
|
selectSession(id);
|
|
3339
3795
|
}, [currentSessionId, selectSession, useExternalStorage]);
|
|
3340
|
-
const deleteSession = (0,
|
|
3796
|
+
const deleteSession = (0, import_react8.useCallback)(async (id) => {
|
|
3341
3797
|
if (useExternalStorage && onDeleteSessionCallbackRef.current) {
|
|
3342
3798
|
try {
|
|
3343
3799
|
await onDeleteSessionCallbackRef.current(id);
|
|
@@ -3365,7 +3821,7 @@ ${newConversation}
|
|
|
3365
3821
|
return filtered;
|
|
3366
3822
|
});
|
|
3367
3823
|
}, [currentSessionId, storageKey, useExternalStorage]);
|
|
3368
|
-
const renameSession = (0,
|
|
3824
|
+
const renameSession = (0, import_react8.useCallback)(async (id, newTitle) => {
|
|
3369
3825
|
if (!newTitle.trim()) return;
|
|
3370
3826
|
if (useExternalStorage && onUpdateSessionTitleRef.current) {
|
|
3371
3827
|
try {
|
|
@@ -3388,7 +3844,7 @@ ${newConversation}
|
|
|
3388
3844
|
);
|
|
3389
3845
|
onTitleChangeRef.current?.(id, newTitle.trim());
|
|
3390
3846
|
}, [useExternalStorage]);
|
|
3391
|
-
const setModel = (0,
|
|
3847
|
+
const setModel = (0, import_react8.useCallback)((model) => {
|
|
3392
3848
|
setSelectedModel(model);
|
|
3393
3849
|
if (currentSessionId) {
|
|
3394
3850
|
setSessions(
|
|
@@ -3396,10 +3852,10 @@ ${newConversation}
|
|
|
3396
3852
|
);
|
|
3397
3853
|
}
|
|
3398
3854
|
}, [currentSessionId]);
|
|
3399
|
-
const toggleSidebar = (0,
|
|
3400
|
-
const openSettings = (0,
|
|
3401
|
-
const closeSettings = (0,
|
|
3402
|
-
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) => {
|
|
3403
3859
|
if (typeof navigator !== "undefined") {
|
|
3404
3860
|
navigator.clipboard.writeText(content).then(() => {
|
|
3405
3861
|
setCopiedMessageId(id);
|
|
@@ -3407,30 +3863,30 @@ ${newConversation}
|
|
|
3407
3863
|
});
|
|
3408
3864
|
}
|
|
3409
3865
|
}, []);
|
|
3410
|
-
const startEdit = (0,
|
|
3866
|
+
const startEdit = (0, import_react8.useCallback)((message) => {
|
|
3411
3867
|
if (message.role === "user") {
|
|
3412
3868
|
setEditingMessageId(message.id);
|
|
3413
3869
|
}
|
|
3414
3870
|
}, []);
|
|
3415
|
-
const cancelEdit = (0,
|
|
3871
|
+
const cancelEdit = (0, import_react8.useCallback)(() => {
|
|
3416
3872
|
setEditingMessageId(null);
|
|
3417
3873
|
}, []);
|
|
3418
|
-
const stopGeneration = (0,
|
|
3874
|
+
const stopGeneration = (0, import_react8.useCallback)(() => {
|
|
3419
3875
|
if (currentSessionId) {
|
|
3420
3876
|
abortControllersRef.current.get(currentSessionId)?.abort();
|
|
3421
3877
|
}
|
|
3422
3878
|
}, [currentSessionId]);
|
|
3423
|
-
const updatePersonalization = (0,
|
|
3879
|
+
const updatePersonalization = (0, import_react8.useCallback)((config) => {
|
|
3424
3880
|
setPersonalization((prev) => {
|
|
3425
3881
|
const next = { ...prev, ...config };
|
|
3426
3882
|
onPersonalizationChangeRef.current?.(next);
|
|
3427
3883
|
return next;
|
|
3428
3884
|
});
|
|
3429
3885
|
}, []);
|
|
3430
|
-
const savePersonalization = (0,
|
|
3886
|
+
const savePersonalization = (0, import_react8.useCallback)(() => {
|
|
3431
3887
|
onPersonalizationSaveRef.current?.(personalization);
|
|
3432
3888
|
}, [personalization]);
|
|
3433
|
-
const addAttachments = (0,
|
|
3889
|
+
const addAttachments = (0, import_react8.useCallback)((files) => {
|
|
3434
3890
|
const newAttachments = files.map((file) => {
|
|
3435
3891
|
const isImage = file.type.startsWith("image/");
|
|
3436
3892
|
return {
|
|
@@ -3445,7 +3901,7 @@ ${newConversation}
|
|
|
3445
3901
|
});
|
|
3446
3902
|
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
3447
3903
|
}, []);
|
|
3448
|
-
const removeAttachment = (0,
|
|
3904
|
+
const removeAttachment = (0, import_react8.useCallback)((id) => {
|
|
3449
3905
|
setAttachments((prev) => {
|
|
3450
3906
|
const target = prev.find((a) => a.id === id);
|
|
3451
3907
|
if (target?.previewUrl) {
|
|
@@ -3454,17 +3910,17 @@ ${newConversation}
|
|
|
3454
3910
|
return prev.filter((a) => a.id !== id);
|
|
3455
3911
|
});
|
|
3456
3912
|
}, []);
|
|
3457
|
-
const toggleDeepResearchMode = (0,
|
|
3913
|
+
const toggleDeepResearchMode = (0, import_react8.useCallback)(() => {
|
|
3458
3914
|
setIsDeepResearchMode((prev) => !prev);
|
|
3459
3915
|
}, []);
|
|
3460
|
-
const trackBehavior = (0,
|
|
3916
|
+
const trackBehavior = (0, import_react8.useCallback)(async (key, updater) => {
|
|
3461
3917
|
if (!globalMemory) return;
|
|
3462
3918
|
const fullKey = `behavior.${key}`;
|
|
3463
3919
|
const prev = globalMemory.get(fullKey) ?? {};
|
|
3464
3920
|
const next = updater(prev);
|
|
3465
3921
|
await globalMemory.set(fullKey, next, { category: "behavior" });
|
|
3466
3922
|
}, [globalMemory]);
|
|
3467
|
-
const trackMessageLength = (0,
|
|
3923
|
+
const trackMessageLength = (0, import_react8.useCallback)((length) => {
|
|
3468
3924
|
trackBehavior("messageLength", (prev) => {
|
|
3469
3925
|
const samples = (prev.messageLengthSamples || []).slice(-19);
|
|
3470
3926
|
samples.push(length);
|
|
@@ -3472,39 +3928,69 @@ ${newConversation}
|
|
|
3472
3928
|
return { ...prev, messageLengthSamples: samples, avgMessageLength: avg };
|
|
3473
3929
|
});
|
|
3474
3930
|
}, [trackBehavior]);
|
|
3475
|
-
const trackSkillUsage = (0,
|
|
3931
|
+
const trackSkillUsage = (0, import_react8.useCallback)((skillName) => {
|
|
3476
3932
|
trackBehavior("skillUsage", (prev) => ({
|
|
3477
3933
|
...prev,
|
|
3478
3934
|
[skillName]: (prev[skillName] || 0) + 1
|
|
3479
3935
|
}));
|
|
3480
3936
|
}, [trackBehavior]);
|
|
3481
|
-
const trackChecklistSkip = (0,
|
|
3937
|
+
const trackChecklistSkip = (0, import_react8.useCallback)(() => {
|
|
3482
3938
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3483
3939
|
total: (prev.total || 0) + 1,
|
|
3484
3940
|
skipped: (prev.skipped || 0) + 1
|
|
3485
3941
|
}));
|
|
3486
3942
|
}, [trackBehavior]);
|
|
3487
|
-
const trackChecklistComplete = (0,
|
|
3943
|
+
const trackChecklistComplete = (0, import_react8.useCallback)(() => {
|
|
3488
3944
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3489
3945
|
total: (prev.total || 0) + 1,
|
|
3490
3946
|
skipped: prev.skipped || 0
|
|
3491
3947
|
}));
|
|
3492
3948
|
}, [trackBehavior]);
|
|
3493
|
-
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)(() => {
|
|
3494
3970
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3495
3971
|
...prev,
|
|
3496
3972
|
regenerated: (prev.regenerated || 0) + 1
|
|
3497
3973
|
}));
|
|
3498
3974
|
}, [trackBehavior]);
|
|
3499
|
-
const trackMessageSent = (0,
|
|
3975
|
+
const trackMessageSent = (0, import_react8.useCallback)(() => {
|
|
3500
3976
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3501
3977
|
total: (prev.total || 0) + 1,
|
|
3502
3978
|
regenerated: prev.regenerated || 0
|
|
3503
3979
|
}));
|
|
3504
3980
|
}, [trackBehavior]);
|
|
3505
|
-
const sendMessage = (0,
|
|
3981
|
+
const sendMessage = (0, import_react8.useCallback)(async (content, options2) => {
|
|
3506
3982
|
const messageContent = content || input;
|
|
3507
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
|
+
}
|
|
3508
3994
|
let sessionId = currentSessionId;
|
|
3509
3995
|
if (!sessionId) {
|
|
3510
3996
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
@@ -3790,10 +4276,11 @@ ${finalContent}`;
|
|
|
3790
4276
|
return;
|
|
3791
4277
|
}
|
|
3792
4278
|
abortControllersRef.current.set(capturedSessionId, new AbortController());
|
|
4279
|
+
let accumulatedContent = "";
|
|
4280
|
+
let lastUsage = null;
|
|
3793
4281
|
try {
|
|
3794
4282
|
const shouldSkipSkillParsing = skipNextSkillParsingRef.current;
|
|
3795
4283
|
skipNextSkillParsingRef.current = false;
|
|
3796
|
-
let accumulatedContent = "";
|
|
3797
4284
|
let checklistStepImageUrl = null;
|
|
3798
4285
|
let messagesToSend = [...existingMessages, userMessage];
|
|
3799
4286
|
const recompressionThreshold = DEFAULT_RECOMPRESSION_THRESHOLD;
|
|
@@ -3999,119 +4486,71 @@ ${attachmentContext}
|
|
|
3999
4486
|
emitFetchHeaders(response);
|
|
4000
4487
|
}
|
|
4001
4488
|
if (response) {
|
|
4002
|
-
if (!response.ok) throw new Error("API error");
|
|
4003
|
-
const reader = response.body?.getReader();
|
|
4004
|
-
if (!reader) throw new Error("No reader");
|
|
4005
|
-
const decoder = new TextDecoder();
|
|
4006
|
-
let buffer = "";
|
|
4007
4489
|
let skillTagDetected = false;
|
|
4008
4490
|
let checklistTagDetected = false;
|
|
4009
4491
|
let toolCallAcc = null;
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
buffer += decoder.decode(value, { stream: true });
|
|
4014
|
-
const lines = buffer.split("\n");
|
|
4015
|
-
buffer = lines.pop() || "";
|
|
4016
|
-
for (const line of lines) {
|
|
4017
|
-
if (!line.trim()) continue;
|
|
4018
|
-
let data = line;
|
|
4019
|
-
if (line.startsWith("data: ")) {
|
|
4020
|
-
data = line.slice(6);
|
|
4021
|
-
if (data === "[DONE]") continue;
|
|
4022
|
-
}
|
|
4023
|
-
try {
|
|
4024
|
-
const parsed = JSON.parse(data);
|
|
4025
|
-
const delta = parsed.choices?.[0]?.delta;
|
|
4026
|
-
const finishReason = parsed.choices?.[0]?.finish_reason;
|
|
4027
|
-
if (useNativeTools && delta?.tool_calls) {
|
|
4028
|
-
toolCallAcc = accumulateToolCallDelta(toolCallAcc, delta);
|
|
4029
|
-
}
|
|
4030
|
-
if (useNativeTools && finishReason === "tool_calls" && toolCallAcc) {
|
|
4031
|
-
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4032
|
-
skillTagDetected = true;
|
|
4033
|
-
break;
|
|
4034
|
-
}
|
|
4035
|
-
const content2 = delta?.content || parsed.message?.content || parsed.content || parsed.text || "";
|
|
4036
|
-
const thinking = parsed.message?.thinking || "";
|
|
4037
|
-
if (content2 || thinking) {
|
|
4038
|
-
if (content2) accumulatedContent += content2;
|
|
4039
|
-
if (!shouldSkipSkillParsing && accumulatedContent.includes("</skill_use>")) {
|
|
4040
|
-
const endIdx = accumulatedContent.indexOf("</skill_use>");
|
|
4041
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</skill_use>".length);
|
|
4042
|
-
skillTagDetected = true;
|
|
4043
|
-
}
|
|
4044
|
-
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
4045
|
-
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
4046
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
4047
|
-
checklistTagDetected = true;
|
|
4048
|
-
}
|
|
4049
|
-
const displayContent = skillTagDetected ? accumulatedContent : null;
|
|
4050
|
-
setSessions(
|
|
4051
|
-
(prev) => prev.map((s) => {
|
|
4052
|
-
if (s.id === capturedSessionId) {
|
|
4053
|
-
return {
|
|
4054
|
-
...s,
|
|
4055
|
-
messages: s.messages.map((m) => {
|
|
4056
|
-
if (m.id !== assistantMessageId) return m;
|
|
4057
|
-
if (displayContent) {
|
|
4058
|
-
return { ...m, content: displayContent };
|
|
4059
|
-
}
|
|
4060
|
-
let newContent = m.content;
|
|
4061
|
-
if (thinking) {
|
|
4062
|
-
if (!newContent.includes("<thinking>")) {
|
|
4063
|
-
newContent = "<thinking>" + thinking;
|
|
4064
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
4065
|
-
newContent += thinking;
|
|
4066
|
-
}
|
|
4067
|
-
}
|
|
4068
|
-
if (content2) {
|
|
4069
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
4070
|
-
newContent += "</thinking>\n\n";
|
|
4071
|
-
}
|
|
4072
|
-
newContent += content2;
|
|
4073
|
-
}
|
|
4074
|
-
return { ...m, content: newContent };
|
|
4075
|
-
})
|
|
4076
|
-
};
|
|
4077
|
-
}
|
|
4078
|
-
return s;
|
|
4079
|
-
})
|
|
4080
|
-
);
|
|
4081
|
-
if (skillTagDetected || checklistTagDetected) break;
|
|
4082
|
-
}
|
|
4083
|
-
} catch {
|
|
4084
|
-
}
|
|
4492
|
+
const streamResult = await streamResponse(response, (chunk) => {
|
|
4493
|
+
if (useNativeTools && chunk.delta?.tool_calls) {
|
|
4494
|
+
toolCallAcc = accumulateToolCallDelta(toolCallAcc, chunk.delta);
|
|
4085
4495
|
}
|
|
4086
|
-
if (
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
skillTagDetected = true;
|
|
4091
|
-
}
|
|
4092
|
-
if (buffer.trim()) {
|
|
4093
|
-
try {
|
|
4094
|
-
const parsed = JSON.parse(buffer);
|
|
4095
|
-
const content2 = parsed.message?.content || parsed.content || parsed.text;
|
|
4096
|
-
if (content2) {
|
|
4097
|
-
accumulatedContent += content2;
|
|
4098
|
-
setSessions(
|
|
4099
|
-
(prev) => prev.map((s) => {
|
|
4100
|
-
if (s.id === capturedSessionId) {
|
|
4101
|
-
return {
|
|
4102
|
-
...s,
|
|
4103
|
-
messages: s.messages.map(
|
|
4104
|
-
(m) => m.id === assistantMessageId ? { ...m, content: m.content + content2 } : m
|
|
4105
|
-
)
|
|
4106
|
-
};
|
|
4107
|
-
}
|
|
4108
|
-
return s;
|
|
4109
|
-
})
|
|
4110
|
-
);
|
|
4111
|
-
}
|
|
4112
|
-
} catch {
|
|
4496
|
+
if (useNativeTools && chunk.finishReason === "tool_calls" && toolCallAcc) {
|
|
4497
|
+
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4498
|
+
skillTagDetected = true;
|
|
4499
|
+
return "break";
|
|
4113
4500
|
}
|
|
4114
|
-
|
|
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;
|
|
4508
|
+
}
|
|
4509
|
+
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
4510
|
+
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
4511
|
+
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
4512
|
+
checklistTagDetected = true;
|
|
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";
|
|
4547
|
+
}
|
|
4548
|
+
});
|
|
4549
|
+
lastUsage = streamResult.usage;
|
|
4550
|
+
if (useNativeTools && toolCallAcc && !skillTagDetected) {
|
|
4551
|
+
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4552
|
+
skillTagDetected = true;
|
|
4553
|
+
}
|
|
4115
4554
|
}
|
|
4116
4555
|
const saveMessagesOnEarlyReturn = (overrideContentParts) => {
|
|
4117
4556
|
if (!useExternalStorage || !capturedSessionId) return;
|
|
@@ -4472,9 +4911,10 @@ ${result.content}
|
|
|
4472
4911
|
}
|
|
4473
4912
|
skipNextSkillParsingRef.current = true;
|
|
4474
4913
|
saveMessagesOnEarlyReturn();
|
|
4914
|
+
const truncatedResult = result.content.length > maxToolResultSize ? result.content.substring(0, maxToolResultSize) + "\n\n...(truncated)" : result.content;
|
|
4475
4915
|
const resultPrompt = `\uC2A4\uD0AC "${detectedSkill.name}" \uC2E4\uD589 \uACB0\uACFC:
|
|
4476
4916
|
|
|
4477
|
-
${
|
|
4917
|
+
${truncatedResult}
|
|
4478
4918
|
|
|
4479
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.`;
|
|
4480
4920
|
setTimeout(() => {
|
|
@@ -4800,6 +5240,32 @@ ${stepSummary}
|
|
|
4800
5240
|
};
|
|
4801
5241
|
})
|
|
4802
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
|
+
}
|
|
4803
5269
|
if (useExternalStorage && capturedSessionId) {
|
|
4804
5270
|
const assistantContentForSave = accumulatedContent;
|
|
4805
5271
|
if (assistantContentForSave && onSaveMessagesRef.current) {
|
|
@@ -4839,18 +5305,32 @@ ${stepSummary}
|
|
|
4839
5305
|
observer.processMessages(capturedSessionId, newMessages);
|
|
4840
5306
|
}
|
|
4841
5307
|
} catch (error) {
|
|
4842
|
-
|
|
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
|
+
}
|
|
4843
5324
|
return;
|
|
4844
5325
|
}
|
|
4845
|
-
|
|
4846
|
-
onErrorRef.current?.(err);
|
|
5326
|
+
onErrorRef.current?.(classified);
|
|
4847
5327
|
setSessions(
|
|
4848
5328
|
(prev) => prev.map((s) => {
|
|
4849
5329
|
if (s.id === capturedSessionId) {
|
|
4850
5330
|
return {
|
|
4851
5331
|
...s,
|
|
4852
5332
|
messages: s.messages.map(
|
|
4853
|
-
(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
|
|
4854
5334
|
)
|
|
4855
5335
|
};
|
|
4856
5336
|
}
|
|
@@ -4883,241 +5363,8 @@ ${stepSummary}
|
|
|
4883
5363
|
attachments,
|
|
4884
5364
|
continueAfterToolResult
|
|
4885
5365
|
]);
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
const session = sessionsRef.current.find(
|
|
4889
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4890
|
-
);
|
|
4891
|
-
if (!session) return;
|
|
4892
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4893
|
-
if (!message?.checklistBlock) return;
|
|
4894
|
-
pendingChecklistRef.current = null;
|
|
4895
|
-
activeChecklistRef.current = {
|
|
4896
|
-
messageId,
|
|
4897
|
-
sessionId: session.id,
|
|
4898
|
-
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 })),
|
|
4899
|
-
currentStep: 0,
|
|
4900
|
-
stepResults: []
|
|
4901
|
-
};
|
|
4902
|
-
setSessions(
|
|
4903
|
-
(prev) => prev.map((s) => {
|
|
4904
|
-
if (s.id !== session.id) return s;
|
|
4905
|
-
return {
|
|
4906
|
-
...s,
|
|
4907
|
-
messages: s.messages.map((m) => {
|
|
4908
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4909
|
-
const updatedItems = m.checklistBlock.items.map((it, idx) => ({
|
|
4910
|
-
...it,
|
|
4911
|
-
status: idx === 0 ? "in_progress" : it.status
|
|
4912
|
-
}));
|
|
4913
|
-
return {
|
|
4914
|
-
...m,
|
|
4915
|
-
checklistBlock: { ...m.checklistBlock, items: updatedItems, currentStep: 0 }
|
|
4916
|
-
};
|
|
4917
|
-
})
|
|
4918
|
-
};
|
|
4919
|
-
})
|
|
4920
|
-
);
|
|
4921
|
-
skipNextChecklistParsingRef.current = true;
|
|
4922
|
-
setTimeout(() => {
|
|
4923
|
-
const items = message.checklistBlock.items;
|
|
4924
|
-
const refUrl = resolveChecklistRefImage(items[0], messageId, session.id);
|
|
4925
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4926
|
-
sendMessage(
|
|
4927
|
-
buildChecklistStepPrompt(0, items.length, items[0], true, refUrl),
|
|
4928
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: 0, title: items[0].title } }
|
|
4929
|
-
);
|
|
4930
|
-
}, 100);
|
|
4931
|
-
},
|
|
4932
|
-
[sendMessage]
|
|
4933
|
-
);
|
|
4934
|
-
const handleChecklistAbort = (0, import_react6.useCallback)(() => {
|
|
4935
|
-
if (!activeChecklistRef.current) return;
|
|
4936
|
-
const checklist = activeChecklistRef.current;
|
|
4937
|
-
const stepIdx = checklist.currentStep;
|
|
4938
|
-
abortControllersRef.current.get(checklist.sessionId)?.abort();
|
|
4939
|
-
setSessions(
|
|
4940
|
-
(prev) => prev.map((s) => {
|
|
4941
|
-
if (s.id !== checklist.sessionId) return s;
|
|
4942
|
-
return {
|
|
4943
|
-
...s,
|
|
4944
|
-
messages: s.messages.map((m) => {
|
|
4945
|
-
if (m.id !== checklist.messageId || !m.checklistBlock) return m;
|
|
4946
|
-
return {
|
|
4947
|
-
...m,
|
|
4948
|
-
checklistBlock: {
|
|
4949
|
-
...m.checklistBlock,
|
|
4950
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4951
|
-
...it,
|
|
4952
|
-
status: idx === stepIdx ? "error" : it.status
|
|
4953
|
-
}))
|
|
4954
|
-
}
|
|
4955
|
-
};
|
|
4956
|
-
})
|
|
4957
|
-
};
|
|
4958
|
-
})
|
|
4959
|
-
);
|
|
4960
|
-
activeChecklistRef.current = null;
|
|
4961
|
-
removeLoadingSession(checklist.sessionId);
|
|
4962
|
-
}, [removeLoadingSession]);
|
|
4963
|
-
const handleChecklistRetry = (0, import_react6.useCallback)(
|
|
4964
|
-
(messageId, stepIndex) => {
|
|
4965
|
-
const session = sessionsRef.current.find(
|
|
4966
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4967
|
-
);
|
|
4968
|
-
if (!session) return;
|
|
4969
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4970
|
-
if (!message?.checklistBlock) return;
|
|
4971
|
-
activeChecklistRef.current = {
|
|
4972
|
-
messageId,
|
|
4973
|
-
sessionId: session.id,
|
|
4974
|
-
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 })),
|
|
4975
|
-
currentStep: stepIndex,
|
|
4976
|
-
stepResults: message.checklistBlock.items.slice(0, stepIndex).map((it) => it.result || "")
|
|
4977
|
-
};
|
|
4978
|
-
setSessions(
|
|
4979
|
-
(prev) => prev.map((s) => {
|
|
4980
|
-
if (s.id !== session.id) return s;
|
|
4981
|
-
return {
|
|
4982
|
-
...s,
|
|
4983
|
-
messages: s.messages.map((m) => {
|
|
4984
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4985
|
-
return {
|
|
4986
|
-
...m,
|
|
4987
|
-
checklistBlock: {
|
|
4988
|
-
...m.checklistBlock,
|
|
4989
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4990
|
-
...it,
|
|
4991
|
-
status: idx === stepIndex ? "in_progress" : idx > stepIndex ? "pending" : it.status
|
|
4992
|
-
})),
|
|
4993
|
-
currentStep: stepIndex
|
|
4994
|
-
}
|
|
4995
|
-
};
|
|
4996
|
-
})
|
|
4997
|
-
};
|
|
4998
|
-
})
|
|
4999
|
-
);
|
|
5000
|
-
skipNextChecklistParsingRef.current = true;
|
|
5001
|
-
setTimeout(() => {
|
|
5002
|
-
const items = message.checklistBlock.items;
|
|
5003
|
-
const refUrl = resolveChecklistRefImage(items[stepIndex], messageId, session.id);
|
|
5004
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
5005
|
-
sendMessage(
|
|
5006
|
-
buildChecklistStepPrompt(stepIndex, items.length, items[stepIndex], stepIndex === 0, refUrl),
|
|
5007
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: stepIndex, title: items[stepIndex].title } }
|
|
5008
|
-
);
|
|
5009
|
-
}, 100);
|
|
5010
|
-
},
|
|
5011
|
-
[sendMessage]
|
|
5012
|
-
);
|
|
5013
|
-
const handleChecklistSkip = (0, import_react6.useCallback)(
|
|
5014
|
-
(messageId, stepIndex) => {
|
|
5015
|
-
const session = sessionsRef.current.find(
|
|
5016
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
5017
|
-
);
|
|
5018
|
-
if (!session) return;
|
|
5019
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
5020
|
-
if (!message?.checklistBlock) return;
|
|
5021
|
-
trackChecklistSkip();
|
|
5022
|
-
setSessions(
|
|
5023
|
-
(prev) => prev.map((s) => {
|
|
5024
|
-
if (s.id !== session.id) return s;
|
|
5025
|
-
return {
|
|
5026
|
-
...s,
|
|
5027
|
-
messages: s.messages.map((m) => {
|
|
5028
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5029
|
-
return {
|
|
5030
|
-
...m,
|
|
5031
|
-
checklistBlock: {
|
|
5032
|
-
...m.checklistBlock,
|
|
5033
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
5034
|
-
...it,
|
|
5035
|
-
status: idx === stepIndex ? "done" : it.status,
|
|
5036
|
-
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
5037
|
-
}))
|
|
5038
|
-
}
|
|
5039
|
-
};
|
|
5040
|
-
})
|
|
5041
|
-
};
|
|
5042
|
-
})
|
|
5043
|
-
);
|
|
5044
|
-
const nextPending = message.checklistBlock.items.findIndex(
|
|
5045
|
-
(it, idx) => idx > stepIndex && (it.status === "pending" || it.status === "error")
|
|
5046
|
-
);
|
|
5047
|
-
if (nextPending >= 0) {
|
|
5048
|
-
activeChecklistRef.current = {
|
|
5049
|
-
messageId,
|
|
5050
|
-
sessionId: session.id,
|
|
5051
|
-
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 })),
|
|
5052
|
-
currentStep: nextPending,
|
|
5053
|
-
stepResults: message.checklistBlock.items.slice(0, nextPending).map((it) => it.result || "(\uAC74\uB108\uB700)")
|
|
5054
|
-
};
|
|
5055
|
-
setSessions(
|
|
5056
|
-
(prev) => prev.map((s) => {
|
|
5057
|
-
if (s.id !== session.id) return s;
|
|
5058
|
-
return {
|
|
5059
|
-
...s,
|
|
5060
|
-
messages: s.messages.map((m) => {
|
|
5061
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5062
|
-
return {
|
|
5063
|
-
...m,
|
|
5064
|
-
checklistBlock: {
|
|
5065
|
-
...m.checklistBlock,
|
|
5066
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
5067
|
-
...it,
|
|
5068
|
-
status: idx === nextPending ? "in_progress" : it.status
|
|
5069
|
-
})),
|
|
5070
|
-
currentStep: nextPending
|
|
5071
|
-
}
|
|
5072
|
-
};
|
|
5073
|
-
})
|
|
5074
|
-
};
|
|
5075
|
-
})
|
|
5076
|
-
);
|
|
5077
|
-
skipNextChecklistParsingRef.current = true;
|
|
5078
|
-
setTimeout(() => {
|
|
5079
|
-
const items = message.checklistBlock.items;
|
|
5080
|
-
const refUrl = resolveChecklistRefImage(items[nextPending], messageId, session.id);
|
|
5081
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
5082
|
-
sendMessage(
|
|
5083
|
-
buildChecklistStepPrompt(nextPending, items.length, items[nextPending], false, refUrl),
|
|
5084
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: nextPending, title: items[nextPending].title } }
|
|
5085
|
-
);
|
|
5086
|
-
}, 100);
|
|
5087
|
-
} else {
|
|
5088
|
-
const allResults = message.checklistBlock.items.map((it, i) => {
|
|
5089
|
-
const result = i === stepIndex ? "(\uAC74\uB108\uB700)" : it.result || "(\uAC74\uB108\uB700)";
|
|
5090
|
-
return `### ${i + 1}. ${it.title}
|
|
5091
|
-
${result}`;
|
|
5092
|
-
}).join("\n\n");
|
|
5093
|
-
setSessions(
|
|
5094
|
-
(prev) => prev.map((s) => {
|
|
5095
|
-
if (s.id !== session.id) return s;
|
|
5096
|
-
return {
|
|
5097
|
-
...s,
|
|
5098
|
-
messages: s.messages.map((m) => {
|
|
5099
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
5100
|
-
return { ...m, checklistBlock: { ...m.checklistBlock, completed: true } };
|
|
5101
|
-
})
|
|
5102
|
-
};
|
|
5103
|
-
})
|
|
5104
|
-
);
|
|
5105
|
-
skipNextChecklistParsingRef.current = true;
|
|
5106
|
-
setTimeout(() => {
|
|
5107
|
-
sendMessage(
|
|
5108
|
-
`\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:
|
|
5109
|
-
|
|
5110
|
-
${allResults}
|
|
5111
|
-
|
|
5112
|
-
\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.`,
|
|
5113
|
-
{ hiddenUserMessage: true, isChecklistExecution: true }
|
|
5114
|
-
);
|
|
5115
|
-
}, 100);
|
|
5116
|
-
}
|
|
5117
|
-
},
|
|
5118
|
-
[sendMessage]
|
|
5119
|
-
);
|
|
5120
|
-
const handlePollSubmit = (0, import_react6.useCallback)(
|
|
5366
|
+
sendMessageForChecklistRef.current = sendMessage;
|
|
5367
|
+
const handlePollSubmit = (0, import_react8.useCallback)(
|
|
5121
5368
|
(messageId, responses) => {
|
|
5122
5369
|
const currentSess = sessions.find((s) => s.id === currentSessionId);
|
|
5123
5370
|
const targetMessage = currentSess?.messages.find((m) => m.id === messageId);
|
|
@@ -5187,7 +5434,7 @@ ${formattedParts.join("\n")}
|
|
|
5187
5434
|
},
|
|
5188
5435
|
[sessions, currentSessionId, selectedModel, sendMessage]
|
|
5189
5436
|
);
|
|
5190
|
-
const saveEdit = (0,
|
|
5437
|
+
const saveEdit = (0, import_react8.useCallback)(async (content) => {
|
|
5191
5438
|
if (!editingMessageId || !currentSession || !currentSessionId) return;
|
|
5192
5439
|
const messageIndex = currentSession.messages.findIndex((m) => m.id === editingMessageId);
|
|
5193
5440
|
if (messageIndex === -1) return;
|
|
@@ -5204,7 +5451,7 @@ ${formattedParts.join("\n")}
|
|
|
5204
5451
|
setEditingMessageId(null);
|
|
5205
5452
|
await sendMessage(content);
|
|
5206
5453
|
}, [editingMessageId, currentSession, currentSessionId, sendMessage]);
|
|
5207
|
-
const regenerate = (0,
|
|
5454
|
+
const regenerate = (0, import_react8.useCallback)(async (messageId) => {
|
|
5208
5455
|
console.log("[ChatUI] Regenerate called:", { messageId, currentSessionId, isLoading });
|
|
5209
5456
|
if (!currentSession || !currentSessionId || isLoading) {
|
|
5210
5457
|
console.log("[ChatUI] Regenerate early return - missing session or loading");
|
|
@@ -5243,137 +5490,76 @@ ${formattedParts.join("\n")}
|
|
|
5243
5490
|
...s.messages,
|
|
5244
5491
|
{
|
|
5245
5492
|
id: assistantMessageId,
|
|
5246
|
-
role: "assistant",
|
|
5247
|
-
content: "",
|
|
5248
|
-
model: selectedModel,
|
|
5249
|
-
timestamp: Date.now()
|
|
5250
|
-
}
|
|
5251
|
-
]
|
|
5252
|
-
};
|
|
5253
|
-
}
|
|
5254
|
-
return s;
|
|
5255
|
-
})
|
|
5256
|
-
);
|
|
5257
|
-
const messagesUpToUser = currentSession.messages.slice(0, assistantIndex);
|
|
5258
|
-
const chatMessages = messagesUpToUser.map((m) => ({
|
|
5259
|
-
role: m.role,
|
|
5260
|
-
content: m.content,
|
|
5261
|
-
...m.contentParts && { contentParts: m.contentParts }
|
|
5262
|
-
}));
|
|
5263
|
-
const baseSystemPrompt = buildSystemPrompt(currentSession);
|
|
5264
|
-
const messagesForApi = baseSystemPrompt ? [{ role: "system", content: baseSystemPrompt }, ...chatMessages] : chatMessages;
|
|
5265
|
-
const modelConfig = models.find((m) => m.id === selectedModel);
|
|
5266
|
-
const provider = modelConfig?.provider || "ollama";
|
|
5267
|
-
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
5268
|
-
const requestBody = isOllama ? { model: selectedModel, messages: messagesForApi, stream: true } : {
|
|
5269
|
-
messages: messagesForApi,
|
|
5270
|
-
model: selectedModel,
|
|
5271
|
-
provider,
|
|
5272
|
-
apiKey: provider === "devdive" ? apiKey : void 0,
|
|
5273
|
-
stream: true
|
|
5274
|
-
};
|
|
5275
|
-
console.log("[ChatUI] Regenerate fetch:", { apiEndpoint, isOllama, model: selectedModel });
|
|
5276
|
-
const response = await fetch(apiEndpoint, {
|
|
5277
|
-
method: "POST",
|
|
5278
|
-
headers: { "Content-Type": "application/json" },
|
|
5279
|
-
body: JSON.stringify(requestBody),
|
|
5280
|
-
signal: abortControllersRef.current.get(capturedSessionId).signal
|
|
5281
|
-
});
|
|
5282
|
-
console.log("[ChatUI] Regenerate response status:", response.status);
|
|
5283
|
-
if (!response.ok) throw new Error(`API error: ${response.status}`);
|
|
5284
|
-
const reader = response.body?.getReader();
|
|
5285
|
-
if (!reader) throw new Error("No reader");
|
|
5286
|
-
const decoder = new TextDecoder();
|
|
5287
|
-
let buffer = "";
|
|
5288
|
-
while (true) {
|
|
5289
|
-
const { done, value } = await reader.read();
|
|
5290
|
-
if (done) break;
|
|
5291
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5292
|
-
const lines = buffer.split("\n");
|
|
5293
|
-
buffer = lines.pop() || "";
|
|
5294
|
-
for (const line of lines) {
|
|
5295
|
-
if (!line.trim()) continue;
|
|
5296
|
-
let data = line;
|
|
5297
|
-
if (line.startsWith("data: ")) {
|
|
5298
|
-
data = line.slice(6);
|
|
5299
|
-
if (data === "[DONE]") continue;
|
|
5300
|
-
}
|
|
5301
|
-
try {
|
|
5302
|
-
const parsed = JSON.parse(data);
|
|
5303
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5304
|
-
const thinking = parsed.message?.thinking || "";
|
|
5305
|
-
if (content || thinking) {
|
|
5306
|
-
setSessions(
|
|
5307
|
-
(prev) => prev.map((s) => {
|
|
5308
|
-
if (s.id === capturedSessionId) {
|
|
5309
|
-
return {
|
|
5310
|
-
...s,
|
|
5311
|
-
messages: s.messages.map((m) => {
|
|
5312
|
-
if (m.id !== assistantMessageId) return m;
|
|
5313
|
-
let newContent = m.content;
|
|
5314
|
-
if (thinking) {
|
|
5315
|
-
if (!newContent.includes("<thinking>")) {
|
|
5316
|
-
newContent = "<thinking>" + thinking;
|
|
5317
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5318
|
-
newContent += thinking;
|
|
5319
|
-
}
|
|
5320
|
-
}
|
|
5321
|
-
if (content) {
|
|
5322
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5323
|
-
newContent += "</thinking>\n\n";
|
|
5324
|
-
}
|
|
5325
|
-
newContent += content;
|
|
5326
|
-
}
|
|
5327
|
-
return { ...m, content: newContent };
|
|
5328
|
-
})
|
|
5329
|
-
};
|
|
5330
|
-
}
|
|
5331
|
-
return s;
|
|
5332
|
-
})
|
|
5333
|
-
);
|
|
5334
|
-
}
|
|
5335
|
-
} catch {
|
|
5336
|
-
}
|
|
5337
|
-
}
|
|
5338
|
-
}
|
|
5339
|
-
if (buffer.trim()) {
|
|
5340
|
-
try {
|
|
5341
|
-
const parsed = JSON.parse(buffer);
|
|
5342
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5343
|
-
const thinking = parsed.message?.thinking || "";
|
|
5344
|
-
if (content || thinking) {
|
|
5345
|
-
setSessions(
|
|
5346
|
-
(prev) => prev.map((s) => {
|
|
5347
|
-
if (s.id === capturedSessionId) {
|
|
5348
|
-
return {
|
|
5349
|
-
...s,
|
|
5350
|
-
messages: s.messages.map((m) => {
|
|
5351
|
-
if (m.id !== assistantMessageId) return m;
|
|
5352
|
-
let newContent = m.content;
|
|
5353
|
-
if (thinking) {
|
|
5354
|
-
if (!newContent.includes("<thinking>")) {
|
|
5355
|
-
newContent = "<thinking>" + thinking;
|
|
5356
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5357
|
-
newContent += thinking;
|
|
5358
|
-
}
|
|
5359
|
-
}
|
|
5360
|
-
if (content) {
|
|
5361
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5362
|
-
newContent += "</thinking>\n\n";
|
|
5363
|
-
}
|
|
5364
|
-
newContent += content;
|
|
5365
|
-
}
|
|
5366
|
-
return { ...m, content: newContent };
|
|
5367
|
-
})
|
|
5368
|
-
};
|
|
5493
|
+
role: "assistant",
|
|
5494
|
+
content: "",
|
|
5495
|
+
model: selectedModel,
|
|
5496
|
+
timestamp: Date.now()
|
|
5369
5497
|
}
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
);
|
|
5498
|
+
]
|
|
5499
|
+
};
|
|
5373
5500
|
}
|
|
5374
|
-
|
|
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
|
+
);
|
|
5375
5561
|
}
|
|
5376
|
-
}
|
|
5562
|
+
}, { noTimeout: true });
|
|
5377
5563
|
} catch (error) {
|
|
5378
5564
|
if (error instanceof Error && error.name === "AbortError") {
|
|
5379
5565
|
return;
|
|
@@ -5384,7 +5570,7 @@ ${formattedParts.join("\n")}
|
|
|
5384
5570
|
removeLoadingSession(capturedSessionId);
|
|
5385
5571
|
}
|
|
5386
5572
|
}, [currentSession, currentSessionId, loadingSessionIds, selectedModel, models, apiEndpoint, apiKey, buildSystemPrompt]);
|
|
5387
|
-
const askOtherModel = (0,
|
|
5573
|
+
const askOtherModel = (0, import_react8.useCallback)(async (messageId, targetModel) => {
|
|
5388
5574
|
if (!currentSession || !currentSessionId || isLoading) return;
|
|
5389
5575
|
const assistantIndex = currentSession.messages.findIndex((m) => m.id === messageId);
|
|
5390
5576
|
if (assistantIndex === -1) return;
|
|
@@ -5439,30 +5625,7 @@ ${currentSession.contextSummary}` },
|
|
|
5439
5625
|
responseContent = result.content;
|
|
5440
5626
|
responseSources = result.sources;
|
|
5441
5627
|
} else {
|
|
5442
|
-
|
|
5443
|
-
const decoder = new TextDecoder();
|
|
5444
|
-
let buffer = "";
|
|
5445
|
-
while (true) {
|
|
5446
|
-
const { done, value } = await reader.read();
|
|
5447
|
-
if (done) break;
|
|
5448
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5449
|
-
const lines = buffer.split("\n");
|
|
5450
|
-
buffer = lines.pop() || "";
|
|
5451
|
-
for (const line of lines) {
|
|
5452
|
-
if (line.startsWith("data: ")) {
|
|
5453
|
-
const data = line.slice(6);
|
|
5454
|
-
if (data === "[DONE]") continue;
|
|
5455
|
-
try {
|
|
5456
|
-
const parsed = JSON.parse(data);
|
|
5457
|
-
{
|
|
5458
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5459
|
-
if (chunk) responseContent += chunk;
|
|
5460
|
-
}
|
|
5461
|
-
} catch {
|
|
5462
|
-
}
|
|
5463
|
-
}
|
|
5464
|
-
}
|
|
5465
|
-
}
|
|
5628
|
+
responseContent = await parseSSEResponse(new Response(result));
|
|
5466
5629
|
}
|
|
5467
5630
|
} else {
|
|
5468
5631
|
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
@@ -5480,32 +5643,9 @@ ${currentSession.contextSummary}` },
|
|
|
5480
5643
|
signal: abortControllersRef.current.get(currentSessionId).signal
|
|
5481
5644
|
});
|
|
5482
5645
|
emitFetchHeaders(response);
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
const decoder = new TextDecoder();
|
|
5487
|
-
let buffer = "";
|
|
5488
|
-
while (true) {
|
|
5489
|
-
const { done, value } = await reader.read();
|
|
5490
|
-
if (done) break;
|
|
5491
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5492
|
-
const lines = buffer.split("\n");
|
|
5493
|
-
buffer = lines.pop() || "";
|
|
5494
|
-
for (const line of lines) {
|
|
5495
|
-
if (line.startsWith("data: ")) {
|
|
5496
|
-
const data = line.slice(6);
|
|
5497
|
-
if (data === "[DONE]") continue;
|
|
5498
|
-
try {
|
|
5499
|
-
const parsed = JSON.parse(data);
|
|
5500
|
-
{
|
|
5501
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5502
|
-
if (chunk) responseContent += chunk;
|
|
5503
|
-
}
|
|
5504
|
-
} catch {
|
|
5505
|
-
}
|
|
5506
|
-
}
|
|
5507
|
-
}
|
|
5508
|
-
}
|
|
5646
|
+
await streamResponse(response, (chunk) => {
|
|
5647
|
+
if (chunk.content) responseContent += chunk.content;
|
|
5648
|
+
}, { noTimeout: true });
|
|
5509
5649
|
}
|
|
5510
5650
|
const alternative = {
|
|
5511
5651
|
id: generateId5("alt"),
|
|
@@ -5565,13 +5705,13 @@ ${currentSession.contextSummary}` },
|
|
|
5565
5705
|
onSendMessage,
|
|
5566
5706
|
onError
|
|
5567
5707
|
]);
|
|
5568
|
-
const setActiveAlternative = (0,
|
|
5708
|
+
const setActiveAlternative = (0, import_react8.useCallback)((assistantMessageId, index) => {
|
|
5569
5709
|
setActiveAlternatives((prev) => ({
|
|
5570
5710
|
...prev,
|
|
5571
5711
|
[assistantMessageId]: index
|
|
5572
5712
|
}));
|
|
5573
5713
|
}, []);
|
|
5574
|
-
const getActiveAlternative = (0,
|
|
5714
|
+
const getActiveAlternative = (0, import_react8.useCallback)((assistantMessageId) => {
|
|
5575
5715
|
return activeAlternatives[assistantMessageId] ?? 0;
|
|
5576
5716
|
}, [activeAlternatives]);
|
|
5577
5717
|
return {
|
|
@@ -5629,6 +5769,10 @@ ${currentSession.contextSummary}` },
|
|
|
5629
5769
|
}));
|
|
5630
5770
|
await infoExtraction.extractInfo(recentMessages);
|
|
5631
5771
|
},
|
|
5772
|
+
getTokenStats: (0, import_react8.useCallback)((sessionId) => {
|
|
5773
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
5774
|
+
return session?.tokenStats ?? null;
|
|
5775
|
+
}, [sessions]),
|
|
5632
5776
|
// External Storage Loading States
|
|
5633
5777
|
/**
|
|
5634
5778
|
* @description 세션 목록 로딩 상태
|
|
@@ -5784,12 +5928,12 @@ ${result.content}
|
|
|
5784
5928
|
};
|
|
5785
5929
|
|
|
5786
5930
|
// src/react/contexts/ImageErrorContext.ts
|
|
5787
|
-
var
|
|
5788
|
-
var ImageErrorContext = (0,
|
|
5789
|
-
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);
|
|
5790
5934
|
|
|
5791
5935
|
// src/react/components/ChatSidebar.tsx
|
|
5792
|
-
var
|
|
5936
|
+
var import_react13 = require("react");
|
|
5793
5937
|
|
|
5794
5938
|
// src/react/components/Icon.tsx
|
|
5795
5939
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -5889,10 +6033,10 @@ var IconSvg = ({
|
|
|
5889
6033
|
};
|
|
5890
6034
|
|
|
5891
6035
|
// src/react/components/MarkdownRenderer.tsx
|
|
5892
|
-
var
|
|
6036
|
+
var import_react11 = __toESM(require("react"));
|
|
5893
6037
|
|
|
5894
6038
|
// src/react/components/LinkChip.tsx
|
|
5895
|
-
var
|
|
6039
|
+
var import_react10 = require("react");
|
|
5896
6040
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
5897
6041
|
var getDomain = (url) => {
|
|
5898
6042
|
try {
|
|
@@ -5946,7 +6090,7 @@ var LinkChip = ({
|
|
|
5946
6090
|
index,
|
|
5947
6091
|
style
|
|
5948
6092
|
}) => {
|
|
5949
|
-
const [isHovered, setIsHovered] = (0,
|
|
6093
|
+
const [isHovered, setIsHovered] = (0, import_react10.useState)(false);
|
|
5950
6094
|
const domain = getDomain(url);
|
|
5951
6095
|
const shortName = getShortName(domain);
|
|
5952
6096
|
const domainColor = getDomainColor(domain);
|
|
@@ -6316,8 +6460,8 @@ var parseTableRow = (row) => {
|
|
|
6316
6460
|
return row.split("|").slice(1, -1).map((cell) => cell.trim());
|
|
6317
6461
|
};
|
|
6318
6462
|
var MarkdownTable = ({ data }) => {
|
|
6319
|
-
const [copied, setCopied] =
|
|
6320
|
-
const [isHovered, setIsHovered] =
|
|
6463
|
+
const [copied, setCopied] = import_react11.default.useState(false);
|
|
6464
|
+
const [isHovered, setIsHovered] = import_react11.default.useState(false);
|
|
6321
6465
|
const handleCopy = async () => {
|
|
6322
6466
|
const headerLine = data.headers.join(" ");
|
|
6323
6467
|
const bodyLines = data.rows.map((row) => row.join(" "));
|
|
@@ -6429,7 +6573,7 @@ var ThinkingSpinner = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
|
6429
6573
|
}
|
|
6430
6574
|
);
|
|
6431
6575
|
var ThinkingBlock = ({ content, defaultOpen = false }) => {
|
|
6432
|
-
const [isOpen, setIsOpen] =
|
|
6576
|
+
const [isOpen, setIsOpen] = import_react11.default.useState(defaultOpen);
|
|
6433
6577
|
const isStreaming = content.trim().endsWith("...");
|
|
6434
6578
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
6435
6579
|
"details",
|
|
@@ -6504,7 +6648,7 @@ var ThinkingBlock = ({ content, defaultOpen = false }) => {
|
|
|
6504
6648
|
);
|
|
6505
6649
|
};
|
|
6506
6650
|
var CodeBlock = ({ language, code }) => {
|
|
6507
|
-
const [copied, setCopied] =
|
|
6651
|
+
const [copied, setCopied] = import_react11.default.useState(false);
|
|
6508
6652
|
const handleCopy = async () => {
|
|
6509
6653
|
try {
|
|
6510
6654
|
await navigator.clipboard.writeText(code);
|
|
@@ -6587,10 +6731,10 @@ var CodeBlock = ({ language, code }) => {
|
|
|
6587
6731
|
);
|
|
6588
6732
|
};
|
|
6589
6733
|
var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
6590
|
-
const [isHovered, setIsHovered] =
|
|
6591
|
-
const [imgSrc, setImgSrc] =
|
|
6734
|
+
const [isHovered, setIsHovered] = import_react11.default.useState(false);
|
|
6735
|
+
const [imgSrc, setImgSrc] = import_react11.default.useState(src);
|
|
6592
6736
|
const onImageError = useImageError();
|
|
6593
|
-
const handleImageError =
|
|
6737
|
+
const handleImageError = import_react11.default.useCallback(async () => {
|
|
6594
6738
|
if (onImageError) {
|
|
6595
6739
|
const newUrl = await onImageError(imgSrc);
|
|
6596
6740
|
if (newUrl) {
|
|
@@ -6599,8 +6743,8 @@ var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
|
6599
6743
|
}
|
|
6600
6744
|
}
|
|
6601
6745
|
}, [onImageError, imgSrc]);
|
|
6602
|
-
const [copyState, setCopyState] =
|
|
6603
|
-
const imgRef =
|
|
6746
|
+
const [copyState, setCopyState] = import_react11.default.useState("idle");
|
|
6747
|
+
const imgRef = import_react11.default.useRef(null);
|
|
6604
6748
|
const getImageBlob = async () => {
|
|
6605
6749
|
const img = imgRef.current;
|
|
6606
6750
|
if (img && img.complete && img.naturalWidth > 0) {
|
|
@@ -6816,7 +6960,7 @@ var ImageWithCopyButton = ({ src, alt, imageKey }) => {
|
|
|
6816
6960
|
);
|
|
6817
6961
|
};
|
|
6818
6962
|
var ChoiceButtons = ({ choices, title, onChoiceClick }) => {
|
|
6819
|
-
const [hoveredIndex, setHoveredIndex] =
|
|
6963
|
+
const [hoveredIndex, setHoveredIndex] = import_react11.default.useState(null);
|
|
6820
6964
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
6821
6965
|
"div",
|
|
6822
6966
|
{
|
|
@@ -6946,11 +7090,11 @@ var MarkdownRenderer = ({
|
|
|
6946
7090
|
sources,
|
|
6947
7091
|
inline = false
|
|
6948
7092
|
}) => {
|
|
6949
|
-
const inlineRendered = (0,
|
|
7093
|
+
const inlineRendered = (0, import_react11.useMemo)(() => {
|
|
6950
7094
|
if (!inline) return null;
|
|
6951
7095
|
return parseInlineElements(content, "inline-md", sources);
|
|
6952
7096
|
}, [inline, content, sources]);
|
|
6953
|
-
const rendered = (0,
|
|
7097
|
+
const rendered = (0, import_react11.useMemo)(() => {
|
|
6954
7098
|
if (inline) return null;
|
|
6955
7099
|
const elements = [];
|
|
6956
7100
|
let processedContent = content;
|
|
@@ -7103,7 +7247,7 @@ var MarkdownRenderer = ({
|
|
|
7103
7247
|
borderRadius: "0 8px 8px 0",
|
|
7104
7248
|
color: "var(--chatllm-text, #374151)"
|
|
7105
7249
|
},
|
|
7106
|
-
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: [
|
|
7107
7251
|
parseInlineElements(line, `bq-line-${i}`, sources),
|
|
7108
7252
|
i < blockquoteLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {})
|
|
7109
7253
|
] }, i))
|
|
@@ -7347,7 +7491,7 @@ var MarkdownRenderer = ({
|
|
|
7347
7491
|
};
|
|
7348
7492
|
|
|
7349
7493
|
// src/react/components/ProjectSelector.tsx
|
|
7350
|
-
var
|
|
7494
|
+
var import_react12 = require("react");
|
|
7351
7495
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
7352
7496
|
var ProjectSelector = ({
|
|
7353
7497
|
projects,
|
|
@@ -7356,10 +7500,10 @@ var ProjectSelector = ({
|
|
|
7356
7500
|
onNewProject,
|
|
7357
7501
|
onProjectSettings
|
|
7358
7502
|
}) => {
|
|
7359
|
-
const [isOpen, setIsOpen] = (0,
|
|
7360
|
-
const dropdownRef = (0,
|
|
7503
|
+
const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
|
|
7504
|
+
const dropdownRef = (0, import_react12.useRef)(null);
|
|
7361
7505
|
const currentProject = projects.find((p) => p.id === currentProjectId);
|
|
7362
|
-
(0,
|
|
7506
|
+
(0, import_react12.useEffect)(() => {
|
|
7363
7507
|
const handleClickOutside = (e) => {
|
|
7364
7508
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
7365
7509
|
setIsOpen(false);
|
|
@@ -7593,10 +7737,10 @@ var ChatSidebar = ({
|
|
|
7593
7737
|
isLoading = false
|
|
7594
7738
|
}) => {
|
|
7595
7739
|
const sidebarWidth = typeof widthProp === "number" ? `${widthProp}px` : widthProp || "288px";
|
|
7596
|
-
const [editingId, setEditingId] = (0,
|
|
7597
|
-
const [editingTitle, setEditingTitle] = (0,
|
|
7598
|
-
const inputRef = (0,
|
|
7599
|
-
(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)(() => {
|
|
7600
7744
|
if (editingId && inputRef.current) {
|
|
7601
7745
|
inputRef.current.focus();
|
|
7602
7746
|
inputRef.current.select();
|
|
@@ -7968,7 +8112,7 @@ var ChatSidebar = ({
|
|
|
7968
8112
|
};
|
|
7969
8113
|
|
|
7970
8114
|
// src/react/components/ChatHeader.tsx
|
|
7971
|
-
var
|
|
8115
|
+
var import_react14 = require("react");
|
|
7972
8116
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
7973
8117
|
var ChatHeader = ({
|
|
7974
8118
|
title,
|
|
@@ -7982,7 +8126,7 @@ var ChatHeader = ({
|
|
|
7982
8126
|
showSettings = true,
|
|
7983
8127
|
renderHeaderExtra
|
|
7984
8128
|
}) => {
|
|
7985
|
-
const [modelDropdownOpen, setModelDropdownOpen] = (0,
|
|
8129
|
+
const [modelDropdownOpen, setModelDropdownOpen] = (0, import_react14.useState)(false);
|
|
7986
8130
|
const currentModel = models.find((m) => m.id === model);
|
|
7987
8131
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
7988
8132
|
"header",
|
|
@@ -8237,7 +8381,7 @@ var ChatHeader = ({
|
|
|
8237
8381
|
};
|
|
8238
8382
|
|
|
8239
8383
|
// src/react/components/ChatInput.tsx
|
|
8240
|
-
var
|
|
8384
|
+
var import_react15 = __toESM(require("react"));
|
|
8241
8385
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
8242
8386
|
var ChatInput = ({
|
|
8243
8387
|
value,
|
|
@@ -8265,20 +8409,20 @@ var ChatInput = ({
|
|
|
8265
8409
|
onDisclaimerClick,
|
|
8266
8410
|
inline = false
|
|
8267
8411
|
}) => {
|
|
8268
|
-
const [mainMenuOpen, setMainMenuOpen] =
|
|
8269
|
-
const textareaRef = (0,
|
|
8270
|
-
const fileInputRef = (0,
|
|
8271
|
-
const [actionMenuOpen, setActionMenuOpen] = (0,
|
|
8272
|
-
const [isDragOver, setIsDragOver] = (0,
|
|
8273
|
-
const mainMenuRef = (0,
|
|
8274
|
-
const actionMenuRef = (0,
|
|
8275
|
-
(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)(() => {
|
|
8276
8420
|
if (textareaRef.current) {
|
|
8277
8421
|
textareaRef.current.style.height = "auto";
|
|
8278
8422
|
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
|
|
8279
8423
|
}
|
|
8280
8424
|
}, [value]);
|
|
8281
|
-
(0,
|
|
8425
|
+
(0, import_react15.useEffect)(() => {
|
|
8282
8426
|
const handleClickOutside = (event) => {
|
|
8283
8427
|
if (mainMenuRef.current && !mainMenuRef.current.contains(event.target)) {
|
|
8284
8428
|
setMainMenuOpen(false);
|
|
@@ -9000,13 +9144,13 @@ var iconButtonStyle = {
|
|
|
9000
9144
|
};
|
|
9001
9145
|
|
|
9002
9146
|
// src/react/components/MessageList.tsx
|
|
9003
|
-
var
|
|
9147
|
+
var import_react23 = __toESM(require("react"));
|
|
9004
9148
|
|
|
9005
9149
|
// src/react/components/MessageBubble.tsx
|
|
9006
|
-
var
|
|
9150
|
+
var import_react22 = require("react");
|
|
9007
9151
|
|
|
9008
9152
|
// src/react/components/DeepResearchProgressUI.tsx
|
|
9009
|
-
var
|
|
9153
|
+
var import_react16 = __toESM(require("react"));
|
|
9010
9154
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
9011
9155
|
var StatusIcon = ({ status }) => {
|
|
9012
9156
|
switch (status) {
|
|
@@ -9070,7 +9214,7 @@ var PhaseProgress = ({ phase }) => {
|
|
|
9070
9214
|
gap: "8px",
|
|
9071
9215
|
marginBottom: "16px"
|
|
9072
9216
|
},
|
|
9073
|
-
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: [
|
|
9074
9218
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
9075
9219
|
"div",
|
|
9076
9220
|
{
|
|
@@ -9289,7 +9433,7 @@ var DeepResearchProgressUI = ({ progress }) => {
|
|
|
9289
9433
|
};
|
|
9290
9434
|
|
|
9291
9435
|
// src/react/components/PollCard.tsx
|
|
9292
|
-
var
|
|
9436
|
+
var import_react17 = require("react");
|
|
9293
9437
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
9294
9438
|
var renderInlineMarkdown = (text) => {
|
|
9295
9439
|
const parts = [];
|
|
@@ -9328,12 +9472,12 @@ var PollCard = ({
|
|
|
9328
9472
|
onSubmit,
|
|
9329
9473
|
onSkip
|
|
9330
9474
|
}) => {
|
|
9331
|
-
const [activeTab, setActiveTab] = (0,
|
|
9332
|
-
const [selections, setSelections] = (0,
|
|
9333
|
-
const [otherTexts, setOtherTexts] = (0,
|
|
9334
|
-
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)({});
|
|
9335
9479
|
const currentQuestion = questions[activeTab];
|
|
9336
|
-
const handleOptionToggle = (0,
|
|
9480
|
+
const handleOptionToggle = (0, import_react17.useCallback)(
|
|
9337
9481
|
(questionId, optionId, multiSelect) => {
|
|
9338
9482
|
setSelections((prev) => {
|
|
9339
9483
|
const current = prev[questionId] || /* @__PURE__ */ new Set();
|
|
@@ -9355,7 +9499,7 @@ var PollCard = ({
|
|
|
9355
9499
|
},
|
|
9356
9500
|
[]
|
|
9357
9501
|
);
|
|
9358
|
-
const handleOtherToggle = (0,
|
|
9502
|
+
const handleOtherToggle = (0, import_react17.useCallback)((questionId, multiSelect) => {
|
|
9359
9503
|
if (multiSelect) {
|
|
9360
9504
|
setOtherSelected((prev) => ({ ...prev, [questionId]: !prev[questionId] }));
|
|
9361
9505
|
} else {
|
|
@@ -9363,7 +9507,7 @@ var PollCard = ({
|
|
|
9363
9507
|
setOtherSelected((prev) => ({ ...prev, [questionId]: true }));
|
|
9364
9508
|
}
|
|
9365
9509
|
}, []);
|
|
9366
|
-
const handleSubmit = (0,
|
|
9510
|
+
const handleSubmit = (0, import_react17.useCallback)(() => {
|
|
9367
9511
|
const responses = questions.map((q) => {
|
|
9368
9512
|
const selected = selections[q.id] || /* @__PURE__ */ new Set();
|
|
9369
9513
|
const other = otherSelected[q.id] && otherTexts[q.id]?.trim();
|
|
@@ -9376,7 +9520,7 @@ var PollCard = ({
|
|
|
9376
9520
|
});
|
|
9377
9521
|
onSubmit(responses);
|
|
9378
9522
|
}, [questions, selections, otherSelected, otherTexts, onSubmit]);
|
|
9379
|
-
const handleSkip = (0,
|
|
9523
|
+
const handleSkip = (0, import_react17.useCallback)(() => {
|
|
9380
9524
|
const responses = questions.map((q) => ({
|
|
9381
9525
|
questionId: q.id,
|
|
9382
9526
|
selectedOptions: [],
|
|
@@ -9385,7 +9529,7 @@ var PollCard = ({
|
|
|
9385
9529
|
onSubmit(responses);
|
|
9386
9530
|
onSkip?.();
|
|
9387
9531
|
}, [questions, onSubmit, onSkip]);
|
|
9388
|
-
(0,
|
|
9532
|
+
(0, import_react17.useEffect)(() => {
|
|
9389
9533
|
if (typeof window === "undefined") return;
|
|
9390
9534
|
const handleKeyDown = (e) => {
|
|
9391
9535
|
if (e.key !== "Escape") return;
|
|
@@ -9404,9 +9548,9 @@ var PollCard = ({
|
|
|
9404
9548
|
const currentHasSelection = getSelectionCount(currentQuestion.id) > 0;
|
|
9405
9549
|
const isLastTab = activeTab === questions.length - 1;
|
|
9406
9550
|
const totalSelected = questions.reduce((sum, q) => sum + getSelectionCount(q.id), 0);
|
|
9407
|
-
const [visibleCount, setVisibleCount] = (0,
|
|
9408
|
-
const [cardVisible, setCardVisible] = (0,
|
|
9409
|
-
(0,
|
|
9551
|
+
const [visibleCount, setVisibleCount] = (0, import_react17.useState)(0);
|
|
9552
|
+
const [cardVisible, setCardVisible] = (0, import_react17.useState)(false);
|
|
9553
|
+
(0, import_react17.useEffect)(() => {
|
|
9410
9554
|
const fadeTimer = setTimeout(() => setCardVisible(true), 50);
|
|
9411
9555
|
const totalItems = currentQuestion.options.length + 1;
|
|
9412
9556
|
const timers = [fadeTimer];
|
|
@@ -9415,7 +9559,7 @@ var PollCard = ({
|
|
|
9415
9559
|
}
|
|
9416
9560
|
return () => timers.forEach(clearTimeout);
|
|
9417
9561
|
}, [currentQuestion.options.length]);
|
|
9418
|
-
(0,
|
|
9562
|
+
(0, import_react17.useEffect)(() => {
|
|
9419
9563
|
setVisibleCount(0);
|
|
9420
9564
|
const totalItems = currentQuestion.options.length + 1;
|
|
9421
9565
|
const timers = [];
|
|
@@ -9424,7 +9568,7 @@ var PollCard = ({
|
|
|
9424
9568
|
}
|
|
9425
9569
|
return () => timers.forEach(clearTimeout);
|
|
9426
9570
|
}, [activeTab, currentQuestion.options.length]);
|
|
9427
|
-
const handleNext = (0,
|
|
9571
|
+
const handleNext = (0, import_react17.useCallback)(() => {
|
|
9428
9572
|
if (!isLastTab) {
|
|
9429
9573
|
setActiveTab((prev) => prev + 1);
|
|
9430
9574
|
} else {
|
|
@@ -9842,15 +9986,15 @@ var SkillProgressUI = ({
|
|
|
9842
9986
|
};
|
|
9843
9987
|
|
|
9844
9988
|
// src/react/components/ImageContentCard.tsx
|
|
9845
|
-
var
|
|
9989
|
+
var import_react18 = require("react");
|
|
9846
9990
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
9847
9991
|
var ImageContentCard = ({ part }) => {
|
|
9848
|
-
const [isExpanded, setIsExpanded] = (0,
|
|
9849
|
-
const [isLoaded, setIsLoaded] = (0,
|
|
9850
|
-
const [hasError, setHasError] = (0,
|
|
9851
|
-
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);
|
|
9852
9996
|
const onImageError = useImageError();
|
|
9853
|
-
const handleImageError = (0,
|
|
9997
|
+
const handleImageError = (0, import_react18.useCallback)(async () => {
|
|
9854
9998
|
if (onImageError) {
|
|
9855
9999
|
const newUrl = await onImageError(imgSrc, part.fileName);
|
|
9856
10000
|
if (newUrl) {
|
|
@@ -10063,7 +10207,7 @@ var FileContentCard = ({ part }) => {
|
|
|
10063
10207
|
};
|
|
10064
10208
|
|
|
10065
10209
|
// src/react/components/ToolStatusCard.tsx
|
|
10066
|
-
var
|
|
10210
|
+
var import_react19 = require("react");
|
|
10067
10211
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
10068
10212
|
var mapIcon2 = (icon) => {
|
|
10069
10213
|
const iconMap = {
|
|
@@ -10096,7 +10240,7 @@ var ToolStatusCard = ({
|
|
|
10096
10240
|
sources,
|
|
10097
10241
|
errorMessage
|
|
10098
10242
|
}) => {
|
|
10099
|
-
const [isExpanded, setIsExpanded] = (0,
|
|
10243
|
+
const [isExpanded, setIsExpanded] = (0, import_react19.useState)(false);
|
|
10100
10244
|
const displayLabel = label || getDefaultLabel(toolName);
|
|
10101
10245
|
const statusText = getStatusSuffix(displayLabel, status);
|
|
10102
10246
|
const hasSources = sources && sources.length > 0;
|
|
@@ -10227,7 +10371,7 @@ var ToolStatusCard = ({
|
|
|
10227
10371
|
};
|
|
10228
10372
|
|
|
10229
10373
|
// src/react/components/ArtifactCard.tsx
|
|
10230
|
-
var
|
|
10374
|
+
var import_react20 = require("react");
|
|
10231
10375
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
10232
10376
|
var getThemeStyles = () => {
|
|
10233
10377
|
const root = document.documentElement;
|
|
@@ -10268,10 +10412,10 @@ var getAutoResizeScript = (id) => `
|
|
|
10268
10412
|
});
|
|
10269
10413
|
</script>`;
|
|
10270
10414
|
var HtmlArtifact = ({ code }) => {
|
|
10271
|
-
const iframeRef = (0,
|
|
10272
|
-
const [height, setHeight] = (0,
|
|
10273
|
-
const artifactId = (0,
|
|
10274
|
-
(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)(() => {
|
|
10275
10419
|
const id = artifactId.current;
|
|
10276
10420
|
const handleMessage = (e) => {
|
|
10277
10421
|
if (e.data?.type === "artifact-resize" && e.data.id === id && typeof e.data.height === "number") {
|
|
@@ -10317,10 +10461,10 @@ var SvgArtifact = ({ code }) => {
|
|
|
10317
10461
|
);
|
|
10318
10462
|
};
|
|
10319
10463
|
var MermaidArtifact = ({ code }) => {
|
|
10320
|
-
const containerRef = (0,
|
|
10321
|
-
const [svgHtml, setSvgHtml] = (0,
|
|
10322
|
-
const [error, setError] = (0,
|
|
10323
|
-
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 () => {
|
|
10324
10468
|
try {
|
|
10325
10469
|
let mermaid;
|
|
10326
10470
|
try {
|
|
@@ -10336,7 +10480,7 @@ var MermaidArtifact = ({ code }) => {
|
|
|
10336
10480
|
setError(e instanceof Error ? e.message : "Mermaid \uB80C\uB354\uB9C1 \uC2E4\uD328");
|
|
10337
10481
|
}
|
|
10338
10482
|
}, [code]);
|
|
10339
|
-
(0,
|
|
10483
|
+
(0, import_react20.useEffect)(() => {
|
|
10340
10484
|
renderMermaid();
|
|
10341
10485
|
}, [renderMermaid]);
|
|
10342
10486
|
if (error) {
|
|
@@ -10412,9 +10556,9 @@ var svgToPngBlob = (svgString, scale = 2) => {
|
|
|
10412
10556
|
});
|
|
10413
10557
|
};
|
|
10414
10558
|
var ArtifactActions = ({ code, language, containerRef }) => {
|
|
10415
|
-
const [showCode, setShowCode] = (0,
|
|
10416
|
-
const [copied, setCopied] = (0,
|
|
10417
|
-
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);
|
|
10418
10562
|
const handleCopy = async () => {
|
|
10419
10563
|
try {
|
|
10420
10564
|
await navigator.clipboard.writeText(code);
|
|
@@ -10579,7 +10723,7 @@ var ArtifactActions = ({ code, language, containerRef }) => {
|
|
|
10579
10723
|
] });
|
|
10580
10724
|
};
|
|
10581
10725
|
var ArtifactCard = ({ part, index = 0 }) => {
|
|
10582
|
-
const contentRef = (0,
|
|
10726
|
+
const contentRef = (0, import_react20.useRef)(null);
|
|
10583
10727
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
10584
10728
|
"div",
|
|
10585
10729
|
{
|
|
@@ -10751,7 +10895,7 @@ var ContentPartRenderer = ({
|
|
|
10751
10895
|
};
|
|
10752
10896
|
|
|
10753
10897
|
// src/react/components/ChecklistCard.tsx
|
|
10754
|
-
var
|
|
10898
|
+
var import_react21 = require("react");
|
|
10755
10899
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
10756
10900
|
var ChecklistCard = ({
|
|
10757
10901
|
items,
|
|
@@ -10761,8 +10905,8 @@ var ChecklistCard = ({
|
|
|
10761
10905
|
onSkipStep,
|
|
10762
10906
|
onStart
|
|
10763
10907
|
}) => {
|
|
10764
|
-
const [expandedItems, setExpandedItems] = (0,
|
|
10765
|
-
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)(() => {
|
|
10766
10910
|
const done = items.filter((it) => it.status === "done").length;
|
|
10767
10911
|
const running = items.some((it) => it.status === "in_progress");
|
|
10768
10912
|
const waiting = items.every((it) => it.status === "pending");
|
|
@@ -11259,9 +11403,9 @@ var MessageBubble = ({
|
|
|
11259
11403
|
onToggleChecklistPanel,
|
|
11260
11404
|
onChecklistStart
|
|
11261
11405
|
}) => {
|
|
11262
|
-
const [showActions, setShowActions] = (0,
|
|
11263
|
-
const [showModelMenu, setShowModelMenu] = (0,
|
|
11264
|
-
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);
|
|
11265
11409
|
const isUser = message.role === "user";
|
|
11266
11410
|
const isAssistant = message.role === "assistant";
|
|
11267
11411
|
const relevantAlternatives = isUser ? alternatives : message.alternatives;
|
|
@@ -12012,14 +12156,14 @@ var MessageList = ({
|
|
|
12012
12156
|
onExport,
|
|
12013
12157
|
onToggleChecklistPanel
|
|
12014
12158
|
}) => {
|
|
12015
|
-
const messagesEndRef = (0,
|
|
12016
|
-
const containerRef = (0,
|
|
12017
|
-
const [selectedText, setSelectedText] = (0,
|
|
12018
|
-
const [selectionPosition, setSelectionPosition] = (0,
|
|
12019
|
-
const [showScrollButton, setShowScrollButton] = (0,
|
|
12020
|
-
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);
|
|
12021
12165
|
const SCROLL_THRESHOLD = 100;
|
|
12022
|
-
(0,
|
|
12166
|
+
(0, import_react23.useEffect)(() => {
|
|
12023
12167
|
const container = containerRef.current;
|
|
12024
12168
|
if (!container) return;
|
|
12025
12169
|
const handleWheel = (e) => {
|
|
@@ -12056,7 +12200,7 @@ var MessageList = ({
|
|
|
12056
12200
|
container.removeEventListener("touchmove", handleTouchMove);
|
|
12057
12201
|
};
|
|
12058
12202
|
}, []);
|
|
12059
|
-
const handleScroll = (0,
|
|
12203
|
+
const handleScroll = (0, import_react23.useCallback)(() => {
|
|
12060
12204
|
if (!containerRef.current) return;
|
|
12061
12205
|
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
|
12062
12206
|
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
@@ -12065,14 +12209,14 @@ var MessageList = ({
|
|
|
12065
12209
|
setShowScrollButton(false);
|
|
12066
12210
|
}
|
|
12067
12211
|
}, []);
|
|
12068
|
-
(0,
|
|
12212
|
+
(0, import_react23.useEffect)(() => {
|
|
12069
12213
|
if (userScrollLockRef.current) return;
|
|
12070
12214
|
containerRef.current?.scrollTo({
|
|
12071
12215
|
top: containerRef.current.scrollHeight,
|
|
12072
12216
|
behavior: "smooth"
|
|
12073
12217
|
});
|
|
12074
12218
|
}, [messages]);
|
|
12075
|
-
const scrollToBottom = (0,
|
|
12219
|
+
const scrollToBottom = (0, import_react23.useCallback)(() => {
|
|
12076
12220
|
userScrollLockRef.current = false;
|
|
12077
12221
|
setShowScrollButton(false);
|
|
12078
12222
|
containerRef.current?.scrollTo({
|
|
@@ -12080,7 +12224,7 @@ var MessageList = ({
|
|
|
12080
12224
|
behavior: "smooth"
|
|
12081
12225
|
});
|
|
12082
12226
|
}, []);
|
|
12083
|
-
const handleMouseUp = (0,
|
|
12227
|
+
const handleMouseUp = (0, import_react23.useCallback)(() => {
|
|
12084
12228
|
const selection = typeof window !== "undefined" ? window.getSelection() : null;
|
|
12085
12229
|
const text = selection?.toString().trim();
|
|
12086
12230
|
if (text && text.length > 0) {
|
|
@@ -12181,7 +12325,7 @@ var MessageList = ({
|
|
|
12181
12325
|
},
|
|
12182
12326
|
message.id
|
|
12183
12327
|
);
|
|
12184
|
-
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;
|
|
12185
12329
|
}),
|
|
12186
12330
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: messagesEndRef })
|
|
12187
12331
|
]
|
|
@@ -12275,7 +12419,7 @@ var MessageList = ({
|
|
|
12275
12419
|
};
|
|
12276
12420
|
|
|
12277
12421
|
// src/react/components/SettingsModal.tsx
|
|
12278
|
-
var
|
|
12422
|
+
var import_react24 = require("react");
|
|
12279
12423
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
12280
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.
|
|
12281
12425
|
|
|
@@ -12340,9 +12484,9 @@ var SettingsModal = ({
|
|
|
12340
12484
|
onImportMemory,
|
|
12341
12485
|
importMemoryPrompt
|
|
12342
12486
|
}) => {
|
|
12343
|
-
const [activeTab, setActiveTab] = (0,
|
|
12344
|
-
const [localApiKey, setLocalApiKey] = (0,
|
|
12345
|
-
(0,
|
|
12487
|
+
const [activeTab, setActiveTab] = (0, import_react24.useState)("general");
|
|
12488
|
+
const [localApiKey, setLocalApiKey] = (0, import_react24.useState)(apiKey);
|
|
12489
|
+
(0, import_react24.useEffect)(() => {
|
|
12346
12490
|
setLocalApiKey(apiKey);
|
|
12347
12491
|
}, [apiKey]);
|
|
12348
12492
|
if (!isOpen) return null;
|
|
@@ -12818,11 +12962,11 @@ var memoryCategoryColors = {
|
|
|
12818
12962
|
preference: "#8b5cf6"
|
|
12819
12963
|
};
|
|
12820
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 }) => {
|
|
12821
|
-
const [activeFilter, setActiveFilter] = (0,
|
|
12822
|
-
const [expandedId, setExpandedId] = (0,
|
|
12823
|
-
const [importModalOpen, setImportModalOpen] = (0,
|
|
12824
|
-
const [importText, setImportText] = (0,
|
|
12825
|
-
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);
|
|
12826
12970
|
const filteredItems = activeFilter === "all" ? items : items.filter((item) => item.category === activeFilter);
|
|
12827
12971
|
const formatDate = (timestamp) => {
|
|
12828
12972
|
const date = new Date(timestamp);
|
|
@@ -13279,7 +13423,7 @@ var MemoryTabContent = ({ items, contextSummary, onDelete, onClearAll, title = "
|
|
|
13279
13423
|
};
|
|
13280
13424
|
|
|
13281
13425
|
// src/react/components/ProjectSettingsModal.tsx
|
|
13282
|
-
var
|
|
13426
|
+
var import_react25 = require("react");
|
|
13283
13427
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
13284
13428
|
var COLOR_PRESETS = [
|
|
13285
13429
|
"#3584FA",
|
|
@@ -13300,12 +13444,12 @@ var ProjectSettingsModal = ({
|
|
|
13300
13444
|
onDeleteFile,
|
|
13301
13445
|
onDeleteProject
|
|
13302
13446
|
}) => {
|
|
13303
|
-
const [activeTab, setActiveTab] = (0,
|
|
13304
|
-
const [title, setTitle] = (0,
|
|
13305
|
-
const [description, setDescription] = (0,
|
|
13306
|
-
const [instructions, setInstructions] = (0,
|
|
13307
|
-
const [color, setColor] = (0,
|
|
13308
|
-
(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)(() => {
|
|
13309
13453
|
if (project) {
|
|
13310
13454
|
setTitle(project.title);
|
|
13311
13455
|
setDescription(project.description || "");
|
|
@@ -14770,28 +14914,28 @@ var ChatUIView = ({
|
|
|
14770
14914
|
handleChecklistSkip,
|
|
14771
14915
|
activeChecklistMessage
|
|
14772
14916
|
} = state;
|
|
14773
|
-
const [disclaimerOpen, setDisclaimerOpen] =
|
|
14774
|
-
const [isMobile, setIsMobile] =
|
|
14775
|
-
|
|
14917
|
+
const [disclaimerOpen, setDisclaimerOpen] = import_react26.default.useState(false);
|
|
14918
|
+
const [isMobile, setIsMobile] = import_react26.default.useState(false);
|
|
14919
|
+
import_react26.default.useEffect(() => {
|
|
14776
14920
|
if (typeof window === "undefined") return;
|
|
14777
14921
|
const check = () => setIsMobile(window.innerWidth < 768);
|
|
14778
14922
|
check();
|
|
14779
14923
|
window.addEventListener("resize", check);
|
|
14780
14924
|
return () => window.removeEventListener("resize", check);
|
|
14781
14925
|
}, []);
|
|
14782
|
-
const [checklistPanelDismissed, setChecklistPanelDismissed] =
|
|
14926
|
+
const [checklistPanelDismissed, setChecklistPanelDismissed] = import_react26.default.useState(false);
|
|
14783
14927
|
const isChecklistPanelOpen = !!activeChecklistMessage && !checklistPanelDismissed;
|
|
14784
|
-
const prevChecklistIdRef =
|
|
14785
|
-
|
|
14928
|
+
const prevChecklistIdRef = import_react26.default.useRef(void 0);
|
|
14929
|
+
import_react26.default.useEffect(() => {
|
|
14786
14930
|
const currentId = activeChecklistMessage?.checklistBlock?.id;
|
|
14787
14931
|
if (currentId && currentId !== prevChecklistIdRef.current && !activeChecklistMessage?.checklistBlock?.completed) {
|
|
14788
14932
|
setChecklistPanelDismissed(false);
|
|
14789
14933
|
}
|
|
14790
14934
|
prevChecklistIdRef.current = currentId;
|
|
14791
14935
|
}, [activeChecklistMessage?.checklistBlock?.id, activeChecklistMessage?.checklistBlock?.completed]);
|
|
14792
|
-
const [welcomeExiting, setWelcomeExiting] =
|
|
14793
|
-
const prevMessageCountRef =
|
|
14794
|
-
|
|
14936
|
+
const [welcomeExiting, setWelcomeExiting] = import_react26.default.useState(false);
|
|
14937
|
+
const prevMessageCountRef = import_react26.default.useRef(messages.length);
|
|
14938
|
+
import_react26.default.useEffect(() => {
|
|
14795
14939
|
let timer;
|
|
14796
14940
|
if (prevMessageCountRef.current === 0 && messages.length > 0) {
|
|
14797
14941
|
setWelcomeExiting(true);
|
|
@@ -14815,7 +14959,7 @@ var ChatUIView = ({
|
|
|
14815
14959
|
const handleChoiceClick = (choice) => {
|
|
14816
14960
|
setInput(choice.text);
|
|
14817
14961
|
};
|
|
14818
|
-
const memoryItems =
|
|
14962
|
+
const memoryItems = import_react26.default.useMemo(() => {
|
|
14819
14963
|
if (!globalMemory?.state.entries) return [];
|
|
14820
14964
|
const items = [];
|
|
14821
14965
|
for (const [key, entry] of globalMemory.state.entries) {
|
|
@@ -14829,7 +14973,7 @@ var ChatUIView = ({
|
|
|
14829
14973
|
}
|
|
14830
14974
|
return items;
|
|
14831
14975
|
}, [globalMemory?.state.entries]);
|
|
14832
|
-
const projectMemoryItems =
|
|
14976
|
+
const projectMemoryItems = import_react26.default.useMemo(() => {
|
|
14833
14977
|
if (!projectMemory?.state.entries) return [];
|
|
14834
14978
|
const items = [];
|
|
14835
14979
|
for (const [key, entry] of projectMemory.state.entries) {
|
|
@@ -15351,13 +15495,13 @@ var ChatUI = (props) => {
|
|
|
15351
15495
|
};
|
|
15352
15496
|
|
|
15353
15497
|
// src/react/ChatFloatingWidget.tsx
|
|
15354
|
-
var
|
|
15498
|
+
var import_react34 = require("react");
|
|
15355
15499
|
|
|
15356
15500
|
// src/react/hooks/useFloatingWidget.ts
|
|
15357
|
-
var
|
|
15501
|
+
var import_react28 = require("react");
|
|
15358
15502
|
|
|
15359
15503
|
// src/react/hooks/useDragResize.ts
|
|
15360
|
-
var
|
|
15504
|
+
var import_react27 = require("react");
|
|
15361
15505
|
var DRAG_THRESHOLD = 5;
|
|
15362
15506
|
var FAB_SIZE = 56;
|
|
15363
15507
|
var EDGE_MARGIN = 24;
|
|
@@ -15425,8 +15569,8 @@ var useDragResize = (options) => {
|
|
|
15425
15569
|
maxWidth = DEFAULT_MAX_WIDTH,
|
|
15426
15570
|
minHeight = DEFAULT_MIN_HEIGHT
|
|
15427
15571
|
} = options;
|
|
15428
|
-
const [isMobile, setIsMobile] = (0,
|
|
15429
|
-
(0,
|
|
15572
|
+
const [isMobile, setIsMobile] = (0, import_react27.useState)(false);
|
|
15573
|
+
(0, import_react27.useEffect)(() => {
|
|
15430
15574
|
if (typeof window === "undefined") return;
|
|
15431
15575
|
const mql = window.matchMedia("(max-width: 767px)");
|
|
15432
15576
|
setIsMobile(mql.matches);
|
|
@@ -15441,19 +15585,19 @@ var useDragResize = (options) => {
|
|
|
15441
15585
|
panelWidth: initialWidth,
|
|
15442
15586
|
panelHeight: initialHeight
|
|
15443
15587
|
});
|
|
15444
|
-
const [fabPos, setFabPos] = (0,
|
|
15445
|
-
const [panelSize, setPanelSize] = (0,
|
|
15446
|
-
const [isDragging, setIsDragging] = (0,
|
|
15447
|
-
const [isResizing, setIsResizing] = (0,
|
|
15448
|
-
const [isDizzy, setIsDizzy] = (0,
|
|
15449
|
-
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)(() => ({
|
|
15450
15594
|
width: typeof window !== "undefined" ? window.innerWidth : 1024,
|
|
15451
15595
|
height: typeof window !== "undefined" ? window.innerHeight : 768
|
|
15452
15596
|
}));
|
|
15453
15597
|
const vw = viewport.width;
|
|
15454
15598
|
const vh = viewport.height;
|
|
15455
|
-
const initializedRef = (0,
|
|
15456
|
-
(0,
|
|
15599
|
+
const initializedRef = (0, import_react27.useRef)(false);
|
|
15600
|
+
(0, import_react27.useEffect)(() => {
|
|
15457
15601
|
if (initializedRef.current || typeof window === "undefined") return;
|
|
15458
15602
|
initializedRef.current = true;
|
|
15459
15603
|
const correctFab = getInitialFabPosition(initialPosition);
|
|
@@ -15466,40 +15610,40 @@ var useDragResize = (options) => {
|
|
|
15466
15610
|
setFabPos({ x: loaded.fabX, y: loaded.fabY });
|
|
15467
15611
|
setPanelSize({ width: loaded.panelWidth, height: loaded.panelHeight });
|
|
15468
15612
|
}, [initialPosition, storageKey, initialWidth, initialHeight]);
|
|
15469
|
-
const dragStartRef = (0,
|
|
15470
|
-
const isDraggingRef = (0,
|
|
15471
|
-
const wasDragRef = (0,
|
|
15472
|
-
const userDraggedRef = (0,
|
|
15473
|
-
const dragDeltaRef = (0,
|
|
15474
|
-
const fabElRef = (0,
|
|
15475
|
-
const dragCleanupRef = (0,
|
|
15476
|
-
const snapTimerRef = (0,
|
|
15477
|
-
const prevDragRef = (0,
|
|
15478
|
-
const shakeScoreRef = (0,
|
|
15479
|
-
const prevVelocityRef = (0,
|
|
15480
|
-
const isDizzyRef = (0,
|
|
15481
|
-
const dizzyTimerRef = (0,
|
|
15482
|
-
const lastVelocityRef = (0,
|
|
15483
|
-
const dragRafRef = (0,
|
|
15484
|
-
const animCleanupRef = (0,
|
|
15485
|
-
const snapEnabledRef = (0,
|
|
15486
|
-
const storageKeyRef = (0,
|
|
15487
|
-
const panelSizeRef = (0,
|
|
15488
|
-
const fabPosRef = (0,
|
|
15489
|
-
(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)(() => {
|
|
15490
15634
|
snapEnabledRef.current = snapEnabled;
|
|
15491
15635
|
}, [snapEnabled]);
|
|
15492
|
-
(0,
|
|
15636
|
+
(0, import_react27.useEffect)(() => {
|
|
15493
15637
|
storageKeyRef.current = storageKey;
|
|
15494
15638
|
}, [storageKey]);
|
|
15495
|
-
(0,
|
|
15639
|
+
(0, import_react27.useEffect)(() => {
|
|
15496
15640
|
panelSizeRef.current = panelSize;
|
|
15497
15641
|
}, [panelSize]);
|
|
15498
|
-
(0,
|
|
15642
|
+
(0, import_react27.useEffect)(() => {
|
|
15499
15643
|
fabPosRef.current = fabPos;
|
|
15500
15644
|
}, [fabPos]);
|
|
15501
|
-
const resizeStartRef = (0,
|
|
15502
|
-
(0,
|
|
15645
|
+
const resizeStartRef = (0, import_react27.useRef)(null);
|
|
15646
|
+
(0, import_react27.useEffect)(() => {
|
|
15503
15647
|
return () => {
|
|
15504
15648
|
dragCleanupRef.current?.();
|
|
15505
15649
|
if (snapTimerRef.current) clearTimeout(snapTimerRef.current);
|
|
@@ -15507,7 +15651,7 @@ var useDragResize = (options) => {
|
|
|
15507
15651
|
if (dragRafRef.current) cancelAnimationFrame(dragRafRef.current);
|
|
15508
15652
|
};
|
|
15509
15653
|
}, []);
|
|
15510
|
-
const handleFabPointerDown = (0,
|
|
15654
|
+
const handleFabPointerDown = (0, import_react27.useCallback)((e) => {
|
|
15511
15655
|
if (disabled || isMobile) return;
|
|
15512
15656
|
dragCleanupRef.current?.();
|
|
15513
15657
|
const el = e.currentTarget;
|
|
@@ -15689,7 +15833,7 @@ var useDragResize = (options) => {
|
|
|
15689
15833
|
}, [disabled, isMobile]);
|
|
15690
15834
|
const fabCenterX = fabPos.x + FAB_SIZE / 2;
|
|
15691
15835
|
const panelDirection = fabCenterX > vw / 2 ? "left" : "right";
|
|
15692
|
-
const panelPositionStyle = (0,
|
|
15836
|
+
const panelPositionStyle = (0, import_react27.useMemo)(() => {
|
|
15693
15837
|
if (isMobile || disabled) return {};
|
|
15694
15838
|
const fabIsTop = fabPos.y < vh / 2;
|
|
15695
15839
|
let panelBottom;
|
|
@@ -15721,7 +15865,7 @@ var useDragResize = (options) => {
|
|
|
15721
15865
|
borderRadius: "var(--floating-panel-radius, 16px)"
|
|
15722
15866
|
};
|
|
15723
15867
|
}, [isMobile, disabled, fabPos.x, fabPos.y, panelSize.width, panelSize.height, vw, vh, panelDirection, minHeight]);
|
|
15724
|
-
const handleResizePointerDown = (0,
|
|
15868
|
+
const handleResizePointerDown = (0, import_react27.useCallback)((edge, e) => {
|
|
15725
15869
|
if (disabled || isMobile) return;
|
|
15726
15870
|
e.preventDefault();
|
|
15727
15871
|
e.stopPropagation();
|
|
@@ -15737,7 +15881,7 @@ var useDragResize = (options) => {
|
|
|
15737
15881
|
};
|
|
15738
15882
|
setIsResizing(true);
|
|
15739
15883
|
}, [disabled, isMobile]);
|
|
15740
|
-
const handleResizePointerMove = (0,
|
|
15884
|
+
const handleResizePointerMove = (0, import_react27.useCallback)((e) => {
|
|
15741
15885
|
if (!resizeStartRef.current) return;
|
|
15742
15886
|
const { startX, startY, width, height, edge, fabY } = resizeStartRef.current;
|
|
15743
15887
|
const dx = e.clientX - startX;
|
|
@@ -15761,7 +15905,7 @@ var useDragResize = (options) => {
|
|
|
15761
15905
|
newHeight = Math.max(minHeight, Math.min(maxH, newHeight));
|
|
15762
15906
|
setPanelSize({ width: newWidth, height: newHeight });
|
|
15763
15907
|
}, [minWidth, maxWidth, minHeight]);
|
|
15764
|
-
const handleResizePointerUp = (0,
|
|
15908
|
+
const handleResizePointerUp = (0, import_react27.useCallback)((e) => {
|
|
15765
15909
|
if (!resizeStartRef.current) return;
|
|
15766
15910
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
15767
15911
|
resizeStartRef.current = null;
|
|
@@ -15770,7 +15914,7 @@ var useDragResize = (options) => {
|
|
|
15770
15914
|
const fp = fabPosRef.current;
|
|
15771
15915
|
persistState(storageKeyRef.current, { fabX: fp.x, fabY: fp.y, panelWidth: ps.width, panelHeight: ps.height });
|
|
15772
15916
|
}, []);
|
|
15773
|
-
(0,
|
|
15917
|
+
(0, import_react27.useEffect)(() => {
|
|
15774
15918
|
if (typeof window === "undefined") return;
|
|
15775
15919
|
const handleResize = () => {
|
|
15776
15920
|
const newVw = window.innerWidth;
|
|
@@ -15832,9 +15976,9 @@ var useFloatingWidget = (options) => {
|
|
|
15832
15976
|
maxWidth,
|
|
15833
15977
|
minHeight
|
|
15834
15978
|
} = options || {};
|
|
15835
|
-
const [isOpen, setIsOpen] = (0,
|
|
15836
|
-
const [activeTab, setActiveTab] = (0,
|
|
15837
|
-
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);
|
|
15838
15982
|
const dragResize = useDragResize({
|
|
15839
15983
|
initialPosition: position,
|
|
15840
15984
|
initialWidth: width,
|
|
@@ -15846,27 +15990,27 @@ var useFloatingWidget = (options) => {
|
|
|
15846
15990
|
maxWidth,
|
|
15847
15991
|
minHeight
|
|
15848
15992
|
});
|
|
15849
|
-
const onOpenRef = (0,
|
|
15850
|
-
const onCloseRef = (0,
|
|
15851
|
-
const onTabChangeRef = (0,
|
|
15852
|
-
(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)(() => {
|
|
15853
15997
|
onOpenRef.current = onOpen;
|
|
15854
15998
|
}, [onOpen]);
|
|
15855
|
-
(0,
|
|
15999
|
+
(0, import_react28.useEffect)(() => {
|
|
15856
16000
|
onCloseRef.current = onClose;
|
|
15857
16001
|
}, [onClose]);
|
|
15858
|
-
(0,
|
|
16002
|
+
(0, import_react28.useEffect)(() => {
|
|
15859
16003
|
onTabChangeRef.current = onTabChange;
|
|
15860
16004
|
}, [onTabChange]);
|
|
15861
|
-
const open = (0,
|
|
16005
|
+
const open = (0, import_react28.useCallback)(() => {
|
|
15862
16006
|
setIsOpen(true);
|
|
15863
16007
|
onOpenRef.current?.();
|
|
15864
16008
|
}, []);
|
|
15865
|
-
const close = (0,
|
|
16009
|
+
const close = (0, import_react28.useCallback)(() => {
|
|
15866
16010
|
setIsOpen(false);
|
|
15867
16011
|
onCloseRef.current?.();
|
|
15868
16012
|
}, []);
|
|
15869
|
-
const toggle = (0,
|
|
16013
|
+
const toggle = (0, import_react28.useCallback)(() => {
|
|
15870
16014
|
setIsOpen((prev) => {
|
|
15871
16015
|
const next = !prev;
|
|
15872
16016
|
if (next) onOpenRef.current?.();
|
|
@@ -15874,15 +16018,15 @@ var useFloatingWidget = (options) => {
|
|
|
15874
16018
|
return next;
|
|
15875
16019
|
});
|
|
15876
16020
|
}, []);
|
|
15877
|
-
const setTab = (0,
|
|
16021
|
+
const setTab = (0, import_react28.useCallback)((tabKey) => {
|
|
15878
16022
|
setActiveTab(tabKey);
|
|
15879
16023
|
onTabChangeRef.current?.(tabKey);
|
|
15880
16024
|
}, []);
|
|
15881
|
-
const handleFabInteraction = (0,
|
|
16025
|
+
const handleFabInteraction = (0, import_react28.useCallback)(() => {
|
|
15882
16026
|
if (dragResize.isDragging) return;
|
|
15883
16027
|
toggle();
|
|
15884
16028
|
}, [dragResize.isDragging, toggle]);
|
|
15885
|
-
(0,
|
|
16029
|
+
(0, import_react28.useEffect)(() => {
|
|
15886
16030
|
if (!isOpen) return;
|
|
15887
16031
|
const handleKeyDown = (e) => {
|
|
15888
16032
|
if (e.key === "Escape") {
|
|
@@ -15892,7 +16036,7 @@ var useFloatingWidget = (options) => {
|
|
|
15892
16036
|
document.addEventListener("keydown", handleKeyDown);
|
|
15893
16037
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
15894
16038
|
}, [isOpen, close]);
|
|
15895
|
-
(0,
|
|
16039
|
+
(0, import_react28.useEffect)(() => {
|
|
15896
16040
|
if (!isOpen) return;
|
|
15897
16041
|
const handleClickOutside = (e) => {
|
|
15898
16042
|
if (dragResize.isDragging || dragResize.isResizing) return;
|
|
@@ -15908,10 +16052,10 @@ var useFloatingWidget = (options) => {
|
|
|
15908
16052
|
};
|
|
15909
16053
|
|
|
15910
16054
|
// src/react/components/floating/FloatingFab.tsx
|
|
15911
|
-
var
|
|
16055
|
+
var import_react30 = require("react");
|
|
15912
16056
|
|
|
15913
16057
|
// src/react/components/floating/DevDiveCharacter.tsx
|
|
15914
|
-
var
|
|
16058
|
+
var import_react29 = require("react");
|
|
15915
16059
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
15916
16060
|
var THEMES = {
|
|
15917
16061
|
dark: { body: "#2ecc71", stroke: "#27ae60", highlight: "#3ddc84", face: "#1a1a2e", eyeLight: "#fff" },
|
|
@@ -15932,32 +16076,32 @@ var DevDiveFabCharacter = ({
|
|
|
15932
16076
|
isComplete = false
|
|
15933
16077
|
}) => {
|
|
15934
16078
|
const c = THEMES[theme];
|
|
15935
|
-
const [eyeOffset, setEyeOffset] = (0,
|
|
15936
|
-
const [isBlinking, setIsBlinking] = (0,
|
|
15937
|
-
const [isHappy, setIsHappy] = (0,
|
|
15938
|
-
const [isPressed, setIsPressed] = (0,
|
|
15939
|
-
const [mouthOpen, setMouthOpen] = (0,
|
|
15940
|
-
const [isSleepy, setIsSleepy] = (0,
|
|
15941
|
-
const [isSurprised, setIsSurprised] = (0,
|
|
15942
|
-
const [isWinking, setIsWinking] = (0,
|
|
15943
|
-
const [isCatMouth, setIsCatMouth] = (0,
|
|
15944
|
-
const [isEntering, setIsEntering] = (0,
|
|
15945
|
-
const svgRef = (0,
|
|
15946
|
-
const cleanupRef = (0,
|
|
15947
|
-
const lastActivityRef = (0,
|
|
15948
|
-
const isSleepyRef = (0,
|
|
15949
|
-
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)(() => {
|
|
15950
16094
|
lastActivityRef.current = Date.now();
|
|
15951
16095
|
if (isSleepyRef.current) {
|
|
15952
16096
|
isSleepyRef.current = false;
|
|
15953
16097
|
setIsSleepy(false);
|
|
15954
16098
|
}
|
|
15955
16099
|
}, []);
|
|
15956
|
-
(0,
|
|
16100
|
+
(0, import_react29.useEffect)(() => {
|
|
15957
16101
|
const t = setTimeout(() => setIsEntering(false), 600);
|
|
15958
16102
|
return () => clearTimeout(t);
|
|
15959
16103
|
}, []);
|
|
15960
|
-
(0,
|
|
16104
|
+
(0, import_react29.useEffect)(() => {
|
|
15961
16105
|
let timer;
|
|
15962
16106
|
let blinkTimer;
|
|
15963
16107
|
const scheduleBlink = () => {
|
|
@@ -15973,7 +16117,7 @@ var DevDiveFabCharacter = ({
|
|
|
15973
16117
|
clearTimeout(blinkTimer);
|
|
15974
16118
|
};
|
|
15975
16119
|
}, []);
|
|
15976
|
-
(0,
|
|
16120
|
+
(0, import_react29.useEffect)(() => {
|
|
15977
16121
|
if (!isTalking) {
|
|
15978
16122
|
setMouthOpen(false);
|
|
15979
16123
|
return;
|
|
@@ -15986,7 +16130,7 @@ var DevDiveFabCharacter = ({
|
|
|
15986
16130
|
toggle();
|
|
15987
16131
|
return () => clearTimeout(timer);
|
|
15988
16132
|
}, [isTalking]);
|
|
15989
|
-
(0,
|
|
16133
|
+
(0, import_react29.useEffect)(() => {
|
|
15990
16134
|
if (typeof window === "undefined") return;
|
|
15991
16135
|
let rafId = 0;
|
|
15992
16136
|
const onMouseMove = (e) => {
|
|
@@ -16010,7 +16154,7 @@ var DevDiveFabCharacter = ({
|
|
|
16010
16154
|
cancelAnimationFrame(rafId);
|
|
16011
16155
|
};
|
|
16012
16156
|
}, []);
|
|
16013
|
-
(0,
|
|
16157
|
+
(0, import_react29.useEffect)(() => {
|
|
16014
16158
|
let upTimer;
|
|
16015
16159
|
let surpriseTimer;
|
|
16016
16160
|
const rafId = requestAnimationFrame(() => {
|
|
@@ -16053,10 +16197,10 @@ var DevDiveFabCharacter = ({
|
|
|
16053
16197
|
cleanupRef.current?.();
|
|
16054
16198
|
};
|
|
16055
16199
|
}, [markActivity]);
|
|
16056
|
-
(0,
|
|
16200
|
+
(0, import_react29.useEffect)(() => {
|
|
16057
16201
|
markActivity();
|
|
16058
16202
|
}, [isOpen, isTalking, markActivity]);
|
|
16059
|
-
(0,
|
|
16203
|
+
(0, import_react29.useEffect)(() => {
|
|
16060
16204
|
if (isOpen || isTalking || isDizzy) return;
|
|
16061
16205
|
const check = setInterval(() => {
|
|
16062
16206
|
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS && !isSleepyRef.current) {
|
|
@@ -16066,8 +16210,8 @@ var DevDiveFabCharacter = ({
|
|
|
16066
16210
|
}, 5e3);
|
|
16067
16211
|
return () => clearInterval(check);
|
|
16068
16212
|
}, [isOpen, isTalking, isDizzy]);
|
|
16069
|
-
const prevCompleteRef = (0,
|
|
16070
|
-
(0,
|
|
16213
|
+
const prevCompleteRef = (0, import_react29.useRef)(false);
|
|
16214
|
+
(0, import_react29.useEffect)(() => {
|
|
16071
16215
|
const wasComplete = prevCompleteRef.current;
|
|
16072
16216
|
prevCompleteRef.current = isComplete;
|
|
16073
16217
|
if (wasComplete || !isComplete) return;
|
|
@@ -16075,7 +16219,7 @@ var DevDiveFabCharacter = ({
|
|
|
16075
16219
|
const t = setTimeout(() => setIsWinking(false), WINK_DURATION_MS);
|
|
16076
16220
|
return () => clearTimeout(t);
|
|
16077
16221
|
}, [isComplete]);
|
|
16078
|
-
(0,
|
|
16222
|
+
(0, import_react29.useEffect)(() => {
|
|
16079
16223
|
if (isOpen || isTalking || isDizzy || isError || isSleepy) {
|
|
16080
16224
|
setIsCatMouth(false);
|
|
16081
16225
|
return;
|
|
@@ -16267,12 +16411,12 @@ var FloatingFab = ({
|
|
|
16267
16411
|
}) => {
|
|
16268
16412
|
const isRight = position.includes("right");
|
|
16269
16413
|
const isBottom = position.includes("bottom");
|
|
16270
|
-
const fabRef = (0,
|
|
16414
|
+
const fabRef = (0, import_react30.useRef)(null);
|
|
16271
16415
|
const isCharacterMode = !icon;
|
|
16272
|
-
const [bubbleOnRight, setBubbleOnRight] = (0,
|
|
16416
|
+
const [bubbleOnRight, setBubbleOnRight] = (0, import_react30.useState)(!isRight);
|
|
16273
16417
|
const posLeft = positionStyle?.left;
|
|
16274
16418
|
const posTop = positionStyle?.top;
|
|
16275
|
-
(0,
|
|
16419
|
+
(0, import_react30.useEffect)(() => {
|
|
16276
16420
|
if (!fabRef.current || typeof window === "undefined") {
|
|
16277
16421
|
setBubbleOnRight(!isRight);
|
|
16278
16422
|
return;
|
|
@@ -16280,14 +16424,14 @@ var FloatingFab = ({
|
|
|
16280
16424
|
const rect = fabRef.current.getBoundingClientRect();
|
|
16281
16425
|
setBubbleOnRight(rect.left + rect.width / 2 < window.innerWidth / 2);
|
|
16282
16426
|
}, [isRight, posLeft, posTop]);
|
|
16283
|
-
const [bubbleText, setBubbleText] = (0,
|
|
16284
|
-
const [bubbleExiting, setBubbleExiting] = (0,
|
|
16285
|
-
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);
|
|
16286
16430
|
bubbleTextRef.current = bubbleText;
|
|
16287
|
-
const [displayText, setDisplayText] = (0,
|
|
16288
|
-
const [isTyping, setIsTyping] = (0,
|
|
16289
|
-
const typingTimerRef = (0,
|
|
16290
|
-
(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)(() => {
|
|
16291
16435
|
if (notification) {
|
|
16292
16436
|
setBubbleText(notification);
|
|
16293
16437
|
setBubbleExiting(false);
|
|
@@ -16323,9 +16467,9 @@ var FloatingFab = ({
|
|
|
16323
16467
|
}, 300);
|
|
16324
16468
|
return () => clearTimeout(timer);
|
|
16325
16469
|
}, [notification]);
|
|
16326
|
-
const notifContentRef = (0,
|
|
16327
|
-
const [needsMarquee, setNeedsMarquee] = (0,
|
|
16328
|
-
(0,
|
|
16470
|
+
const notifContentRef = (0, import_react30.useRef)(null);
|
|
16471
|
+
const [needsMarquee, setNeedsMarquee] = (0, import_react30.useState)(false);
|
|
16472
|
+
(0, import_react30.useEffect)(() => {
|
|
16329
16473
|
if (isTyping || !notification) {
|
|
16330
16474
|
setNeedsMarquee(false);
|
|
16331
16475
|
return;
|
|
@@ -16467,7 +16611,7 @@ var FloatingFab = ({
|
|
|
16467
16611
|
};
|
|
16468
16612
|
|
|
16469
16613
|
// src/react/components/floating/FloatingPanel.tsx
|
|
16470
|
-
var
|
|
16614
|
+
var import_react31 = require("react");
|
|
16471
16615
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
16472
16616
|
var FloatingPanel = ({
|
|
16473
16617
|
isOpen,
|
|
@@ -16485,8 +16629,8 @@ var FloatingPanel = ({
|
|
|
16485
16629
|
}) => {
|
|
16486
16630
|
const isRight = position.includes("right");
|
|
16487
16631
|
const themeClass = theme?.mode === "dark" ? "chatllm-dark" : "";
|
|
16488
|
-
const [isMobile, setIsMobile] = (0,
|
|
16489
|
-
(0,
|
|
16632
|
+
const [isMobile, setIsMobile] = (0, import_react31.useState)(false);
|
|
16633
|
+
(0, import_react31.useEffect)(() => {
|
|
16490
16634
|
if (typeof window === "undefined") return;
|
|
16491
16635
|
const mql = window.matchMedia("(max-width: 767px)");
|
|
16492
16636
|
setIsMobile(mql.matches);
|
|
@@ -16494,10 +16638,10 @@ var FloatingPanel = ({
|
|
|
16494
16638
|
mql.addEventListener("change", handler);
|
|
16495
16639
|
return () => mql.removeEventListener("change", handler);
|
|
16496
16640
|
}, []);
|
|
16497
|
-
const [shouldRender, setShouldRender] = (0,
|
|
16498
|
-
const [isVisible, setIsVisible] = (0,
|
|
16499
|
-
const rafRef = (0,
|
|
16500
|
-
(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)(() => {
|
|
16501
16645
|
if (isOpen) {
|
|
16502
16646
|
setShouldRender(true);
|
|
16503
16647
|
rafRef.current = requestAnimationFrame(() => {
|
|
@@ -16512,7 +16656,7 @@ var FloatingPanel = ({
|
|
|
16512
16656
|
}
|
|
16513
16657
|
return () => cancelAnimationFrame(rafRef.current);
|
|
16514
16658
|
}, [isOpen]);
|
|
16515
|
-
(0,
|
|
16659
|
+
(0, import_react31.useEffect)(() => {
|
|
16516
16660
|
if (!isOpen || !isMobile) return;
|
|
16517
16661
|
const prev = document.body.style.overflow;
|
|
16518
16662
|
document.body.style.overflow = "hidden";
|
|
@@ -16673,10 +16817,10 @@ var FloatingTabBar = ({
|
|
|
16673
16817
|
};
|
|
16674
16818
|
|
|
16675
16819
|
// src/react/components/floating/CompactChatView.tsx
|
|
16676
|
-
var
|
|
16820
|
+
var import_react33 = require("react");
|
|
16677
16821
|
|
|
16678
16822
|
// src/react/components/floating/CompactSessionMenu.tsx
|
|
16679
|
-
var
|
|
16823
|
+
var import_react32 = require("react");
|
|
16680
16824
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
16681
16825
|
var CompactSessionMenu = ({
|
|
16682
16826
|
sessions,
|
|
@@ -16688,13 +16832,13 @@ var CompactSessionMenu = ({
|
|
|
16688
16832
|
onClose,
|
|
16689
16833
|
isLoading = false
|
|
16690
16834
|
}) => {
|
|
16691
|
-
const menuRef = (0,
|
|
16692
|
-
const inputRef = (0,
|
|
16693
|
-
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);
|
|
16694
16838
|
onCloseRef.current = onClose;
|
|
16695
|
-
const [editingId, setEditingId] = (0,
|
|
16696
|
-
const [editingTitle, setEditingTitle] = (0,
|
|
16697
|
-
(0,
|
|
16839
|
+
const [editingId, setEditingId] = (0, import_react32.useState)(null);
|
|
16840
|
+
const [editingTitle, setEditingTitle] = (0, import_react32.useState)("");
|
|
16841
|
+
(0, import_react32.useEffect)(() => {
|
|
16698
16842
|
const handleMouseDown = (e) => {
|
|
16699
16843
|
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
16700
16844
|
onCloseRef.current();
|
|
@@ -16703,7 +16847,7 @@ var CompactSessionMenu = ({
|
|
|
16703
16847
|
document.addEventListener("mousedown", handleMouseDown);
|
|
16704
16848
|
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
16705
16849
|
}, []);
|
|
16706
|
-
(0,
|
|
16850
|
+
(0, import_react32.useEffect)(() => {
|
|
16707
16851
|
if (editingId && inputRef.current) {
|
|
16708
16852
|
inputRef.current.focus();
|
|
16709
16853
|
inputRef.current.select();
|
|
@@ -17018,16 +17162,16 @@ var CompactChatView = ({
|
|
|
17018
17162
|
const handleChoiceClick = (choice) => {
|
|
17019
17163
|
setInput(choice.text);
|
|
17020
17164
|
};
|
|
17021
|
-
const [showSessionMenu, setShowSessionMenu] = (0,
|
|
17022
|
-
const handleSessionSelect = (0,
|
|
17165
|
+
const [showSessionMenu, setShowSessionMenu] = (0, import_react33.useState)(false);
|
|
17166
|
+
const handleSessionSelect = (0, import_react33.useCallback)((id) => {
|
|
17023
17167
|
selectSession(id);
|
|
17024
17168
|
setShowSessionMenu(false);
|
|
17025
17169
|
}, [selectSession]);
|
|
17026
|
-
const handleNewSession = (0,
|
|
17170
|
+
const handleNewSession = (0, import_react33.useCallback)(() => {
|
|
17027
17171
|
newSession();
|
|
17028
17172
|
setShowSessionMenu(false);
|
|
17029
17173
|
}, [newSession]);
|
|
17030
|
-
const handleCloseMenu = (0,
|
|
17174
|
+
const handleCloseMenu = (0, import_react33.useCallback)(() => {
|
|
17031
17175
|
setShowSessionMenu(false);
|
|
17032
17176
|
}, []);
|
|
17033
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";
|
|
@@ -17347,11 +17491,11 @@ var ChatFloatingWidget = ({
|
|
|
17347
17491
|
maxWidth,
|
|
17348
17492
|
minHeight
|
|
17349
17493
|
});
|
|
17350
|
-
const notifObj = (0,
|
|
17494
|
+
const notifObj = (0, import_react34.useMemo)(() => {
|
|
17351
17495
|
if (!notification) return null;
|
|
17352
17496
|
return typeof notification === "string" ? { text: notification } : notification;
|
|
17353
17497
|
}, [notification]);
|
|
17354
|
-
const handleFabClick = (0,
|
|
17498
|
+
const handleFabClick = (0, import_react34.useCallback)(() => {
|
|
17355
17499
|
if (notifObj?.onClick) {
|
|
17356
17500
|
notifObj.onClick();
|
|
17357
17501
|
if (!isOpen) handleFabInteraction();
|
|
@@ -17365,7 +17509,7 @@ var ChatFloatingWidget = ({
|
|
|
17365
17509
|
}
|
|
17366
17510
|
handleFabInteraction();
|
|
17367
17511
|
}, [notifObj, isOpen, handleFabInteraction, setTab, tabs]);
|
|
17368
|
-
const allTabs = (0,
|
|
17512
|
+
const allTabs = (0, import_react34.useMemo)(() => {
|
|
17369
17513
|
const chatTab = {
|
|
17370
17514
|
key: "chat",
|
|
17371
17515
|
label: "\uCC44\uD305",
|
|
@@ -17379,16 +17523,16 @@ var ChatFloatingWidget = ({
|
|
|
17379
17523
|
}));
|
|
17380
17524
|
return [chatTab, ...customTabs];
|
|
17381
17525
|
}, [tabs]);
|
|
17382
|
-
const [unreadBadge, setUnreadBadge] = (0,
|
|
17383
|
-
const seenMessageIdsRef = (0,
|
|
17384
|
-
(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)(() => {
|
|
17385
17529
|
if (seenMessageIdsRef.current.size === 0 && chatState.messages.length > 0) {
|
|
17386
17530
|
for (const m of chatState.messages) {
|
|
17387
17531
|
seenMessageIdsRef.current.add(m.id);
|
|
17388
17532
|
}
|
|
17389
17533
|
}
|
|
17390
17534
|
}, [chatState.messages]);
|
|
17391
|
-
(0,
|
|
17535
|
+
(0, import_react34.useEffect)(() => {
|
|
17392
17536
|
if (isOpen) {
|
|
17393
17537
|
for (const m of chatState.messages) {
|
|
17394
17538
|
seenMessageIdsRef.current.add(m.id);
|
|
@@ -17405,7 +17549,7 @@ var ChatFloatingWidget = ({
|
|
|
17405
17549
|
}
|
|
17406
17550
|
}
|
|
17407
17551
|
}, [chatState.messages, isOpen]);
|
|
17408
|
-
(0,
|
|
17552
|
+
(0, import_react34.useEffect)(() => {
|
|
17409
17553
|
if (isOpen) {
|
|
17410
17554
|
setUnreadBadge(0);
|
|
17411
17555
|
for (const m of chatState.messages) {
|
|
@@ -17413,13 +17557,13 @@ var ChatFloatingWidget = ({
|
|
|
17413
17557
|
}
|
|
17414
17558
|
}
|
|
17415
17559
|
}, [isOpen, chatState.messages]);
|
|
17416
|
-
const totalBadge = (0,
|
|
17560
|
+
const totalBadge = (0, import_react34.useMemo)(() => {
|
|
17417
17561
|
return tabs.reduce((sum, t) => sum + (t.badge || 0), 0) + unreadBadge;
|
|
17418
17562
|
}, [tabs, unreadBadge]);
|
|
17419
|
-
const isTalking = (0,
|
|
17420
|
-
const prevLoadingRef = (0,
|
|
17421
|
-
const [isComplete, setIsComplete] = (0,
|
|
17422
|
-
(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)(() => {
|
|
17423
17567
|
const wasLoading = prevLoadingRef.current;
|
|
17424
17568
|
prevLoadingRef.current = chatState.isLoading;
|
|
17425
17569
|
if (!wasLoading || chatState.isLoading || chatState.messages.length === 0) return;
|
|
@@ -17535,7 +17679,7 @@ var ChatFloatingWidget = ({
|
|
|
17535
17679
|
};
|
|
17536
17680
|
|
|
17537
17681
|
// src/react/hooks/useDeepResearch.ts
|
|
17538
|
-
var
|
|
17682
|
+
var import_react35 = require("react");
|
|
17539
17683
|
var REPORT_GENERATION_PROMPT2 = `\uB2F9\uC2E0\uC740 \uB9AC\uC11C\uCE58 \uBCF4\uACE0\uC11C \uC791\uC131 \uC804\uBB38\uAC00\uC785\uB2C8\uB2E4.
|
|
17540
17684
|
|
|
17541
17685
|
<collected_sources>
|
|
@@ -17586,10 +17730,10 @@ var QUERY_ANALYSIS_PROMPT2 = `\uC0AC\uC6A9\uC790 \uC9C8\uBB38\uC744 \uBD84\uC11D
|
|
|
17586
17730
|
- JSON \uC678 \uB2E4\uB978 \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uAE08\uC9C0`;
|
|
17587
17731
|
var useDeepResearch = (options) => {
|
|
17588
17732
|
const { onWebSearch, onExtractContent, apiEndpoint, apiKey, model, provider } = options;
|
|
17589
|
-
const [isResearching, setIsResearching] = (0,
|
|
17590
|
-
const [progress, setProgress] = (0,
|
|
17591
|
-
const abortControllerRef = (0,
|
|
17592
|
-
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)(
|
|
17593
17737
|
async (prompt, stream = false) => {
|
|
17594
17738
|
const response = await fetch(apiEndpoint, {
|
|
17595
17739
|
method: "POST",
|
|
@@ -17616,7 +17760,7 @@ var useDeepResearch = (options) => {
|
|
|
17616
17760
|
},
|
|
17617
17761
|
[apiEndpoint, apiKey, model, provider]
|
|
17618
17762
|
);
|
|
17619
|
-
const analyzeQuery2 = (0,
|
|
17763
|
+
const analyzeQuery2 = (0, import_react35.useCallback)(
|
|
17620
17764
|
async (query) => {
|
|
17621
17765
|
const prompt = QUERY_ANALYSIS_PROMPT2.replace("{question}", query);
|
|
17622
17766
|
const response = await callLLM2(prompt);
|
|
@@ -17635,7 +17779,7 @@ var useDeepResearch = (options) => {
|
|
|
17635
17779
|
},
|
|
17636
17780
|
[callLLM2]
|
|
17637
17781
|
);
|
|
17638
|
-
const runSubAgent2 = (0,
|
|
17782
|
+
const runSubAgent2 = (0, import_react35.useCallback)(
|
|
17639
17783
|
async (topic, queries, agentId, updateProgress) => {
|
|
17640
17784
|
updateProgress({ status: "searching", searchCount: 0, resultsCount: 0 });
|
|
17641
17785
|
const allResults = [];
|
|
@@ -17674,7 +17818,7 @@ var useDeepResearch = (options) => {
|
|
|
17674
17818
|
},
|
|
17675
17819
|
[onWebSearch, onExtractContent]
|
|
17676
17820
|
);
|
|
17677
|
-
const generateReport2 = (0,
|
|
17821
|
+
const generateReport2 = (0, import_react35.useCallback)(
|
|
17678
17822
|
async (query, results, onStreamContent) => {
|
|
17679
17823
|
const allSources = [];
|
|
17680
17824
|
const sourcesForPrompt = [];
|
|
@@ -17732,7 +17876,7 @@ var useDeepResearch = (options) => {
|
|
|
17732
17876
|
},
|
|
17733
17877
|
[callLLM2]
|
|
17734
17878
|
);
|
|
17735
|
-
const runDeepResearch = (0,
|
|
17879
|
+
const runDeepResearch = (0, import_react35.useCallback)(
|
|
17736
17880
|
async (query, onStreamContent) => {
|
|
17737
17881
|
abortControllerRef.current = new AbortController();
|
|
17738
17882
|
setIsResearching(true);
|
|
@@ -17835,7 +17979,7 @@ var useDeepResearch = (options) => {
|
|
|
17835
17979
|
},
|
|
17836
17980
|
[analyzeQuery2, runSubAgent2, generateReport2]
|
|
17837
17981
|
);
|
|
17838
|
-
const stopResearch = (0,
|
|
17982
|
+
const stopResearch = (0, import_react35.useCallback)(() => {
|
|
17839
17983
|
abortControllerRef.current?.abort();
|
|
17840
17984
|
setIsResearching(false);
|
|
17841
17985
|
setProgress(null);
|
|
@@ -17848,6 +17992,122 @@ var useDeepResearch = (options) => {
|
|
|
17848
17992
|
};
|
|
17849
17993
|
};
|
|
17850
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
|
+
|
|
17851
18111
|
// src/react/utils/conversationSearchAdapter.ts
|
|
17852
18112
|
var formatSearchResults = (results) => {
|
|
17853
18113
|
if (results.length === 0) {
|
|
@@ -18038,7 +18298,7 @@ var EmptyState = ({
|
|
|
18038
18298
|
};
|
|
18039
18299
|
|
|
18040
18300
|
// src/react/components/MemoryPanel.tsx
|
|
18041
|
-
var
|
|
18301
|
+
var import_react37 = require("react");
|
|
18042
18302
|
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
18043
18303
|
var categoryLabels = {
|
|
18044
18304
|
fact: "\uC0AC\uC6A9\uC790 \uC815\uBCF4",
|
|
@@ -18058,8 +18318,8 @@ var MemoryPanel = ({
|
|
|
18058
18318
|
isOpen,
|
|
18059
18319
|
onToggle
|
|
18060
18320
|
}) => {
|
|
18061
|
-
const [expandedId, setExpandedId] = (0,
|
|
18062
|
-
const [activeTab, setActiveTab] = (0,
|
|
18321
|
+
const [expandedId, setExpandedId] = (0, import_react37.useState)(null);
|
|
18322
|
+
const [activeTab, setActiveTab] = (0, import_react37.useState)("all");
|
|
18063
18323
|
const filteredItems = activeTab === "all" ? items : items.filter((item) => item.category === activeTab);
|
|
18064
18324
|
const formatDate = (timestamp) => {
|
|
18065
18325
|
const date = new Date(timestamp);
|
|
@@ -18375,9 +18635,53 @@ var MemoryPanel = ({
|
|
|
18375
18635
|
}
|
|
18376
18636
|
);
|
|
18377
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
|
+
};
|
|
18378
18681
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18379
18682
|
0 && (module.exports = {
|
|
18380
18683
|
ArtifactCard,
|
|
18684
|
+
ChatError,
|
|
18381
18685
|
ChatFloatingWidget,
|
|
18382
18686
|
ChatHeader,
|
|
18383
18687
|
ChatInput,
|
|
@@ -18390,6 +18694,7 @@ var MemoryPanel = ({
|
|
|
18390
18694
|
ContentPartRenderer,
|
|
18391
18695
|
DEFAULT_PROJECT_ID,
|
|
18392
18696
|
DEFAULT_PROJECT_TITLE,
|
|
18697
|
+
DEFAULT_RETRY_CONFIG,
|
|
18393
18698
|
DeepResearchProgressUI,
|
|
18394
18699
|
DevDiveAvatar,
|
|
18395
18700
|
DevDiveFabCharacter,
|
|
@@ -18413,20 +18718,28 @@ var MemoryPanel = ({
|
|
|
18413
18718
|
ResizeHandles,
|
|
18414
18719
|
SettingsModal,
|
|
18415
18720
|
SkillProgressUI,
|
|
18721
|
+
classifyFetchError,
|
|
18416
18722
|
convertSkillsToOpenAITools,
|
|
18417
18723
|
convertToolsToSkills,
|
|
18418
18724
|
createAdvancedResearchSkill,
|
|
18419
18725
|
createConversationSearchSkill,
|
|
18420
18726
|
createDeepResearchSkill,
|
|
18727
|
+
createTimeoutError,
|
|
18421
18728
|
migrateSessionsToProjects,
|
|
18729
|
+
parseSSELine,
|
|
18730
|
+
parseSSEResponse,
|
|
18422
18731
|
useChatUI,
|
|
18732
|
+
useChecklist,
|
|
18733
|
+
useContentParsers,
|
|
18423
18734
|
useDeepResearch,
|
|
18424
18735
|
useDragResize,
|
|
18425
18736
|
useFloatingWidget,
|
|
18426
18737
|
useImageError,
|
|
18427
18738
|
useObserver,
|
|
18428
18739
|
useProject,
|
|
18429
|
-
useSkills
|
|
18740
|
+
useSkills,
|
|
18741
|
+
useStreamingFetch,
|
|
18742
|
+
withRetry
|
|
18430
18743
|
});
|
|
18431
18744
|
/**
|
|
18432
18745
|
* @description localStorage 기반 메모리 저장소 어댑터
|