@agents24/chat-react 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -12
- package/dist/chunk-E3TJ3I6G.js +358 -0
- package/dist/chunk-E3TJ3I6G.js.map +1 -0
- package/dist/chunk-EWZO4QJI.js +140 -0
- package/dist/chunk-EWZO4QJI.js.map +1 -0
- package/dist/chunk-UU7OXHL3.js +11 -0
- package/dist/chunk-UU7OXHL3.js.map +1 -0
- package/dist/context-window.d.ts +38 -0
- package/dist/controller.d.ts +22 -0
- package/dist/index.cjs +653 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -466
- package/dist/index.js +290 -455
- package/dist/index.js.map +1 -1
- package/dist/latest-thread-scroller.d.ts +54 -0
- package/dist/model.d.ts +29 -0
- package/dist/renderers.d.ts +10 -0
- package/dist/sse.d.ts +3 -0
- package/dist/streaming-text.d.ts +24 -0
- package/dist/templates/agent-chat-shell.d.ts +25 -0
- package/dist/templates/index.cjs +523 -0
- package/dist/templates/index.cjs.map +1 -0
- package/dist/templates/index.d.ts +1 -0
- package/dist/templates/index.js +140 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/transport.d.ts +29 -0
- package/dist/types.d.ts +312 -0
- package/dist/ui/adapters.d.ts +36 -0
- package/dist/ui/attachment.d.ts +23 -0
- package/dist/ui/bubble.d.ts +17 -0
- package/dist/ui/index.cjs +943 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/index.js +734 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/marker.d.ts +11 -0
- package/dist/ui/message-response.d.ts +7 -0
- package/dist/ui/message.d.ts +10 -0
- package/dist/ui/utils.d.ts +2 -0
- package/dist/viewport.d.ts +2 -0
- package/package.json +22 -4
- package/dist/index.d.cts +0 -466
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -23,31 +33,38 @@ __export(index_exports, {
|
|
|
23
33
|
DEFAULT_THREAD_PAGE_SIZE: () => DEFAULT_THREAD_PAGE_SIZE,
|
|
24
34
|
DefaultChatPart: () => DefaultChatPart,
|
|
25
35
|
DefaultToolPart: () => DefaultToolPart,
|
|
26
|
-
|
|
36
|
+
LatestThreadScroller: () => LatestThreadScroller,
|
|
37
|
+
LatestThreadScrollerButton: () => LatestThreadScrollerButton,
|
|
38
|
+
LatestThreadScrollerContent: () => LatestThreadScrollerContent,
|
|
39
|
+
LatestThreadScrollerItem: () => LatestThreadScrollerItem,
|
|
40
|
+
LatestThreadScrollerOutline: () => LatestThreadScrollerOutline,
|
|
41
|
+
LatestThreadScrollerProvider: () => LatestThreadScrollerProvider,
|
|
42
|
+
LatestThreadScrollerRoot: () => LatestThreadScrollerRoot,
|
|
43
|
+
LatestThreadScrollerViewport: () => LatestThreadScrollerViewport,
|
|
44
|
+
MessageScroller: () => import_message_scroller2.MessageScroller,
|
|
27
45
|
activeRunIdFromThread: () => activeRunIdFromThread,
|
|
28
46
|
activeRunIdFromThreadDetail: () => activeRunIdFromThreadDetail,
|
|
29
47
|
assistantTextFromParts: () => assistantTextFromParts,
|
|
30
48
|
assistantTextFromResponseBlocks: () => assistantTextFromResponseBlocks,
|
|
49
|
+
compressionFromContextWindow: () => compressionFromContextWindow,
|
|
31
50
|
consumeSseResponse: () => consumeSseResponse,
|
|
32
51
|
createChatId: () => createChatId,
|
|
33
52
|
createFetchChatTransport: () => createFetchChatTransport,
|
|
34
53
|
getActiveStreamingTextPartId: () => getActiveStreamingTextPartId,
|
|
35
|
-
getLatestScrollTop: () => getLatestScrollTop,
|
|
36
54
|
hasStaleUnfinishedAssistantCache: () => hasStaleUnfinishedAssistantCache,
|
|
37
55
|
hasUnfinishedAssistantMessage: () => hasUnfinishedAssistantMessage,
|
|
38
56
|
isActiveStreamingTextPart: () => isActiveStreamingTextPart,
|
|
39
|
-
isAtTimelineLatestEdge: () => isAtTimelineLatestEdge,
|
|
40
57
|
isRunningThreadStatus: () => isRunningThreadStatus,
|
|
41
|
-
isScrollable: () => isScrollable,
|
|
42
58
|
latestContextWindowFromThread: () => latestContextWindowFromThread,
|
|
59
|
+
mergeContextWindow: () => mergeContextWindow,
|
|
60
|
+
mergeContextWindowUpdate: () => mergeContextWindowUpdate,
|
|
43
61
|
mergeReasoningSteps: () => mergeReasoningSteps,
|
|
44
|
-
|
|
62
|
+
normalizeContextCompression: () => normalizeContextCompression,
|
|
63
|
+
normalizeContextWindow: () => normalizeContextWindow,
|
|
45
64
|
parseSseBlock: () => parseSseBlock,
|
|
46
65
|
partsFromResponseBlocks: () => partsFromResponseBlocks,
|
|
47
66
|
reasoningStepsFromParts: () => reasoningStepsFromParts,
|
|
48
67
|
renderChatPart: () => renderChatPart,
|
|
49
|
-
shouldPrefetchOlder: () => shouldPrefetchOlder,
|
|
50
|
-
shouldUseTopOriginTimeline: () => shouldUseTopOriginTimeline,
|
|
51
68
|
textFromFinalOutput: () => textFromFinalOutput,
|
|
52
69
|
threadActivityDate: () => threadActivityDate,
|
|
53
70
|
threadDetailToMessages: () => threadDetailToMessages,
|
|
@@ -55,7 +72,9 @@ __export(index_exports, {
|
|
|
55
72
|
titleFromMessage: () => titleFromMessage,
|
|
56
73
|
toolStateFromStatus: () => toolStateFromStatus,
|
|
57
74
|
useAgents24ChatController: () => useAgents24ChatController,
|
|
58
|
-
|
|
75
|
+
useMessageScroller: () => import_message_scroller2.useMessageScroller,
|
|
76
|
+
useMessageScrollerScrollable: () => import_message_scroller2.useMessageScrollerScrollable,
|
|
77
|
+
useMessageScrollerVisibility: () => import_message_scroller2.useMessageScrollerVisibility,
|
|
59
78
|
useStreamingText: () => useStreamingText
|
|
60
79
|
});
|
|
61
80
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -63,9 +82,128 @@ module.exports = __toCommonJS(index_exports);
|
|
|
63
82
|
// src/controller.ts
|
|
64
83
|
var import_react = require("react");
|
|
65
84
|
|
|
85
|
+
// src/context-window.ts
|
|
86
|
+
var SOURCE_PRIORITY = {
|
|
87
|
+
unknown: 0,
|
|
88
|
+
heuristic_estimate: 1,
|
|
89
|
+
tokenizer_estimate: 1,
|
|
90
|
+
text_estimate: 1,
|
|
91
|
+
estimated: 1,
|
|
92
|
+
multimodal_estimate: 2,
|
|
93
|
+
hf_tokenizer: 3,
|
|
94
|
+
runtime_tokenizer: 3,
|
|
95
|
+
provider_count_api: 4,
|
|
96
|
+
provider_usage: 5,
|
|
97
|
+
exact: 5
|
|
98
|
+
};
|
|
99
|
+
var STAGE_PRIORITY = {
|
|
100
|
+
preflight: 0,
|
|
101
|
+
sent_prompt: 1,
|
|
102
|
+
final_usage: 2
|
|
103
|
+
};
|
|
104
|
+
function numberOrNull(value) {
|
|
105
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
106
|
+
}
|
|
107
|
+
function stagePriority(window2) {
|
|
108
|
+
return STAGE_PRIORITY[window2?.stage || "sent_prompt"] ?? STAGE_PRIORITY.sent_prompt;
|
|
109
|
+
}
|
|
110
|
+
function hasRenderableContextWindow(window2) {
|
|
111
|
+
const maxTokens = window2?.max_tokens;
|
|
112
|
+
return typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0;
|
|
113
|
+
}
|
|
114
|
+
function windowWeight(window2) {
|
|
115
|
+
if (!window2) return [0, 0];
|
|
116
|
+
return [stagePriority(window2), SOURCE_PRIORITY[window2.source] || 0];
|
|
117
|
+
}
|
|
118
|
+
function normalizeContextCompression(value) {
|
|
119
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
120
|
+
const payload = value;
|
|
121
|
+
return {
|
|
122
|
+
active: Boolean(payload.active),
|
|
123
|
+
reason: typeof payload.reason === "string" ? payload.reason : null,
|
|
124
|
+
input_tokens: numberOrNull(payload.input_tokens),
|
|
125
|
+
max_tokens: numberOrNull(payload.max_tokens),
|
|
126
|
+
usage_ratio: numberOrNull(payload.usage_ratio),
|
|
127
|
+
full_frame_count: numberOrNull(payload.full_frame_count),
|
|
128
|
+
compact_frame_count: numberOrNull(payload.compact_frame_count),
|
|
129
|
+
dropped_frame_count: numberOrNull(payload.dropped_frame_count),
|
|
130
|
+
artifact_ref_count: numberOrNull(payload.artifact_ref_count),
|
|
131
|
+
compression_trigger_budget: numberOrNull(payload.compression_trigger_budget),
|
|
132
|
+
compression_target_budget: numberOrNull(payload.compression_target_budget),
|
|
133
|
+
compression_target_ratio: numberOrNull(payload.compression_target_ratio),
|
|
134
|
+
threshold_used: numberOrNull(payload.threshold_used)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function normalizeContextWindow(value) {
|
|
138
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
139
|
+
const payload = value;
|
|
140
|
+
const rawSource = String(payload.source || "").trim();
|
|
141
|
+
const source = rawSource === "exact" || rawSource === "estimated" || rawSource === "unknown" || rawSource === "provider_count_api" || rawSource === "provider_usage" || rawSource === "runtime_tokenizer" || rawSource === "hf_tokenizer" || rawSource === "multimodal_estimate" || rawSource === "text_estimate" || rawSource === "tokenizer_estimate" || rawSource === "heuristic_estimate" ? rawSource : rawSource ? "estimated" : "unknown";
|
|
142
|
+
const rawStage = String(payload.stage || "").trim();
|
|
143
|
+
const stage = rawStage === "preflight" || rawStage === "sent_prompt" || rawStage === "final_usage" ? rawStage : "sent_prompt";
|
|
144
|
+
const rawConfidence = String(payload.confidence || "").trim();
|
|
145
|
+
const confidence = rawConfidence === "exact" || rawConfidence === "high" || rawConfidence === "medium" || rawConfidence === "low" || rawConfidence === "unknown" ? rawConfidence : null;
|
|
146
|
+
return {
|
|
147
|
+
source,
|
|
148
|
+
run_id: typeof payload.run_id === "string" ? payload.run_id : null,
|
|
149
|
+
stage,
|
|
150
|
+
confidence,
|
|
151
|
+
counter: typeof payload.counter === "string" ? payload.counter : null,
|
|
152
|
+
model_id: typeof payload.model_id === "string" ? payload.model_id : null,
|
|
153
|
+
max_tokens: numberOrNull(payload.max_tokens),
|
|
154
|
+
max_tokens_source: typeof payload.max_tokens_source === "string" ? payload.max_tokens_source : null,
|
|
155
|
+
input_tokens: numberOrNull(payload.input_tokens),
|
|
156
|
+
remaining_tokens: numberOrNull(payload.remaining_tokens),
|
|
157
|
+
usage_ratio: numberOrNull(payload.usage_ratio),
|
|
158
|
+
assembly: payload.assembly && typeof payload.assembly === "object" && !Array.isArray(payload.assembly) ? payload.assembly : null,
|
|
159
|
+
context_compression: normalizeContextCompression(payload.context_compression)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function mergeContextWindow(current, incoming) {
|
|
163
|
+
if (!incoming) return current ?? null;
|
|
164
|
+
if (!current) return incoming;
|
|
165
|
+
if (hasRenderableContextWindow(current) && !hasRenderableContextWindow(incoming)) return current;
|
|
166
|
+
const currentRunId = current.run_id || null;
|
|
167
|
+
const incomingRunId = incoming.run_id || null;
|
|
168
|
+
if (incomingRunId && currentRunId && incomingRunId !== currentRunId) {
|
|
169
|
+
return stagePriority(incoming) >= STAGE_PRIORITY.sent_prompt ? incoming : current;
|
|
170
|
+
}
|
|
171
|
+
const currentWeight = windowWeight(current);
|
|
172
|
+
const incomingWeight = windowWeight(incoming);
|
|
173
|
+
return incomingWeight[0] > currentWeight[0] || incomingWeight[0] === currentWeight[0] && incomingWeight[1] >= currentWeight[1] ? incoming : current;
|
|
174
|
+
}
|
|
175
|
+
function mergeContextWindowUpdate(current, incoming) {
|
|
176
|
+
return mergeContextWindow(current, normalizeContextWindow(incoming));
|
|
177
|
+
}
|
|
178
|
+
function compressionFromContextWindow(contextWindow) {
|
|
179
|
+
if (!contextWindow) return null;
|
|
180
|
+
if (contextWindow.context_compression) return contextWindow.context_compression;
|
|
181
|
+
const assembly = contextWindow.assembly;
|
|
182
|
+
if (!assembly || typeof assembly !== "object" || Array.isArray(assembly)) return null;
|
|
183
|
+
const artifactRefs = Array.isArray(assembly.artifact_refs) ? assembly.artifact_refs : [];
|
|
184
|
+
const compactFrameCount = numberOrNull(assembly.compact_frame_count) ?? 0;
|
|
185
|
+
const droppedFrameCount = numberOrNull(assembly.dropped_frame_count) ?? 0;
|
|
186
|
+
const reason = typeof assembly.compaction_reason === "string" ? assembly.compaction_reason : "none";
|
|
187
|
+
return {
|
|
188
|
+
active: reason !== "none" || compactFrameCount > 0 || droppedFrameCount > 0,
|
|
189
|
+
reason,
|
|
190
|
+
input_tokens: contextWindow.input_tokens ?? null,
|
|
191
|
+
max_tokens: contextWindow.max_tokens ?? null,
|
|
192
|
+
usage_ratio: contextWindow.usage_ratio ?? null,
|
|
193
|
+
full_frame_count: numberOrNull(assembly.full_frame_count),
|
|
194
|
+
compact_frame_count: compactFrameCount,
|
|
195
|
+
dropped_frame_count: droppedFrameCount,
|
|
196
|
+
artifact_ref_count: artifactRefs.length,
|
|
197
|
+
compression_trigger_budget: numberOrNull(assembly.compression_trigger_budget),
|
|
198
|
+
compression_target_budget: numberOrNull(assembly.compression_target_budget),
|
|
199
|
+
compression_target_ratio: numberOrNull(assembly.compression_target_ratio),
|
|
200
|
+
threshold_used: numberOrNull(assembly.threshold_used)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
66
204
|
// src/model.ts
|
|
67
205
|
var DEFAULT_THREAD_PAGE_SIZE = 5;
|
|
68
|
-
var isRunningThreadStatus = (status) => (/* @__PURE__ */ new Set(["queued", "running"])).has(String(status || "").toLowerCase());
|
|
206
|
+
var isRunningThreadStatus = (status) => (/* @__PURE__ */ new Set(["queued", "running", "cancelling"])).has(String(status || "").toLowerCase());
|
|
69
207
|
var createChatId = () => {
|
|
70
208
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
71
209
|
return `chat-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -104,6 +242,16 @@ var attachmentsFromTurn = (turn) => (turn.attachments || []).map((attachment, in
|
|
|
104
242
|
mediaType: String(record.mime_type || record.mediaType || record.type || "application/octet-stream")
|
|
105
243
|
};
|
|
106
244
|
});
|
|
245
|
+
var tokenUsageFromTurn = (turn) => {
|
|
246
|
+
const usage = turn.run_usage;
|
|
247
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
248
|
+
return {
|
|
249
|
+
inputTokens: usage.input_tokens ?? null,
|
|
250
|
+
outputTokens: usage.output_tokens ?? null,
|
|
251
|
+
totalTokens: usage.total_tokens ?? null,
|
|
252
|
+
usageSource: usage.source ?? null
|
|
253
|
+
};
|
|
254
|
+
};
|
|
107
255
|
var latestEventPayloadValue = (events, key) => {
|
|
108
256
|
for (let index = (events || []).length - 1; index >= 0; index -= 1) {
|
|
109
257
|
const payload = events?.[index]?.payload;
|
|
@@ -305,7 +453,8 @@ var turnToMessages = (turn, activeRunId) => {
|
|
|
305
453
|
createdAt: turn.completed_at ? new Date(turn.completed_at) : createdAt,
|
|
306
454
|
parts,
|
|
307
455
|
reasoningSteps: mergeReasoningSteps(reasoningStepsFromParts(parts), { finalize: !isRunning }),
|
|
308
|
-
isFinal: !isRunning
|
|
456
|
+
isFinal: !isRunning,
|
|
457
|
+
tokenUsage: tokenUsageFromTurn(turn)
|
|
309
458
|
});
|
|
310
459
|
}
|
|
311
460
|
return messages;
|
|
@@ -322,10 +471,11 @@ var threadPaging = (thread) => {
|
|
|
322
471
|
};
|
|
323
472
|
};
|
|
324
473
|
var latestContextWindowFromThread = (thread) => {
|
|
325
|
-
|
|
474
|
+
const threadContextWindow = normalizeContextWindow(thread.context_window);
|
|
475
|
+
if (threadContextWindow) return threadContextWindow;
|
|
326
476
|
for (let index = (thread.turns || []).length - 1; index >= 0; index -= 1) {
|
|
327
|
-
const
|
|
328
|
-
if (
|
|
477
|
+
const contextWindow = normalizeContextWindow(thread.turns?.[index]?.context_window);
|
|
478
|
+
if (contextWindow) return contextWindow;
|
|
329
479
|
}
|
|
330
480
|
return null;
|
|
331
481
|
};
|
|
@@ -356,10 +506,6 @@ var activeRunIdFromThreadDetail = (thread) => {
|
|
|
356
506
|
};
|
|
357
507
|
|
|
358
508
|
// src/controller.ts
|
|
359
|
-
var mergeContextWindow = (current, incoming) => {
|
|
360
|
-
if (!incoming || typeof incoming !== "object") return current;
|
|
361
|
-
return { ...current || {}, ...incoming };
|
|
362
|
-
};
|
|
363
509
|
var threadSummaryToStored = (thread) => ({
|
|
364
510
|
...thread,
|
|
365
511
|
id: String(thread.id),
|
|
@@ -377,7 +523,9 @@ function useAgents24ChatController({
|
|
|
377
523
|
createId = createChatId,
|
|
378
524
|
onActiveThreadIdChange,
|
|
379
525
|
onSourceClick,
|
|
380
|
-
onStreamErrorMessage
|
|
526
|
+
onStreamErrorMessage,
|
|
527
|
+
onRuntimeEvent,
|
|
528
|
+
onThreadDetailLoaded
|
|
381
529
|
}) {
|
|
382
530
|
const activeThreadId = controlledActiveThreadId ?? storage.getActiveThreadId?.() ?? null;
|
|
383
531
|
const initialCached = activeThreadId ? storage.getThread(activeThreadId) : void 0;
|
|
@@ -394,6 +542,9 @@ function useAgents24ChatController({
|
|
|
394
542
|
const [disliked, setDisliked] = (0, import_react.useState)({});
|
|
395
543
|
const [copiedMessageId, setCopiedMessageId] = (0, import_react.useState)(null);
|
|
396
544
|
const [lastThinkingDurationMs, setLastThinkingDurationMs] = (0, import_react.useState)(null);
|
|
545
|
+
const [threads, setThreads] = (0, import_react.useState)(() => storage.listThreads());
|
|
546
|
+
const [isRefreshingThreads, setIsRefreshingThreads] = (0, import_react.useState)(false);
|
|
547
|
+
const [activeRunId, setActiveRunId] = (0, import_react.useState)(null);
|
|
397
548
|
const textareaRef = (0, import_react.useRef)(null);
|
|
398
549
|
const activeThreadIdRef = (0, import_react.useRef)(activeThreadId);
|
|
399
550
|
const messagesRef = (0, import_react.useRef)(messages);
|
|
@@ -410,6 +561,20 @@ function useAgents24ChatController({
|
|
|
410
561
|
const streamingMessageIdRef = (0, import_react.useRef)(null);
|
|
411
562
|
const reasoningRef = (0, import_react.useRef)([]);
|
|
412
563
|
const liveVoiceIdsRef = (0, import_react.useRef)({});
|
|
564
|
+
const setActiveRunIdValue = (0, import_react.useCallback)((runId) => {
|
|
565
|
+
activeRunIdRef.current = runId;
|
|
566
|
+
setActiveRunId(runId);
|
|
567
|
+
}, []);
|
|
568
|
+
const syncThreadsFromStorage = (0, import_react.useCallback)(() => {
|
|
569
|
+
setThreads(storage.listThreads());
|
|
570
|
+
}, [storage]);
|
|
571
|
+
const upsertStoredThread = (0, import_react.useCallback)(
|
|
572
|
+
(thread) => {
|
|
573
|
+
storage.upsertThread(thread);
|
|
574
|
+
syncThreadsFromStorage();
|
|
575
|
+
},
|
|
576
|
+
[storage, syncThreadsFromStorage]
|
|
577
|
+
);
|
|
413
578
|
(0, import_react.useEffect)(() => {
|
|
414
579
|
messagesRef.current = messages;
|
|
415
580
|
}, [messages]);
|
|
@@ -417,7 +582,7 @@ function useAgents24ChatController({
|
|
|
417
582
|
(threadId, nextMessages, paging, options) => {
|
|
418
583
|
const existing = storage.getThread(threadId);
|
|
419
584
|
const firstUser = nextMessages.find((message) => message.role === "user");
|
|
420
|
-
|
|
585
|
+
upsertStoredThread({
|
|
421
586
|
...existing || {},
|
|
422
587
|
id: threadId,
|
|
423
588
|
title: existing?.title || (firstUser ? titleFromMessage(firstUser.content, firstUser.attachments || []) : "New chat"),
|
|
@@ -428,13 +593,13 @@ function useAgents24ChatController({
|
|
|
428
593
|
nextBeforeTurnIndex: paging?.nextBeforeTurnIndex ?? nextBeforeTurnIndexRef.current
|
|
429
594
|
});
|
|
430
595
|
},
|
|
431
|
-
[storage]
|
|
596
|
+
[storage, upsertStoredThread]
|
|
432
597
|
);
|
|
433
598
|
const markThreadRunStatus = (0, import_react.useCallback)(
|
|
434
599
|
(threadId, runId, status, lastEventSeq) => {
|
|
435
600
|
const existing = storage.getThread(threadId);
|
|
436
601
|
if (!existing || !runId) return;
|
|
437
|
-
|
|
602
|
+
upsertStoredThread({
|
|
438
603
|
...existing,
|
|
439
604
|
last_run_id: runId,
|
|
440
605
|
last_run_status: status,
|
|
@@ -451,14 +616,25 @@ function useAgents24ChatController({
|
|
|
451
616
|
created_at: existing.activeRun?.created_at ?? existing.active_run?.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
452
617
|
},
|
|
453
618
|
lastEventSeq: typeof lastEventSeq === "number" ? lastEventSeq : existing.lastEventSeq ?? null,
|
|
454
|
-
isRunning:
|
|
619
|
+
isRunning: activeRunIdFromThread({
|
|
620
|
+
lastRunId: runId,
|
|
621
|
+
lastRunStatus: status,
|
|
622
|
+
activeRun: { run_id: runId, status }
|
|
623
|
+
}) !== null
|
|
455
624
|
});
|
|
456
625
|
},
|
|
457
|
-
[storage]
|
|
626
|
+
[storage, upsertStoredThread]
|
|
458
627
|
);
|
|
459
628
|
const refresh = (0, import_react.useCallback)(async () => {
|
|
460
|
-
|
|
461
|
-
|
|
629
|
+
setIsRefreshingThreads(true);
|
|
630
|
+
try {
|
|
631
|
+
const data = await transport.listThreads();
|
|
632
|
+
const nextThreads = (data.items || []).map((item) => threadSummaryToStored(item));
|
|
633
|
+
storage.setThreads(nextThreads);
|
|
634
|
+
setThreads(storage.listThreads());
|
|
635
|
+
} finally {
|
|
636
|
+
setIsRefreshingThreads(false);
|
|
637
|
+
}
|
|
462
638
|
}, [storage, transport]);
|
|
463
639
|
const applyThreadId = (0, import_react.useCallback)(
|
|
464
640
|
(threadId, baseMessages) => {
|
|
@@ -486,7 +662,7 @@ function useAgents24ChatController({
|
|
|
486
662
|
const controller = abortControllerRef.current;
|
|
487
663
|
abortControllerRef.current = null;
|
|
488
664
|
controller?.abort();
|
|
489
|
-
|
|
665
|
+
setActiveRunIdValue(null);
|
|
490
666
|
reattachedRunIdRef.current = null;
|
|
491
667
|
streamingMessageIdRef.current = null;
|
|
492
668
|
streamingContentRef.current = "";
|
|
@@ -495,7 +671,7 @@ function useAgents24ChatController({
|
|
|
495
671
|
setIsLoading(false);
|
|
496
672
|
setStreamingContent("");
|
|
497
673
|
setCurrentReasoning([]);
|
|
498
|
-
}, []);
|
|
674
|
+
}, [setActiveRunIdValue]);
|
|
499
675
|
const setLiveAssistantMessage = (0, import_react.useCallback)(
|
|
500
676
|
(input) => {
|
|
501
677
|
setMessages((prev) => {
|
|
@@ -565,7 +741,7 @@ function useAgents24ChatController({
|
|
|
565
741
|
persistThread(input.threadId, completed);
|
|
566
742
|
const existingThread = storage.getThread(input.threadId);
|
|
567
743
|
if (existingThread && input.runId) {
|
|
568
|
-
|
|
744
|
+
upsertStoredThread({
|
|
569
745
|
...existingThread,
|
|
570
746
|
messages: completed,
|
|
571
747
|
last_run_id: input.runId,
|
|
@@ -580,7 +756,7 @@ function useAgents24ChatController({
|
|
|
580
756
|
}
|
|
581
757
|
return completed;
|
|
582
758
|
},
|
|
583
|
-
[createId, persistThread, storage]
|
|
759
|
+
[createId, persistThread, storage, upsertStoredThread]
|
|
584
760
|
);
|
|
585
761
|
const loadThread = (0, import_react.useCallback)(
|
|
586
762
|
async (threadId) => {
|
|
@@ -595,6 +771,7 @@ function useAgents24ChatController({
|
|
|
595
771
|
includeRunEvents: false
|
|
596
772
|
});
|
|
597
773
|
if (activeThreadIdRef.current !== threadId || seq !== requestSeqRef.current) return;
|
|
774
|
+
void onThreadDetailLoaded?.(detail);
|
|
598
775
|
const nextMessages = threadDetailToMessages(detail);
|
|
599
776
|
const paging = threadPaging(detail);
|
|
600
777
|
setContextStatus(latestContextWindowFromThread(detail));
|
|
@@ -604,7 +781,7 @@ function useAgents24ChatController({
|
|
|
604
781
|
hasOlderTurnsRef.current = paging.hasOlderTurns;
|
|
605
782
|
nextBeforeTurnIndexRef.current = paging.nextBeforeTurnIndex;
|
|
606
783
|
loadedThreadIdRef.current = threadId;
|
|
607
|
-
|
|
784
|
+
upsertStoredThread({
|
|
608
785
|
...threadSummaryToStored(detail),
|
|
609
786
|
messages: nextMessages,
|
|
610
787
|
isHydrated: true,
|
|
@@ -616,7 +793,7 @@ function useAgents24ChatController({
|
|
|
616
793
|
if (activeThreadIdRef.current === threadId) setLoadingHistory(false);
|
|
617
794
|
}
|
|
618
795
|
},
|
|
619
|
-
[pageSize, setLoadingHistory,
|
|
796
|
+
[onThreadDetailLoaded, pageSize, setLoadingHistory, transport, upsertStoredThread]
|
|
620
797
|
);
|
|
621
798
|
const loadOlderTurns = (0, import_react.useCallback)(async () => {
|
|
622
799
|
const threadId = activeThreadIdRef.current;
|
|
@@ -648,11 +825,18 @@ function useAgents24ChatController({
|
|
|
648
825
|
}, [pageSize, persistThread, transport]);
|
|
649
826
|
const handleStreamEvent = (0, import_react.useCallback)(
|
|
650
827
|
(input) => {
|
|
651
|
-
const { event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
828
|
+
const { mode, event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages } = input;
|
|
652
829
|
const payload = event.payload || {};
|
|
653
830
|
const responseBlocks = Array.isArray(payload.response_blocks) ? payload.response_blocks : null;
|
|
654
|
-
if (event.run_id)
|
|
655
|
-
|
|
831
|
+
if (event.run_id) setActiveRunIdValue(event.run_id);
|
|
832
|
+
void onRuntimeEvent?.(event, {
|
|
833
|
+
mode,
|
|
834
|
+
assistantMessageId,
|
|
835
|
+
threadId: streamThreadIdRef.current,
|
|
836
|
+
runId: event.run_id || activeRunIdRef.current,
|
|
837
|
+
startedAt
|
|
838
|
+
});
|
|
839
|
+
setContextStatus((current) => mergeContextWindowUpdate(current, payload.context_window));
|
|
656
840
|
if (responseBlocks) {
|
|
657
841
|
const blockText = String(payload.assistant_output_text || "") || assistantTextFromResponseBlocks(responseBlocks) || streamingContentRef.current;
|
|
658
842
|
if (blockText) setStreamingText(blockText);
|
|
@@ -698,7 +882,7 @@ function useAgents24ChatController({
|
|
|
698
882
|
error: isFailed ? String(payload.message || payload.error || event.diagnostics?.[0]?.message || onStreamErrorMessage?.(event) || "The chat run failed.") : void 0
|
|
699
883
|
});
|
|
700
884
|
},
|
|
701
|
-
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onStreamErrorMessage, setLiveAssistantMessage, setReasoningSteps]
|
|
885
|
+
[applyThreadId, finalizeAssistantMessage, markThreadRunStatus, onRuntimeEvent, onStreamErrorMessage, setActiveRunIdValue, setLiveAssistantMessage, setReasoningSteps]
|
|
702
886
|
);
|
|
703
887
|
const runStream = (0, import_react.useCallback)(
|
|
704
888
|
async (input) => {
|
|
@@ -760,16 +944,16 @@ function useAgents24ChatController({
|
|
|
760
944
|
}
|
|
761
945
|
try {
|
|
762
946
|
if (input.mode === "attach") {
|
|
763
|
-
|
|
947
|
+
setActiveRunIdValue(input.runId);
|
|
764
948
|
reattachedRunIdRef.current = input.runId;
|
|
765
949
|
await transport.attachRun(
|
|
766
950
|
{ runId: input.runId, signal: controller.signal },
|
|
767
|
-
(event) => handleStreamEvent({ event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
951
|
+
(event) => handleStreamEvent({ mode: "attach", event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
768
952
|
);
|
|
769
953
|
} else {
|
|
770
954
|
await transport.streamMessage(
|
|
771
|
-
{
|
|
772
|
-
(event) => handleStreamEvent({ event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
955
|
+
{ ...input.message, files: input.message.files || [], threadId: activeThreadIdRef.current, signal: controller.signal },
|
|
956
|
+
(event) => handleStreamEvent({ mode: "submit", event, assistantMessageId, startedAt, streamThreadIdRef, baseMessages })
|
|
773
957
|
);
|
|
774
958
|
}
|
|
775
959
|
if (streamingContentRef.current.trim() && streamingMessageIdRef.current) {
|
|
@@ -801,7 +985,7 @@ function useAgents24ChatController({
|
|
|
801
985
|
const isCurrentStream = abortControllerRef.current === controller || streamingMessageIdRef.current === assistantMessageId;
|
|
802
986
|
if (abortControllerRef.current === controller) abortControllerRef.current = null;
|
|
803
987
|
if (isCurrentStream) {
|
|
804
|
-
|
|
988
|
+
setActiveRunIdValue(null);
|
|
805
989
|
streamingMessageIdRef.current = null;
|
|
806
990
|
reattachedRunIdRef.current = null;
|
|
807
991
|
setStreamingMessageId(null);
|
|
@@ -812,7 +996,7 @@ function useAgents24ChatController({
|
|
|
812
996
|
}
|
|
813
997
|
}
|
|
814
998
|
},
|
|
815
|
-
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setReasoningSteps, transport]
|
|
999
|
+
[createId, finalizeAssistantMessage, handleStreamEvent, onStreamErrorMessage, persistThread, refresh, setActiveRunIdValue, setReasoningSteps, transport]
|
|
816
1000
|
);
|
|
817
1001
|
const handleSubmit = (0, import_react.useCallback)(
|
|
818
1002
|
async (message) => {
|
|
@@ -827,7 +1011,7 @@ function useAgents24ChatController({
|
|
|
827
1011
|
const liveMessageId = streamingMessageIdRef.current;
|
|
828
1012
|
abortControllerRef.current?.abort();
|
|
829
1013
|
abortControllerRef.current = null;
|
|
830
|
-
|
|
1014
|
+
setActiveRunIdValue(null);
|
|
831
1015
|
streamingMessageIdRef.current = null;
|
|
832
1016
|
setStreamingMessageId(null);
|
|
833
1017
|
setIsLoading(false);
|
|
@@ -844,10 +1028,16 @@ function useAgents24ChatController({
|
|
|
844
1028
|
messageId: liveMessageId
|
|
845
1029
|
});
|
|
846
1030
|
}
|
|
847
|
-
}, [finalizeAssistantMessage, lastThinkingDurationMs, setReasoningSteps, transport]);
|
|
1031
|
+
}, [finalizeAssistantMessage, lastThinkingDurationMs, setActiveRunIdValue, setReasoningSteps, transport]);
|
|
848
1032
|
(0, import_react.useEffect)(() => {
|
|
849
|
-
refresh().catch(() =>
|
|
1033
|
+
refresh().catch(() => {
|
|
1034
|
+
storage.setThreads([]);
|
|
1035
|
+
setThreads([]);
|
|
1036
|
+
});
|
|
850
1037
|
}, [refresh, storage]);
|
|
1038
|
+
(0, import_react.useEffect)(() => {
|
|
1039
|
+
syncThreadsFromStorage();
|
|
1040
|
+
}, [storageKey, syncThreadsFromStorage]);
|
|
851
1041
|
(0, import_react.useEffect)(() => {
|
|
852
1042
|
const previous = activeThreadIdRef.current;
|
|
853
1043
|
activeThreadIdRef.current = activeThreadId;
|
|
@@ -955,7 +1145,40 @@ function useAgents24ChatController({
|
|
|
955
1145
|
return next;
|
|
956
1146
|
});
|
|
957
1147
|
}, [createId, persistThread]);
|
|
1148
|
+
const startNewThread = (0, import_react.useCallback)(() => {
|
|
1149
|
+
detachActiveStream();
|
|
1150
|
+
requestSeqRef.current += 1;
|
|
1151
|
+
activeThreadIdRef.current = null;
|
|
1152
|
+
loadedThreadIdRef.current = null;
|
|
1153
|
+
nextBeforeTurnIndexRef.current = null;
|
|
1154
|
+
hasOlderTurnsRef.current = false;
|
|
1155
|
+
storage.setActiveThreadId?.(null);
|
|
1156
|
+
onActiveThreadIdChange?.(null);
|
|
1157
|
+
setMessages([]);
|
|
1158
|
+
messagesRef.current = [];
|
|
1159
|
+
setHasOlderTurns(false);
|
|
1160
|
+
setIsLoadingOlder(false);
|
|
1161
|
+
setContextStatus(null);
|
|
1162
|
+
setLoadingHistory(false);
|
|
1163
|
+
}, [detachActiveStream, onActiveThreadIdChange, setLoadingHistory, storage]);
|
|
1164
|
+
const loadThreadById = (0, import_react.useCallback)(
|
|
1165
|
+
async (threadId) => {
|
|
1166
|
+
if (!threadId) return;
|
|
1167
|
+
if (activeThreadIdRef.current !== threadId) {
|
|
1168
|
+
if (abortControllerRef.current) detachActiveStream();
|
|
1169
|
+
activeThreadIdRef.current = threadId;
|
|
1170
|
+
storage.setActiveThreadId?.(threadId);
|
|
1171
|
+
onActiveThreadIdChange?.(threadId);
|
|
1172
|
+
}
|
|
1173
|
+
await loadThread(threadId);
|
|
1174
|
+
},
|
|
1175
|
+
[detachActiveStream, loadThread, onActiveThreadIdChange, storage]
|
|
1176
|
+
);
|
|
1177
|
+
const activeThread = activeThreadId ? storage.getThread(activeThreadId) || threads.find((thread) => thread.id === activeThreadId) || null : null;
|
|
958
1178
|
return (0, import_react.useMemo)(() => ({
|
|
1179
|
+
threads,
|
|
1180
|
+
activeThreadId,
|
|
1181
|
+
activeThread,
|
|
959
1182
|
messages,
|
|
960
1183
|
streamingContent,
|
|
961
1184
|
streamingMessageId,
|
|
@@ -964,12 +1187,13 @@ function useAgents24ChatController({
|
|
|
964
1187
|
isLoading,
|
|
965
1188
|
isLoadingHistory,
|
|
966
1189
|
isLoadingOlder,
|
|
1190
|
+
isRefreshingThreads,
|
|
967
1191
|
hasOlderTurns,
|
|
968
1192
|
liked,
|
|
969
1193
|
disliked,
|
|
970
1194
|
copiedMessageId,
|
|
971
1195
|
lastThinkingDurationMs,
|
|
972
|
-
activeRunId
|
|
1196
|
+
activeRunId,
|
|
973
1197
|
handleSubmit,
|
|
974
1198
|
handleStop,
|
|
975
1199
|
handleCopy,
|
|
@@ -978,11 +1202,16 @@ function useAgents24ChatController({
|
|
|
978
1202
|
handleRetry,
|
|
979
1203
|
handleSourceClick: (citations) => onSourceClick?.(citations),
|
|
980
1204
|
upsertLiveVoiceMessage,
|
|
1205
|
+
startNewThread,
|
|
1206
|
+
loadThreadById,
|
|
981
1207
|
loadOlderTurns,
|
|
982
1208
|
refresh,
|
|
983
1209
|
textareaRef
|
|
984
1210
|
}), [
|
|
985
1211
|
copiedMessageId,
|
|
1212
|
+
activeRunId,
|
|
1213
|
+
activeThread,
|
|
1214
|
+
activeThreadId,
|
|
986
1215
|
contextStatus,
|
|
987
1216
|
currentReasoning,
|
|
988
1217
|
disliked,
|
|
@@ -996,26 +1225,373 @@ function useAgents24ChatController({
|
|
|
996
1225
|
isLoading,
|
|
997
1226
|
isLoadingHistory,
|
|
998
1227
|
isLoadingOlder,
|
|
1228
|
+
isRefreshingThreads,
|
|
999
1229
|
lastThinkingDurationMs,
|
|
1000
1230
|
liked,
|
|
1231
|
+
loadThreadById,
|
|
1001
1232
|
loadOlderTurns,
|
|
1002
1233
|
messages,
|
|
1003
1234
|
onSourceClick,
|
|
1004
1235
|
refresh,
|
|
1005
1236
|
streamingContent,
|
|
1006
1237
|
streamingMessageId,
|
|
1238
|
+
startNewThread,
|
|
1239
|
+
threads,
|
|
1007
1240
|
upsertLiveVoiceMessage
|
|
1008
1241
|
]);
|
|
1009
1242
|
}
|
|
1010
1243
|
|
|
1011
|
-
// src/
|
|
1244
|
+
// src/latest-thread-scroller.tsx
|
|
1245
|
+
var React = __toESM(require("react"), 1);
|
|
1246
|
+
var import_message_scroller = require("@shadcn/react/message-scroller");
|
|
1012
1247
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1248
|
+
var cx = (...values) => values.filter(Boolean).join(" ");
|
|
1249
|
+
var LatestThreadScrollerLayoutContext = React.createContext(null);
|
|
1250
|
+
var useLatestThreadScrollerLayout = () => React.useContext(LatestThreadScrollerLayoutContext);
|
|
1251
|
+
function LatestThreadScrollerProvider({
|
|
1252
|
+
autoScroll = true,
|
|
1253
|
+
defaultScrollPosition = "last-anchor",
|
|
1254
|
+
scrollPreviousItemPeek = 64,
|
|
1255
|
+
...props
|
|
1256
|
+
}) {
|
|
1257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1258
|
+
import_message_scroller.MessageScroller.Provider,
|
|
1259
|
+
{
|
|
1260
|
+
autoScroll,
|
|
1261
|
+
defaultScrollPosition,
|
|
1262
|
+
scrollPreviousItemPeek,
|
|
1263
|
+
...props
|
|
1264
|
+
}
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
function LatestThreadScrollerRoot({
|
|
1268
|
+
className,
|
|
1269
|
+
ref,
|
|
1270
|
+
...props
|
|
1271
|
+
}) {
|
|
1272
|
+
const [rootElement, setRootElement] = React.useState(null);
|
|
1273
|
+
const [contentElement, setContentElement] = React.useState(null);
|
|
1274
|
+
const layoutContext = React.useMemo(
|
|
1275
|
+
() => ({
|
|
1276
|
+
contentElement,
|
|
1277
|
+
rootElement,
|
|
1278
|
+
setContentElement,
|
|
1279
|
+
setRootElement
|
|
1280
|
+
}),
|
|
1281
|
+
[contentElement, rootElement]
|
|
1282
|
+
);
|
|
1283
|
+
const setRef = React.useCallback(
|
|
1284
|
+
(node) => {
|
|
1285
|
+
setRootElement(node);
|
|
1286
|
+
if (typeof ref === "function") {
|
|
1287
|
+
ref(node);
|
|
1288
|
+
} else if (ref) {
|
|
1289
|
+
ref.current = node;
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
[ref]
|
|
1293
|
+
);
|
|
1294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LatestThreadScrollerLayoutContext.Provider, { value: layoutContext, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1295
|
+
import_message_scroller.MessageScroller.Root,
|
|
1296
|
+
{
|
|
1297
|
+
ref: setRef,
|
|
1298
|
+
className: cx(
|
|
1299
|
+
"relative flex size-full min-h-0 flex-1 flex-col overflow-hidden",
|
|
1300
|
+
className
|
|
1301
|
+
),
|
|
1302
|
+
...props
|
|
1303
|
+
}
|
|
1304
|
+
) });
|
|
1305
|
+
}
|
|
1306
|
+
function LatestThreadScrollerViewport({
|
|
1307
|
+
className,
|
|
1308
|
+
onViewportChange,
|
|
1309
|
+
preserveScrollOnPrepend = true,
|
|
1310
|
+
ref,
|
|
1311
|
+
...props
|
|
1312
|
+
}) {
|
|
1313
|
+
const setRef = React.useCallback(
|
|
1314
|
+
(node) => {
|
|
1315
|
+
if (typeof ref === "function") {
|
|
1316
|
+
ref(node);
|
|
1317
|
+
} else if (ref) {
|
|
1318
|
+
ref.current = node;
|
|
1319
|
+
}
|
|
1320
|
+
onViewportChange?.(node);
|
|
1321
|
+
},
|
|
1322
|
+
[onViewportChange, ref]
|
|
1323
|
+
);
|
|
1324
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1325
|
+
import_message_scroller.MessageScroller.Viewport,
|
|
1326
|
+
{
|
|
1327
|
+
ref: setRef,
|
|
1328
|
+
className: cx(
|
|
1329
|
+
"size-full min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain",
|
|
1330
|
+
className
|
|
1331
|
+
),
|
|
1332
|
+
preserveScrollOnPrepend,
|
|
1333
|
+
...props
|
|
1334
|
+
}
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
function LatestThreadScrollerContent({
|
|
1338
|
+
className,
|
|
1339
|
+
ref,
|
|
1340
|
+
...props
|
|
1341
|
+
}) {
|
|
1342
|
+
const layout = useLatestThreadScrollerLayout();
|
|
1343
|
+
const setRef = React.useCallback(
|
|
1344
|
+
(node) => {
|
|
1345
|
+
layout?.setContentElement(node);
|
|
1346
|
+
if (typeof ref === "function") {
|
|
1347
|
+
ref(node);
|
|
1348
|
+
} else if (ref) {
|
|
1349
|
+
ref.current = node;
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
[layout, ref]
|
|
1353
|
+
);
|
|
1354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1355
|
+
import_message_scroller.MessageScroller.Content,
|
|
1356
|
+
{
|
|
1357
|
+
ref: setRef,
|
|
1358
|
+
"data-agents24-latest-thread-content": "",
|
|
1359
|
+
className: cx("flex h-max min-h-full flex-col gap-8 p-4", className),
|
|
1360
|
+
...props
|
|
1361
|
+
}
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
function LatestThreadScrollerItem({
|
|
1365
|
+
className,
|
|
1366
|
+
messageId,
|
|
1367
|
+
...props
|
|
1368
|
+
}) {
|
|
1369
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1370
|
+
import_message_scroller.MessageScroller.Item,
|
|
1371
|
+
{
|
|
1372
|
+
className: cx(
|
|
1373
|
+
"min-w-0 shrink-0 overflow-visible",
|
|
1374
|
+
className
|
|
1375
|
+
),
|
|
1376
|
+
messageId,
|
|
1377
|
+
...props
|
|
1378
|
+
}
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
function LatestThreadScrollerButton({
|
|
1382
|
+
children,
|
|
1383
|
+
className,
|
|
1384
|
+
direction = "end",
|
|
1385
|
+
render,
|
|
1386
|
+
...props
|
|
1387
|
+
}) {
|
|
1388
|
+
if (render) {
|
|
1389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1390
|
+
import_message_scroller.MessageScroller.Button,
|
|
1391
|
+
{
|
|
1392
|
+
direction,
|
|
1393
|
+
render,
|
|
1394
|
+
...props
|
|
1395
|
+
}
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1399
|
+
import_message_scroller.MessageScroller.Button,
|
|
1400
|
+
{
|
|
1401
|
+
direction,
|
|
1402
|
+
render: (buttonProps) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1403
|
+
"button",
|
|
1404
|
+
{
|
|
1405
|
+
...buttonProps,
|
|
1406
|
+
"aria-label": direction === "end" ? "Scroll to latest message" : "Scroll to first message",
|
|
1407
|
+
className: cx(
|
|
1408
|
+
"absolute bottom-4 left-1/2 z-50 flex size-9 -translate-x-1/2 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-md transition-all duration-200 data-[active=false]:pointer-events-none data-[active=false]:translate-y-2 data-[active=false]:opacity-0 data-[active=true]:translate-y-0 data-[active=true]:opacity-100",
|
|
1409
|
+
className
|
|
1410
|
+
),
|
|
1411
|
+
type: "button",
|
|
1412
|
+
children: children ?? (direction === "end" ? "\u2193" : "\u2191")
|
|
1413
|
+
}
|
|
1414
|
+
),
|
|
1415
|
+
...props
|
|
1416
|
+
}
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
function LatestThreadScrollerOutline({
|
|
1420
|
+
cardClassName,
|
|
1421
|
+
className,
|
|
1422
|
+
contentMaxWidth = 768,
|
|
1423
|
+
dir = "ltr",
|
|
1424
|
+
items,
|
|
1425
|
+
minContainerWidthForRail = 0,
|
|
1426
|
+
minSideGutterForRail = 48,
|
|
1427
|
+
side
|
|
1428
|
+
}) {
|
|
1429
|
+
const navRef = React.useRef(null);
|
|
1430
|
+
const layout = useLatestThreadScrollerLayout();
|
|
1431
|
+
const [hasSideRoom, setHasSideRoom] = React.useState(false);
|
|
1432
|
+
const [hoveredIndex, setHoveredIndex] = React.useState(null);
|
|
1433
|
+
const { scrollToMessage } = (0, import_message_scroller.useMessageScroller)();
|
|
1434
|
+
const { currentAnchorId, visibleMessageIds } = (0, import_message_scroller.useMessageScrollerVisibility)();
|
|
1435
|
+
const visibleMessageIdSet = React.useMemo(
|
|
1436
|
+
() => new Set(visibleMessageIds),
|
|
1437
|
+
[visibleMessageIds]
|
|
1438
|
+
);
|
|
1439
|
+
const focusIndex = hoveredIndex ?? -1;
|
|
1440
|
+
const railSide = side ?? (dir === "rtl" ? "right" : "left");
|
|
1441
|
+
React.useEffect(() => {
|
|
1442
|
+
const root = layout?.rootElement ?? navRef.current?.parentElement;
|
|
1443
|
+
if (!root) return;
|
|
1444
|
+
const updateSideRoom = () => {
|
|
1445
|
+
const rootRect = root.getBoundingClientRect();
|
|
1446
|
+
const rootWidth = rootRect.width;
|
|
1447
|
+
const content2 = layout?.contentElement;
|
|
1448
|
+
const sideGutter = content2 ? Math.max(
|
|
1449
|
+
0,
|
|
1450
|
+
railSide === "left" ? content2.getBoundingClientRect().left - rootRect.left : rootRect.right - content2.getBoundingClientRect().right
|
|
1451
|
+
) : Math.max(0, (rootWidth - contentMaxWidth) / 2);
|
|
1452
|
+
const nextHasSideRoom = rootWidth >= minContainerWidthForRail && sideGutter >= minSideGutterForRail;
|
|
1453
|
+
setHasSideRoom(
|
|
1454
|
+
(current) => current === nextHasSideRoom ? current : nextHasSideRoom
|
|
1455
|
+
);
|
|
1456
|
+
};
|
|
1457
|
+
updateSideRoom();
|
|
1458
|
+
const resizeObserver = new ResizeObserver(updateSideRoom);
|
|
1459
|
+
resizeObserver.observe(root);
|
|
1460
|
+
const content = layout?.contentElement;
|
|
1461
|
+
if (content) {
|
|
1462
|
+
resizeObserver.observe(content);
|
|
1463
|
+
}
|
|
1464
|
+
return () => resizeObserver.disconnect();
|
|
1465
|
+
}, [
|
|
1466
|
+
contentMaxWidth,
|
|
1467
|
+
layout?.contentElement,
|
|
1468
|
+
layout?.rootElement,
|
|
1469
|
+
minContainerWidthForRail,
|
|
1470
|
+
minSideGutterForRail,
|
|
1471
|
+
railSide
|
|
1472
|
+
]);
|
|
1473
|
+
if (items.length < 2) {
|
|
1474
|
+
return null;
|
|
1475
|
+
}
|
|
1476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1477
|
+
"nav",
|
|
1478
|
+
{
|
|
1479
|
+
ref: navRef,
|
|
1480
|
+
"aria-label": "Transcript outline",
|
|
1481
|
+
className: cx(
|
|
1482
|
+
"pointer-events-none absolute top-[46%] z-10 max-h-[min(18rem,calc(100%-10rem))] w-16 -translate-y-1/2 overflow-visible",
|
|
1483
|
+
hasSideRoom ? "hidden md:block" : "hidden",
|
|
1484
|
+
railSide === "left" ? "left-1" : "right-1",
|
|
1485
|
+
className
|
|
1486
|
+
),
|
|
1487
|
+
dir,
|
|
1488
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-auto flex flex-col gap-0 py-1", children: items.map((item, index) => {
|
|
1489
|
+
const itemMessageIds = item.messageIds && item.messageIds.length > 0 ? item.messageIds : [item.id];
|
|
1490
|
+
const active = itemMessageIds.some(
|
|
1491
|
+
(messageId) => visibleMessageIdSet.has(messageId) || messageId === currentAnchorId
|
|
1492
|
+
);
|
|
1493
|
+
const distance = focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;
|
|
1494
|
+
const isFocused = distance === 0;
|
|
1495
|
+
const isNear = distance === 1;
|
|
1496
|
+
const isNextNear = distance === 2;
|
|
1497
|
+
const barWidth = isFocused ? 50 : isNear ? 36 : isNextNear ? 23 : 10;
|
|
1498
|
+
const barOpacity = isFocused ? 1 : isNear ? 0.78 : isNextNear ? 0.58 : 0.36;
|
|
1499
|
+
const fallbackLabel = `Turn ${index + 1}`;
|
|
1500
|
+
const previewLabel = item.label && item.label !== fallbackLabel ? item.label : "";
|
|
1501
|
+
const responsePreview = (item.responsePreview || "").trim();
|
|
1502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1503
|
+
"button",
|
|
1504
|
+
{
|
|
1505
|
+
type: "button",
|
|
1506
|
+
"data-active": active,
|
|
1507
|
+
dir: "ltr",
|
|
1508
|
+
className: cx(
|
|
1509
|
+
"group relative flex h-2 w-16 items-center rounded-sm outline-none",
|
|
1510
|
+
railSide === "left" ? "justify-start" : "justify-end"
|
|
1511
|
+
),
|
|
1512
|
+
"aria-label": `Jump to ${item.label || fallbackLabel}`,
|
|
1513
|
+
onBlur: () => setHoveredIndex(null),
|
|
1514
|
+
onFocus: () => setHoveredIndex(index),
|
|
1515
|
+
onMouseEnter: () => setHoveredIndex(index),
|
|
1516
|
+
onMouseLeave: () => setHoveredIndex(null),
|
|
1517
|
+
onClick: () => scrollToMessage(item.id, {
|
|
1518
|
+
align: "start",
|
|
1519
|
+
behavior: "smooth"
|
|
1520
|
+
}),
|
|
1521
|
+
children: [
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1523
|
+
"span",
|
|
1524
|
+
{
|
|
1525
|
+
className: "h-0.5 rounded-full bg-muted-foreground/55 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground group-data-[active=true]:bg-foreground",
|
|
1526
|
+
style: {
|
|
1527
|
+
opacity: barOpacity,
|
|
1528
|
+
width: barWidth
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
),
|
|
1532
|
+
previewLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1533
|
+
"span",
|
|
1534
|
+
{
|
|
1535
|
+
dir,
|
|
1536
|
+
className: cx(
|
|
1537
|
+
"pointer-events-none absolute top-1/2 z-20 max-h-32 w-64 -translate-y-1/2 overflow-hidden rounded-lg border border-border/80 bg-background p-3 text-start opacity-0 shadow-lg transition-all duration-200 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-visible:pointer-events-auto group-focus-visible:opacity-100",
|
|
1538
|
+
railSide === "left" ? "left-full ml-2 -translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0" : "right-full mr-2 translate-x-1 group-hover:translate-x-0 group-focus-visible:translate-x-0",
|
|
1539
|
+
cardClassName
|
|
1540
|
+
),
|
|
1541
|
+
children: [
|
|
1542
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1543
|
+
"span",
|
|
1544
|
+
{
|
|
1545
|
+
className: "block overflow-hidden text-sm leading-5 text-foreground",
|
|
1546
|
+
style: {
|
|
1547
|
+
display: "-webkit-box",
|
|
1548
|
+
WebkitBoxOrient: "vertical",
|
|
1549
|
+
WebkitLineClamp: responsePreview ? 2 : 4
|
|
1550
|
+
},
|
|
1551
|
+
children: previewLabel
|
|
1552
|
+
}
|
|
1553
|
+
),
|
|
1554
|
+
responsePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1555
|
+
"span",
|
|
1556
|
+
{
|
|
1557
|
+
className: "mt-1 block overflow-hidden text-sm leading-5 text-muted-foreground",
|
|
1558
|
+
style: {
|
|
1559
|
+
display: "-webkit-box",
|
|
1560
|
+
WebkitBoxOrient: "vertical",
|
|
1561
|
+
WebkitLineClamp: 2
|
|
1562
|
+
},
|
|
1563
|
+
children: responsePreview
|
|
1564
|
+
}
|
|
1565
|
+
) : null
|
|
1566
|
+
]
|
|
1567
|
+
}
|
|
1568
|
+
) : null
|
|
1569
|
+
]
|
|
1570
|
+
},
|
|
1571
|
+
item.id
|
|
1572
|
+
);
|
|
1573
|
+
}) })
|
|
1574
|
+
}
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
var LatestThreadScroller = {
|
|
1578
|
+
Provider: LatestThreadScrollerProvider,
|
|
1579
|
+
Root: LatestThreadScrollerRoot,
|
|
1580
|
+
Viewport: LatestThreadScrollerViewport,
|
|
1581
|
+
Content: LatestThreadScrollerContent,
|
|
1582
|
+
Item: LatestThreadScrollerItem,
|
|
1583
|
+
Button: LatestThreadScrollerButton,
|
|
1584
|
+
Outline: LatestThreadScrollerOutline
|
|
1585
|
+
};
|
|
1586
|
+
|
|
1587
|
+
// src/renderers.tsx
|
|
1588
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1013
1589
|
var DefaultToolPart = ({ part }) => {
|
|
1014
1590
|
const label = typeof part.presentation?.activity === "string" ? part.presentation.activity : typeof part.presentation?.title === "string" ? part.presentation.title : part.toolName;
|
|
1015
1591
|
const detail = typeof part.presentation?.detail === "string" ? part.presentation.detail : part.errorText || part.state;
|
|
1016
|
-
return /* @__PURE__ */ (0,
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
1018
|
-
detail ? /* @__PURE__ */ (0,
|
|
1592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: [
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: label }),
|
|
1594
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: detail }) : null
|
|
1019
1595
|
] });
|
|
1020
1596
|
};
|
|
1021
1597
|
var DefaultChatPart = ({
|
|
@@ -1024,28 +1600,28 @@ var DefaultChatPart = ({
|
|
|
1024
1600
|
renderOptions
|
|
1025
1601
|
}) => {
|
|
1026
1602
|
if (renderOptions?.renderPart) {
|
|
1027
|
-
return /* @__PURE__ */ (0,
|
|
1603
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: renderOptions.renderPart(part, message) });
|
|
1028
1604
|
}
|
|
1029
|
-
if (part.kind === "text") return /* @__PURE__ */ (0,
|
|
1605
|
+
if (part.kind === "text") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: part.text });
|
|
1030
1606
|
if (part.kind === "tool") {
|
|
1031
1607
|
const ToolRenderer = renderOptions?.toolRenderers?.[part.type] || renderOptions?.fallbackToolRenderer || DefaultToolPart;
|
|
1032
|
-
return /* @__PURE__ */ (0,
|
|
1608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: ToolRenderer({ part, message }) });
|
|
1033
1609
|
}
|
|
1034
1610
|
if (part.kind === "reasoning") {
|
|
1035
|
-
return /* @__PURE__ */ (0,
|
|
1611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-reasoning-part": true, children: part.text || part.label });
|
|
1036
1612
|
}
|
|
1037
1613
|
if (part.kind === "ui-blocks") {
|
|
1038
|
-
return /* @__PURE__ */ (0,
|
|
1614
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-ui-blocks-part": true, "data-state": part.state });
|
|
1039
1615
|
}
|
|
1040
1616
|
if (part.kind === "approval") {
|
|
1041
|
-
return /* @__PURE__ */ (0,
|
|
1617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-approval-part": true });
|
|
1042
1618
|
}
|
|
1043
1619
|
if (part.kind === "error") {
|
|
1044
|
-
return /* @__PURE__ */ (0,
|
|
1620
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-error-part": true, children: part.errorText });
|
|
1045
1621
|
}
|
|
1046
|
-
return /* @__PURE__ */ (0,
|
|
1622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-agents24-data-part": part.name });
|
|
1047
1623
|
};
|
|
1048
|
-
var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0,
|
|
1624
|
+
var renderChatPart = (part, message, renderOptions) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1049
1625
|
DefaultChatPart,
|
|
1050
1626
|
{
|
|
1051
1627
|
part,
|
|
@@ -1311,306 +1887,45 @@ var createFetchChatTransport = ({
|
|
|
1311
1887
|
};
|
|
1312
1888
|
};
|
|
1313
1889
|
|
|
1314
|
-
// src/viewport.
|
|
1315
|
-
var
|
|
1316
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1317
|
-
var LATEST_EDGE_THRESHOLD_PX = 2;
|
|
1318
|
-
var MIN_OLDER_PREFETCH_PX = 320;
|
|
1319
|
-
var MAX_OLDER_PREFETCH_PX = 900;
|
|
1320
|
-
var isScrollable = (element) => element.scrollHeight - element.clientHeight > LATEST_EDGE_THRESHOLD_PX;
|
|
1321
|
-
var isAtTimelineLatestEdge = (element, isTopOrigin) => {
|
|
1322
|
-
if (!isScrollable(element)) return true;
|
|
1323
|
-
return isTopOrigin ? element.scrollHeight - element.clientHeight - element.scrollTop <= LATEST_EDGE_THRESHOLD_PX : Math.abs(element.scrollTop) <= LATEST_EDGE_THRESHOLD_PX;
|
|
1324
|
-
};
|
|
1325
|
-
var shouldPrefetchOlder = (element) => Math.abs(element.scrollTop) + element.clientHeight >= element.scrollHeight - Math.min(MAX_OLDER_PREFETCH_PX, Math.max(MIN_OLDER_PREFETCH_PX, element.clientHeight * 0.75));
|
|
1326
|
-
var getLatestScrollTop = (element, isTopOrigin) => isTopOrigin ? Math.max(0, element.scrollHeight - element.clientHeight) : 0;
|
|
1327
|
-
var shouldUseTopOriginTimeline = ({
|
|
1328
|
-
hasOlder,
|
|
1329
|
-
itemCount,
|
|
1330
|
-
topOriginMaxItems = 4
|
|
1331
|
-
}) => !hasOlder && itemCount > 0 && itemCount <= topOriginMaxItems;
|
|
1332
|
-
var nextLatestFollowStateOnScroll = ({
|
|
1333
|
-
atLatest,
|
|
1334
|
-
current,
|
|
1335
|
-
isProgrammatic
|
|
1336
|
-
}) => {
|
|
1337
|
-
if (isProgrammatic) return current;
|
|
1338
|
-
return atLatest ? "following" : "detached";
|
|
1339
|
-
};
|
|
1340
|
-
function useLatestThreadViewport({
|
|
1341
|
-
itemCount,
|
|
1342
|
-
hasOlder,
|
|
1343
|
-
isLoadingOlder,
|
|
1344
|
-
onLoadOlder,
|
|
1345
|
-
activeStreamKey,
|
|
1346
|
-
shouldAutoFollow = true,
|
|
1347
|
-
topOriginMaxItems = 4
|
|
1348
|
-
}) {
|
|
1349
|
-
const scrollContainerRef = (0, import_react3.useRef)(null);
|
|
1350
|
-
const olderPagePreserveRef = (0, import_react3.useRef)(null);
|
|
1351
|
-
const olderPageRequestInFlightRef = (0, import_react3.useRef)(false);
|
|
1352
|
-
const intrinsicResizePreserveRef = (0, import_react3.useRef)(null);
|
|
1353
|
-
const followStateRef = (0, import_react3.useRef)("following");
|
|
1354
|
-
const programmaticScrollRef = (0, import_react3.useRef)(false);
|
|
1355
|
-
const programmaticScrollTimeoutRef = (0, import_react3.useRef)(null);
|
|
1356
|
-
const activeStreamKeyRef = (0, import_react3.useRef)(null);
|
|
1357
|
-
const observedScrollHeightRef = (0, import_react3.useRef)(null);
|
|
1358
|
-
const [isAtLatest, setIsAtLatest] = (0, import_react3.useState)(true);
|
|
1359
|
-
const [followState, setFollowState] = (0, import_react3.useState)("following");
|
|
1360
|
-
const isTopOrigin = shouldUseTopOriginTimeline({ hasOlder, itemCount, topOriginMaxItems });
|
|
1361
|
-
const setLatestFollowState = (0, import_react3.useCallback)((next) => {
|
|
1362
|
-
if (followStateRef.current === next) return;
|
|
1363
|
-
followStateRef.current = next;
|
|
1364
|
-
setFollowState(next);
|
|
1365
|
-
}, []);
|
|
1366
|
-
const markProgrammaticScroll = (0, import_react3.useCallback)(() => {
|
|
1367
|
-
programmaticScrollRef.current = true;
|
|
1368
|
-
if (programmaticScrollTimeoutRef.current) clearTimeout(programmaticScrollTimeoutRef.current);
|
|
1369
|
-
programmaticScrollTimeoutRef.current = setTimeout(() => {
|
|
1370
|
-
programmaticScrollRef.current = false;
|
|
1371
|
-
programmaticScrollTimeoutRef.current = null;
|
|
1372
|
-
}, 80);
|
|
1373
|
-
}, []);
|
|
1374
|
-
const scrollToLatest = (0, import_react3.useCallback)((options) => {
|
|
1375
|
-
const element = scrollContainerRef.current;
|
|
1376
|
-
if (!element) return;
|
|
1377
|
-
if (options?.reattach !== false) setLatestFollowState("following");
|
|
1378
|
-
markProgrammaticScroll();
|
|
1379
|
-
element.scrollTop = getLatestScrollTop(element, isTopOrigin);
|
|
1380
|
-
setIsAtLatest(true);
|
|
1381
|
-
observedScrollHeightRef.current = element.scrollHeight;
|
|
1382
|
-
}, [isTopOrigin, markProgrammaticScroll, setLatestFollowState]);
|
|
1383
|
-
const detachFromLatest = (0, import_react3.useCallback)(() => {
|
|
1384
|
-
setLatestFollowState("detached");
|
|
1385
|
-
}, [setLatestFollowState]);
|
|
1386
|
-
const reattachToLatest = (0, import_react3.useCallback)(() => {
|
|
1387
|
-
scrollToLatest({ reattach: true });
|
|
1388
|
-
}, [scrollToLatest]);
|
|
1389
|
-
const handleUserScrollIntent = (0, import_react3.useCallback)(() => {
|
|
1390
|
-
const element = scrollContainerRef.current;
|
|
1391
|
-
if (!activeStreamKey || !element) return;
|
|
1392
|
-
if (!isAtTimelineLatestEdge(element, isTopOrigin)) detachFromLatest();
|
|
1393
|
-
}, [activeStreamKey, detachFromLatest, isTopOrigin]);
|
|
1394
|
-
(0, import_react3.useEffect)(() => {
|
|
1395
|
-
return () => {
|
|
1396
|
-
if (programmaticScrollTimeoutRef.current) clearTimeout(programmaticScrollTimeoutRef.current);
|
|
1397
|
-
const preserve = intrinsicResizePreserveRef.current;
|
|
1398
|
-
if (preserve?.frame !== null && preserve?.frame !== void 0) cancelAnimationFrame(preserve.frame);
|
|
1399
|
-
};
|
|
1400
|
-
}, []);
|
|
1401
|
-
(0, import_react3.useLayoutEffect)(() => {
|
|
1402
|
-
const element = scrollContainerRef.current;
|
|
1403
|
-
const preserve = olderPagePreserveRef.current;
|
|
1404
|
-
if (!element) return;
|
|
1405
|
-
observedScrollHeightRef.current = element.scrollHeight;
|
|
1406
|
-
if (!preserve) {
|
|
1407
|
-
setIsAtLatest(isAtTimelineLatestEdge(element, isTopOrigin));
|
|
1408
|
-
return;
|
|
1409
|
-
}
|
|
1410
|
-
const addedHeight = element.scrollHeight - preserve.previousHeight;
|
|
1411
|
-
markProgrammaticScroll();
|
|
1412
|
-
element.scrollTop = preserve.previousTop - addedHeight;
|
|
1413
|
-
olderPagePreserveRef.current = null;
|
|
1414
|
-
olderPageRequestInFlightRef.current = false;
|
|
1415
|
-
setIsAtLatest(isAtTimelineLatestEdge(element, isTopOrigin));
|
|
1416
|
-
}, [isTopOrigin, itemCount, markProgrammaticScroll]);
|
|
1417
|
-
const handleScroll = (0, import_react3.useCallback)(
|
|
1418
|
-
(event) => {
|
|
1419
|
-
const element = event.currentTarget;
|
|
1420
|
-
const atLatest = isAtTimelineLatestEdge(element, isTopOrigin);
|
|
1421
|
-
setIsAtLatest(atLatest);
|
|
1422
|
-
setLatestFollowState(
|
|
1423
|
-
nextLatestFollowStateOnScroll({
|
|
1424
|
-
atLatest,
|
|
1425
|
-
current: followStateRef.current,
|
|
1426
|
-
isProgrammatic: programmaticScrollRef.current
|
|
1427
|
-
})
|
|
1428
|
-
);
|
|
1429
|
-
if (isTopOrigin || !hasOlder || isLoadingOlder || olderPageRequestInFlightRef.current || !shouldPrefetchOlder(element)) {
|
|
1430
|
-
return;
|
|
1431
|
-
}
|
|
1432
|
-
olderPageRequestInFlightRef.current = true;
|
|
1433
|
-
olderPagePreserveRef.current = {
|
|
1434
|
-
previousHeight: element.scrollHeight,
|
|
1435
|
-
previousTop: element.scrollTop
|
|
1436
|
-
};
|
|
1437
|
-
void Promise.resolve(onLoadOlder()).finally(() => {
|
|
1438
|
-
requestAnimationFrame(() => {
|
|
1439
|
-
if (!olderPagePreserveRef.current) return;
|
|
1440
|
-
olderPagePreserveRef.current = null;
|
|
1441
|
-
olderPageRequestInFlightRef.current = false;
|
|
1442
|
-
});
|
|
1443
|
-
});
|
|
1444
|
-
},
|
|
1445
|
-
[hasOlder, isLoadingOlder, isTopOrigin, onLoadOlder, setLatestFollowState]
|
|
1446
|
-
);
|
|
1447
|
-
const preserveIntrinsicResize = (0, import_react3.useCallback)(() => {
|
|
1448
|
-
const element = scrollContainerRef.current;
|
|
1449
|
-
if (!element || isTopOrigin) return;
|
|
1450
|
-
const active = intrinsicResizePreserveRef.current;
|
|
1451
|
-
if (active?.frame !== null && active?.frame !== void 0) cancelAnimationFrame(active.frame);
|
|
1452
|
-
intrinsicResizePreserveRef.current = {
|
|
1453
|
-
lastHeight: element.scrollHeight,
|
|
1454
|
-
endAt: Date.now() + 260,
|
|
1455
|
-
frame: null
|
|
1456
|
-
};
|
|
1457
|
-
const preserveFrame = () => {
|
|
1458
|
-
const nextElement = scrollContainerRef.current;
|
|
1459
|
-
const preserve = intrinsicResizePreserveRef.current;
|
|
1460
|
-
if (!nextElement || !preserve) return;
|
|
1461
|
-
const heightDelta = nextElement.scrollHeight - preserve.lastHeight;
|
|
1462
|
-
if (Math.abs(heightDelta) >= 1) {
|
|
1463
|
-
markProgrammaticScroll();
|
|
1464
|
-
nextElement.scrollTop -= heightDelta;
|
|
1465
|
-
preserve.lastHeight = nextElement.scrollHeight;
|
|
1466
|
-
setIsAtLatest(isAtTimelineLatestEdge(nextElement, isTopOrigin));
|
|
1467
|
-
}
|
|
1468
|
-
if (Date.now() < preserve.endAt) {
|
|
1469
|
-
preserve.frame = requestAnimationFrame(preserveFrame);
|
|
1470
|
-
} else {
|
|
1471
|
-
intrinsicResizePreserveRef.current = null;
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
intrinsicResizePreserveRef.current.frame = requestAnimationFrame(preserveFrame);
|
|
1475
|
-
}, [isTopOrigin, markProgrammaticScroll]);
|
|
1476
|
-
(0, import_react3.useLayoutEffect)(() => {
|
|
1477
|
-
const key = activeStreamKey || null;
|
|
1478
|
-
const previousKey = activeStreamKeyRef.current;
|
|
1479
|
-
if (!key) {
|
|
1480
|
-
activeStreamKeyRef.current = null;
|
|
1481
|
-
if (previousKey && shouldAutoFollow && followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
|
|
1482
|
-
const frame2 = requestAnimationFrame(() => {
|
|
1483
|
-
if (followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
|
|
1484
|
-
scrollToLatest({ reattach: false });
|
|
1485
|
-
}
|
|
1486
|
-
});
|
|
1487
|
-
return () => cancelAnimationFrame(frame2);
|
|
1488
|
-
}
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
if (previousKey === key) {
|
|
1492
|
-
activeStreamKeyRef.current = key;
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
setLatestFollowState("following");
|
|
1496
|
-
const frame = requestAnimationFrame(() => scrollToLatest());
|
|
1497
|
-
activeStreamKeyRef.current = key;
|
|
1498
|
-
return () => cancelAnimationFrame(frame);
|
|
1499
|
-
}, [activeStreamKey, isLoadingOlder, scrollToLatest, setLatestFollowState, shouldAutoFollow]);
|
|
1500
|
-
(0, import_react3.useLayoutEffect)(() => {
|
|
1501
|
-
if (!activeStreamKey || !shouldAutoFollow || followStateRef.current !== "following") return;
|
|
1502
|
-
if (isLoadingOlder || olderPageRequestInFlightRef.current) return;
|
|
1503
|
-
const frame = requestAnimationFrame(() => {
|
|
1504
|
-
if (followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
|
|
1505
|
-
scrollToLatest({ reattach: false });
|
|
1506
|
-
}
|
|
1507
|
-
});
|
|
1508
|
-
return () => cancelAnimationFrame(frame);
|
|
1509
|
-
}, [activeStreamKey, isLoadingOlder, itemCount, scrollToLatest, shouldAutoFollow]);
|
|
1510
|
-
(0, import_react3.useEffect)(() => {
|
|
1511
|
-
if (!activeStreamKey) {
|
|
1512
|
-
observedScrollHeightRef.current = scrollContainerRef.current?.scrollHeight ?? null;
|
|
1513
|
-
return;
|
|
1514
|
-
}
|
|
1515
|
-
let frame = null;
|
|
1516
|
-
const watchStreamResize = () => {
|
|
1517
|
-
const element = scrollContainerRef.current;
|
|
1518
|
-
if (!element) return;
|
|
1519
|
-
const previousHeight = observedScrollHeightRef.current;
|
|
1520
|
-
const nextHeight = element.scrollHeight;
|
|
1521
|
-
observedScrollHeightRef.current = nextHeight;
|
|
1522
|
-
if (previousHeight !== null && Math.abs(nextHeight - previousHeight) >= 1 && shouldAutoFollow && followStateRef.current === "following" && !isLoadingOlder && !olderPageRequestInFlightRef.current) {
|
|
1523
|
-
scrollToLatest({ reattach: false });
|
|
1524
|
-
}
|
|
1525
|
-
frame = requestAnimationFrame(watchStreamResize);
|
|
1526
|
-
};
|
|
1527
|
-
frame = requestAnimationFrame(watchStreamResize);
|
|
1528
|
-
return () => {
|
|
1529
|
-
if (frame !== null) cancelAnimationFrame(frame);
|
|
1530
|
-
};
|
|
1531
|
-
}, [activeStreamKey, isLoadingOlder, scrollToLatest, shouldAutoFollow]);
|
|
1532
|
-
return {
|
|
1533
|
-
scrollContainerRef,
|
|
1534
|
-
isAtLatest,
|
|
1535
|
-
isFollowingLatest: followState === "following",
|
|
1536
|
-
isTopOrigin,
|
|
1537
|
-
shouldShowLatestButton: !isAtLatest,
|
|
1538
|
-
handleScroll,
|
|
1539
|
-
handleUserScrollIntent,
|
|
1540
|
-
scrollToLatest,
|
|
1541
|
-
detachFromLatest,
|
|
1542
|
-
reattachToLatest,
|
|
1543
|
-
preserveIntrinsicResize
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
function LatestThreadViewport({
|
|
1547
|
-
items,
|
|
1548
|
-
hasOlder,
|
|
1549
|
-
isLoadingOlder,
|
|
1550
|
-
onLoadOlder,
|
|
1551
|
-
activeStreamKey,
|
|
1552
|
-
className,
|
|
1553
|
-
latestSpacer,
|
|
1554
|
-
trailingSpacer,
|
|
1555
|
-
renderItem
|
|
1556
|
-
}) {
|
|
1557
|
-
const viewport = useLatestThreadViewport({
|
|
1558
|
-
itemCount: items.length,
|
|
1559
|
-
hasOlder,
|
|
1560
|
-
isLoadingOlder,
|
|
1561
|
-
onLoadOlder,
|
|
1562
|
-
activeStreamKey
|
|
1563
|
-
});
|
|
1564
|
-
const timelineItems = (0, import_react3.useMemo)(() => viewport.isTopOrigin ? items : [...items].reverse(), [items, viewport.isTopOrigin]);
|
|
1565
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1566
|
-
"div",
|
|
1567
|
-
{
|
|
1568
|
-
ref: viewport.scrollContainerRef,
|
|
1569
|
-
className,
|
|
1570
|
-
onKeyDown: viewport.handleUserScrollIntent,
|
|
1571
|
-
onScroll: viewport.handleScroll,
|
|
1572
|
-
onTouchMove: viewport.handleUserScrollIntent,
|
|
1573
|
-
onWheel: viewport.handleUserScrollIntent,
|
|
1574
|
-
role: "log",
|
|
1575
|
-
style: { overflowAnchor: "none" },
|
|
1576
|
-
children: [
|
|
1577
|
-
!viewport.isTopOrigin && (latestSpacer ?? null),
|
|
1578
|
-
timelineItems.map((item) => renderItem(item, viewport)),
|
|
1579
|
-
viewport.isTopOrigin && (trailingSpacer ?? latestSpacer ?? null)
|
|
1580
|
-
]
|
|
1581
|
-
}
|
|
1582
|
-
);
|
|
1583
|
-
}
|
|
1890
|
+
// src/viewport.ts
|
|
1891
|
+
var import_message_scroller2 = require("@shadcn/react/message-scroller");
|
|
1584
1892
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1585
1893
|
0 && (module.exports = {
|
|
1586
1894
|
DEFAULT_THREAD_PAGE_SIZE,
|
|
1587
1895
|
DefaultChatPart,
|
|
1588
1896
|
DefaultToolPart,
|
|
1589
|
-
|
|
1897
|
+
LatestThreadScroller,
|
|
1898
|
+
LatestThreadScrollerButton,
|
|
1899
|
+
LatestThreadScrollerContent,
|
|
1900
|
+
LatestThreadScrollerItem,
|
|
1901
|
+
LatestThreadScrollerOutline,
|
|
1902
|
+
LatestThreadScrollerProvider,
|
|
1903
|
+
LatestThreadScrollerRoot,
|
|
1904
|
+
LatestThreadScrollerViewport,
|
|
1905
|
+
MessageScroller,
|
|
1590
1906
|
activeRunIdFromThread,
|
|
1591
1907
|
activeRunIdFromThreadDetail,
|
|
1592
1908
|
assistantTextFromParts,
|
|
1593
1909
|
assistantTextFromResponseBlocks,
|
|
1910
|
+
compressionFromContextWindow,
|
|
1594
1911
|
consumeSseResponse,
|
|
1595
1912
|
createChatId,
|
|
1596
1913
|
createFetchChatTransport,
|
|
1597
1914
|
getActiveStreamingTextPartId,
|
|
1598
|
-
getLatestScrollTop,
|
|
1599
1915
|
hasStaleUnfinishedAssistantCache,
|
|
1600
1916
|
hasUnfinishedAssistantMessage,
|
|
1601
1917
|
isActiveStreamingTextPart,
|
|
1602
|
-
isAtTimelineLatestEdge,
|
|
1603
1918
|
isRunningThreadStatus,
|
|
1604
|
-
isScrollable,
|
|
1605
1919
|
latestContextWindowFromThread,
|
|
1920
|
+
mergeContextWindow,
|
|
1921
|
+
mergeContextWindowUpdate,
|
|
1606
1922
|
mergeReasoningSteps,
|
|
1607
|
-
|
|
1923
|
+
normalizeContextCompression,
|
|
1924
|
+
normalizeContextWindow,
|
|
1608
1925
|
parseSseBlock,
|
|
1609
1926
|
partsFromResponseBlocks,
|
|
1610
1927
|
reasoningStepsFromParts,
|
|
1611
1928
|
renderChatPart,
|
|
1612
|
-
shouldPrefetchOlder,
|
|
1613
|
-
shouldUseTopOriginTimeline,
|
|
1614
1929
|
textFromFinalOutput,
|
|
1615
1930
|
threadActivityDate,
|
|
1616
1931
|
threadDetailToMessages,
|
|
@@ -1618,7 +1933,9 @@ function LatestThreadViewport({
|
|
|
1618
1933
|
titleFromMessage,
|
|
1619
1934
|
toolStateFromStatus,
|
|
1620
1935
|
useAgents24ChatController,
|
|
1621
|
-
|
|
1936
|
+
useMessageScroller,
|
|
1937
|
+
useMessageScrollerScrollable,
|
|
1938
|
+
useMessageScrollerVisibility,
|
|
1622
1939
|
useStreamingText
|
|
1623
1940
|
});
|
|
1624
1941
|
//# sourceMappingURL=index.cjs.map
|