@iota-uz/sdk 0.4.36 → 0.4.38
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/bichat/index.cjs +458 -263
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.d.cts +46 -19
- package/dist/bichat/index.d.ts +46 -19
- package/dist/bichat/index.mjs +459 -264
- package/dist/bichat/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tailwind/compiled.css +1 -1
- package/tailwind/iota.css +40 -0
package/dist/bichat/index.cjs
CHANGED
|
@@ -2103,6 +2103,14 @@ var ChatMachine = class {
|
|
|
2103
2103
|
this.lastSendAttempt = null;
|
|
2104
2104
|
/** Prevents fetchSession effect from clobbering state while stream is active. */
|
|
2105
2105
|
this.sendingSessionId = null;
|
|
2106
|
+
/**
|
|
2107
|
+
* Monotonic counter bumped on every real session switch. An async op (send
|
|
2108
|
+
* stream, post-stream sync, queue drain, resume, HITL) captures it at start
|
|
2109
|
+
* and re-checks before writing to the single shared `state.messaging`; a stale
|
|
2110
|
+
* epoch means the user navigated to a different session, so the write is
|
|
2111
|
+
* dropped instead of bleeding into / clobbering the now-visible session.
|
|
2112
|
+
*/
|
|
2113
|
+
this.viewEpoch = 0;
|
|
2106
2114
|
this.fetchCancelled = false;
|
|
2107
2115
|
this.disposed = false;
|
|
2108
2116
|
this.reasoningEffortOptions = null;
|
|
@@ -2287,6 +2295,20 @@ var ChatMachine = class {
|
|
|
2287
2295
|
if (id === prev) {
|
|
2288
2296
|
return;
|
|
2289
2297
|
}
|
|
2298
|
+
this.viewEpoch++;
|
|
2299
|
+
this.abortController?.abort();
|
|
2300
|
+
this.sendingSessionId = null;
|
|
2301
|
+
this._stopPassivePolling();
|
|
2302
|
+
this._updateMessaging({
|
|
2303
|
+
streamingContent: "",
|
|
2304
|
+
thinkingContent: "",
|
|
2305
|
+
activeSteps: [],
|
|
2306
|
+
isStreaming: false,
|
|
2307
|
+
loading: false,
|
|
2308
|
+
generationInProgress: false,
|
|
2309
|
+
streamError: null,
|
|
2310
|
+
streamErrorRetryable: false
|
|
2311
|
+
});
|
|
2290
2312
|
this.state.session.currentSessionId = id;
|
|
2291
2313
|
this._hydrateDebugModeForSession(id);
|
|
2292
2314
|
this._notifySession();
|
|
@@ -2364,6 +2386,48 @@ var ChatMachine = class {
|
|
|
2364
2386
|
}
|
|
2365
2387
|
saveQueue(sid, this.state.input.messageQueue);
|
|
2366
2388
|
}
|
|
2389
|
+
/**
|
|
2390
|
+
* True while `epoch` is still the active view — i.e. the machine has not been
|
|
2391
|
+
* disposed and no session switch has happened since `epoch` was captured.
|
|
2392
|
+
* Async ops gate their writes to the shared messaging state on this.
|
|
2393
|
+
*/
|
|
2394
|
+
_isCurrentEpoch(epoch) {
|
|
2395
|
+
return !this.disposed && this.viewEpoch === epoch;
|
|
2396
|
+
}
|
|
2397
|
+
/** A send can't start while generating, mid-resume, or with an open question. */
|
|
2398
|
+
_isBlockedForSend() {
|
|
2399
|
+
return this.state.messaging.loading || this.state.messaging.generationInProgress || isOpenQuestionStatus(this.state.messaging.pendingQuestion?.status);
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Send the next queued message, but only if we still own the view (`epoch`)
|
|
2403
|
+
* and nothing is blocking (an in-flight generation or an open question). The
|
|
2404
|
+
* item is dequeued optimistically and restored to the front if it turns out we
|
|
2405
|
+
* can't send when the deferred tick fires — so a queued message is never
|
|
2406
|
+
* silently dropped.
|
|
2407
|
+
*/
|
|
2408
|
+
_drainQueueIfReady(epoch) {
|
|
2409
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
const queue = this.state.input.messageQueue;
|
|
2413
|
+
if (queue.length === 0 || this._isBlockedForSend()) {
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
const next = queue[0];
|
|
2417
|
+
this._updateInput({ messageQueue: queue.slice(1) });
|
|
2418
|
+
setTimeout(() => {
|
|
2419
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
if (this._isBlockedForSend()) {
|
|
2423
|
+
this._updateInput({
|
|
2424
|
+
messageQueue: [next, ...this.state.input.messageQueue]
|
|
2425
|
+
});
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
this._sendMessageCore(next.content, next.attachments);
|
|
2429
|
+
}, 0);
|
|
2430
|
+
}
|
|
2367
2431
|
_setDebugModeForSession(sessionId, enabled) {
|
|
2368
2432
|
const nextDebugModeBySession = { ...this.state.session.debugModeBySession };
|
|
2369
2433
|
if (enabled) {
|
|
@@ -2578,7 +2642,9 @@ var ChatMachine = class {
|
|
|
2578
2642
|
return;
|
|
2579
2643
|
}
|
|
2580
2644
|
this.sendingSessionId = sessionId;
|
|
2581
|
-
|
|
2645
|
+
const controller = new AbortController();
|
|
2646
|
+
this.abortController = controller;
|
|
2647
|
+
const epoch = this.viewEpoch;
|
|
2582
2648
|
this._updateMessaging({ isStreaming: true });
|
|
2583
2649
|
try {
|
|
2584
2650
|
let accumulatedContent = "";
|
|
@@ -2586,6 +2652,9 @@ var ChatMachine = class {
|
|
|
2586
2652
|
sessionId,
|
|
2587
2653
|
runId,
|
|
2588
2654
|
(chunk) => {
|
|
2655
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2589
2658
|
if (chunk.type === "snapshot" && chunk.snapshot?.partialContent !== void 0) {
|
|
2590
2659
|
accumulatedContent = chunk.snapshot.partialContent;
|
|
2591
2660
|
this._updateMessaging({ streamingContent: accumulatedContent });
|
|
@@ -2601,21 +2670,25 @@ var ChatMachine = class {
|
|
|
2601
2670
|
} else if (chunk.type === "done" || chunk.type === "error") {
|
|
2602
2671
|
}
|
|
2603
2672
|
},
|
|
2604
|
-
|
|
2673
|
+
controller.signal
|
|
2605
2674
|
);
|
|
2606
2675
|
clearRunMarker(sessionId);
|
|
2607
|
-
await this._syncSessionFromServer(sessionId, true);
|
|
2676
|
+
await this._syncSessionFromServer(sessionId, true, epoch);
|
|
2608
2677
|
} finally {
|
|
2609
|
-
this.
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2678
|
+
if (this.abortController === controller) {
|
|
2679
|
+
this.abortController = null;
|
|
2680
|
+
}
|
|
2681
|
+
if (this._isCurrentEpoch(epoch)) {
|
|
2682
|
+
this.sendingSessionId = null;
|
|
2683
|
+
this._updateMessaging({
|
|
2684
|
+
isStreaming: false,
|
|
2685
|
+
loading: false,
|
|
2686
|
+
streamingContent: "",
|
|
2687
|
+
thinkingContent: "",
|
|
2688
|
+
activeSteps: [],
|
|
2689
|
+
generationInProgress: false
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2619
2692
|
}
|
|
2620
2693
|
}
|
|
2621
2694
|
async _resumeAcceptedRunOrPoll(sessionId, runId) {
|
|
@@ -2858,11 +2931,14 @@ var ChatMachine = class {
|
|
|
2858
2931
|
}
|
|
2859
2932
|
return { activeSessionId, shouldNavigateAfter };
|
|
2860
2933
|
}
|
|
2861
|
-
async _syncSessionFromServer(sessionId, allowEmptyTurns = false) {
|
|
2934
|
+
async _syncSessionFromServer(sessionId, allowEmptyTurns = false, epoch) {
|
|
2862
2935
|
const fetchResult = await this.dataSource.fetchSession(sessionId);
|
|
2863
2936
|
if (!fetchResult) {
|
|
2864
2937
|
return;
|
|
2865
2938
|
}
|
|
2939
|
+
if (epoch !== void 0 && !this._isCurrentEpoch(epoch)) {
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2866
2942
|
this._updateSession({ session: fetchResult.session });
|
|
2867
2943
|
this._setTurnsFromFetch(
|
|
2868
2944
|
allowEmptyTurns ? fetchResult.turns ?? [] : fetchResult.turns,
|
|
@@ -2878,7 +2954,9 @@ var ChatMachine = class {
|
|
|
2878
2954
|
replaceFromMessageID,
|
|
2879
2955
|
reasoningEffort,
|
|
2880
2956
|
model,
|
|
2881
|
-
tempTurnId
|
|
2957
|
+
tempTurnId,
|
|
2958
|
+
controller,
|
|
2959
|
+
epoch
|
|
2882
2960
|
} = params;
|
|
2883
2961
|
let accumulatedContent = "";
|
|
2884
2962
|
let createdSessionId;
|
|
@@ -2889,7 +2967,7 @@ var ChatMachine = class {
|
|
|
2889
2967
|
activeSessionId || "new",
|
|
2890
2968
|
content,
|
|
2891
2969
|
attachments,
|
|
2892
|
-
|
|
2970
|
+
controller.signal,
|
|
2893
2971
|
{
|
|
2894
2972
|
debugMode,
|
|
2895
2973
|
replaceFromMessageID,
|
|
@@ -2897,7 +2975,7 @@ var ChatMachine = class {
|
|
|
2897
2975
|
model
|
|
2898
2976
|
}
|
|
2899
2977
|
)) {
|
|
2900
|
-
if (
|
|
2978
|
+
if (controller.signal.aborted || !this._isCurrentEpoch(epoch)) {
|
|
2901
2979
|
break;
|
|
2902
2980
|
}
|
|
2903
2981
|
if (chunk.type === "stream_started" && chunk.runId) {
|
|
@@ -2938,7 +3016,7 @@ var ChatMachine = class {
|
|
|
2938
3016
|
sessionFetched = true;
|
|
2939
3017
|
const finalSessionId2 = createdSessionId || activeSessionId;
|
|
2940
3018
|
if (finalSessionId2 && finalSessionId2 !== "new") {
|
|
2941
|
-
await this._syncSessionFromServer(finalSessionId2);
|
|
3019
|
+
await this._syncSessionFromServer(finalSessionId2, false, epoch);
|
|
2942
3020
|
}
|
|
2943
3021
|
}
|
|
2944
3022
|
} else if (chunk.type === "done") {
|
|
@@ -2949,7 +3027,7 @@ var ChatMachine = class {
|
|
|
2949
3027
|
sessionFetched = true;
|
|
2950
3028
|
const finalSessionId2 = createdSessionId || activeSessionId;
|
|
2951
3029
|
if (finalSessionId2 && finalSessionId2 !== "new") {
|
|
2952
|
-
await this._syncSessionFromServer(finalSessionId2);
|
|
3030
|
+
await this._syncSessionFromServer(finalSessionId2, false, epoch);
|
|
2953
3031
|
}
|
|
2954
3032
|
}
|
|
2955
3033
|
} else if (chunk.type === "user_message" && chunk.sessionId) {
|
|
@@ -2963,10 +3041,10 @@ var ChatMachine = class {
|
|
|
2963
3041
|
if (finalSessionId && finalSessionId !== "new") {
|
|
2964
3042
|
clearRunMarker(finalSessionId);
|
|
2965
3043
|
}
|
|
2966
|
-
const stopped =
|
|
3044
|
+
const stopped = controller.signal.aborted;
|
|
2967
3045
|
return { createdSessionId, sessionFetched, stopped };
|
|
2968
3046
|
}
|
|
2969
|
-
async _ensureSessionSyncAfterStream(activeSessionId, createdSessionId, sessionFetched) {
|
|
3047
|
+
async _ensureSessionSyncAfterStream(activeSessionId, createdSessionId, sessionFetched, epoch) {
|
|
2970
3048
|
if (sessionFetched) {
|
|
2971
3049
|
return;
|
|
2972
3050
|
}
|
|
@@ -2975,7 +3053,7 @@ var ChatMachine = class {
|
|
|
2975
3053
|
return;
|
|
2976
3054
|
}
|
|
2977
3055
|
try {
|
|
2978
|
-
await this._syncSessionFromServer(finalSessionId, true);
|
|
3056
|
+
await this._syncSessionFromServer(finalSessionId, true, epoch);
|
|
2979
3057
|
} catch (fetchErr) {
|
|
2980
3058
|
console.error("Failed to fetch session after stream:", fetchErr);
|
|
2981
3059
|
}
|
|
@@ -2993,32 +3071,36 @@ var ChatMachine = class {
|
|
|
2993
3071
|
this._clearStreamError();
|
|
2994
3072
|
this.lastSendAttempt = null;
|
|
2995
3073
|
}
|
|
2996
|
-
_handleSendError(err, content,
|
|
3074
|
+
_handleSendError(err, content, tempTurn, epoch) {
|
|
3075
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
2997
3078
|
if (err instanceof Error && err.name === "AbortError") {
|
|
2998
3079
|
this._updateInput({ message: content });
|
|
2999
3080
|
this._clearStreamError();
|
|
3000
3081
|
this._updateMessaging({
|
|
3001
3082
|
turns: this.state.messaging.turns.filter(
|
|
3002
|
-
(turn) => turn.id !==
|
|
3083
|
+
(turn) => turn.id !== tempTurn.id
|
|
3003
3084
|
)
|
|
3004
3085
|
});
|
|
3005
3086
|
const sessionId = this.sendingSessionId ?? this.state.session.currentSessionId;
|
|
3006
3087
|
if (sessionId && sessionId !== "new") {
|
|
3007
|
-
this._syncSessionFromServer(sessionId, true).catch(() => {
|
|
3088
|
+
this._syncSessionFromServer(sessionId, true, epoch).catch(() => {
|
|
3008
3089
|
});
|
|
3009
3090
|
}
|
|
3010
3091
|
return false;
|
|
3011
3092
|
}
|
|
3012
|
-
this._updateMessaging({
|
|
3013
|
-
turns: this.state.messaging.turns.filter(
|
|
3014
|
-
(turn) => turn.id !== tempTurnId
|
|
3015
|
-
)
|
|
3016
|
-
});
|
|
3017
3093
|
const normalized = normalizeRPCError(err, "Failed to send message");
|
|
3018
|
-
this.
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3094
|
+
if (this.lastSendAttempt) {
|
|
3095
|
+
this.lastSendAttempt = {
|
|
3096
|
+
...this.lastSendAttempt,
|
|
3097
|
+
options: {
|
|
3098
|
+
...this.lastSendAttempt.options,
|
|
3099
|
+
replaceFromMessageID: tempTurn.userTurn.id
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
this._updateInput({ message: "", inputError: null });
|
|
3022
3104
|
this._updateMessaging({
|
|
3023
3105
|
streamError: normalized.userMessage,
|
|
3024
3106
|
streamErrorRetryable: normalized.retryable
|
|
@@ -3079,7 +3161,9 @@ var ChatMachine = class {
|
|
|
3079
3161
|
loading: true,
|
|
3080
3162
|
streamingContent: ""
|
|
3081
3163
|
});
|
|
3082
|
-
|
|
3164
|
+
const controller = new AbortController();
|
|
3165
|
+
this.abortController = controller;
|
|
3166
|
+
const epoch = this.viewEpoch;
|
|
3083
3167
|
const curSessionId = this.state.session.currentSessionId;
|
|
3084
3168
|
const curDebugMode = deriveDebugMode(this.state);
|
|
3085
3169
|
const replaceFromMessageID = options?.replaceFromMessageID;
|
|
@@ -3105,9 +3189,12 @@ var ChatMachine = class {
|
|
|
3105
3189
|
this.state.session.reasoningEffort
|
|
3106
3190
|
),
|
|
3107
3191
|
model: this.state.session.model,
|
|
3108
|
-
tempTurnId: tempTurn.id
|
|
3192
|
+
tempTurnId: tempTurn.id,
|
|
3193
|
+
controller,
|
|
3194
|
+
epoch
|
|
3109
3195
|
});
|
|
3110
|
-
if (
|
|
3196
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3197
|
+
} else if (stopped) {
|
|
3111
3198
|
this._updateMessaging({
|
|
3112
3199
|
turns: this.state.messaging.turns.filter(
|
|
3113
3200
|
(turn) => turn.id !== tempTurn.id
|
|
@@ -3117,39 +3204,37 @@ var ChatMachine = class {
|
|
|
3117
3204
|
this._clearStreamError();
|
|
3118
3205
|
const syncId = createdSessionId || activeSessionId;
|
|
3119
3206
|
if (syncId && syncId !== "new") {
|
|
3120
|
-
await this._syncSessionFromServer(syncId, true).catch(() => {
|
|
3207
|
+
await this._syncSessionFromServer(syncId, true, epoch).catch(() => {
|
|
3121
3208
|
});
|
|
3122
3209
|
}
|
|
3123
3210
|
} else {
|
|
3124
3211
|
await this._ensureSessionSyncAfterStream(
|
|
3125
3212
|
activeSessionId,
|
|
3126
3213
|
createdSessionId,
|
|
3127
|
-
sessionFetched
|
|
3214
|
+
sessionFetched,
|
|
3215
|
+
epoch
|
|
3128
3216
|
);
|
|
3129
3217
|
const targetSessionId = createdSessionId || activeSessionId;
|
|
3130
3218
|
this._finalizeSuccessfulSend(targetSessionId, shouldNavigateAfter);
|
|
3131
3219
|
}
|
|
3132
3220
|
} catch (err) {
|
|
3133
|
-
shouldDrainQueue = this._handleSendError(err, content, tempTurn
|
|
3221
|
+
shouldDrainQueue = this._handleSendError(err, content, tempTurn, epoch);
|
|
3134
3222
|
} finally {
|
|
3135
|
-
this.
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3223
|
+
if (this._isCurrentEpoch(epoch)) {
|
|
3224
|
+
this._updateMessaging({
|
|
3225
|
+
loading: false,
|
|
3226
|
+
streamingContent: "",
|
|
3227
|
+
isStreaming: false,
|
|
3228
|
+
thinkingContent: "",
|
|
3229
|
+
activeSteps: []
|
|
3230
|
+
});
|
|
3231
|
+
this.sendingSessionId = null;
|
|
3232
|
+
}
|
|
3233
|
+
if (this.abortController === controller) {
|
|
3234
|
+
this.abortController = null;
|
|
3235
|
+
}
|
|
3144
3236
|
if (shouldDrainQueue) {
|
|
3145
|
-
|
|
3146
|
-
if (queue.length > 0 && !isOpenQuestionStatus(this.state.messaging.pendingQuestion?.status)) {
|
|
3147
|
-
const next = queue[0];
|
|
3148
|
-
this._updateInput({ messageQueue: queue.slice(1) });
|
|
3149
|
-
setTimeout(() => {
|
|
3150
|
-
this._sendMessageCore(next.content, next.attachments);
|
|
3151
|
-
}, 0);
|
|
3152
|
-
}
|
|
3237
|
+
this._drainQueueIfReady(epoch);
|
|
3153
3238
|
}
|
|
3154
3239
|
}
|
|
3155
3240
|
}
|
|
@@ -3236,7 +3321,7 @@ var ChatMachine = class {
|
|
|
3236
3321
|
);
|
|
3237
3322
|
}
|
|
3238
3323
|
// ── Regenerate / Edit ───────────────────────────────────────────────────
|
|
3239
|
-
async _handleRegenerate(turnId) {
|
|
3324
|
+
async _handleRegenerate(turnId, model) {
|
|
3240
3325
|
const curSessionId = this.state.session.currentSessionId;
|
|
3241
3326
|
if (!curSessionId || curSessionId === "new") {
|
|
3242
3327
|
return;
|
|
@@ -3245,6 +3330,9 @@ var ChatMachine = class {
|
|
|
3245
3330
|
if (!turn) {
|
|
3246
3331
|
return;
|
|
3247
3332
|
}
|
|
3333
|
+
if (model && model !== this.state.session.model) {
|
|
3334
|
+
this._setModel(model);
|
|
3335
|
+
}
|
|
3248
3336
|
this._updateSession({ error: null, errorRetryable: false });
|
|
3249
3337
|
await this._sendMessageDirect(
|
|
3250
3338
|
turn.userTurn.content,
|
|
@@ -3292,6 +3380,7 @@ var ChatMachine = class {
|
|
|
3292
3380
|
if (!curSessionId || !curPendingQuestion) {
|
|
3293
3381
|
return;
|
|
3294
3382
|
}
|
|
3383
|
+
const epoch = this.viewEpoch;
|
|
3295
3384
|
this._updateMessaging({ loading: true });
|
|
3296
3385
|
this._updateSession({ error: null, errorRetryable: false });
|
|
3297
3386
|
const previousPendingQuestion = curPendingQuestion;
|
|
@@ -3301,7 +3390,7 @@ var ChatMachine = class {
|
|
|
3301
3390
|
previousPendingQuestion.id,
|
|
3302
3391
|
answers
|
|
3303
3392
|
);
|
|
3304
|
-
if (this.
|
|
3393
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3305
3394
|
return;
|
|
3306
3395
|
}
|
|
3307
3396
|
if (result.success) {
|
|
@@ -3322,7 +3411,7 @@ var ChatMachine = class {
|
|
|
3322
3411
|
await this._resumeAcceptedRunOrPoll(curSessionId, result.data.runId);
|
|
3323
3412
|
if (!this.state.messaging.generationInProgress) {
|
|
3324
3413
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
3325
|
-
if (this.
|
|
3414
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3326
3415
|
return;
|
|
3327
3416
|
}
|
|
3328
3417
|
if (fetchResult) {
|
|
@@ -3340,7 +3429,7 @@ var ChatMachine = class {
|
|
|
3340
3429
|
}
|
|
3341
3430
|
} else if (curSessionId !== "new") {
|
|
3342
3431
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
3343
|
-
if (this.
|
|
3432
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3344
3433
|
return;
|
|
3345
3434
|
}
|
|
3346
3435
|
if (fetchResult) {
|
|
@@ -3363,7 +3452,7 @@ var ChatMachine = class {
|
|
|
3363
3452
|
});
|
|
3364
3453
|
}
|
|
3365
3454
|
} catch (err) {
|
|
3366
|
-
if (this.
|
|
3455
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3367
3456
|
return;
|
|
3368
3457
|
}
|
|
3369
3458
|
const normalized = normalizeRPCError(err, "Failed to submit answers");
|
|
@@ -3372,8 +3461,11 @@ var ChatMachine = class {
|
|
|
3372
3461
|
errorRetryable: normalized.retryable
|
|
3373
3462
|
});
|
|
3374
3463
|
} finally {
|
|
3375
|
-
if (
|
|
3464
|
+
if (this._isCurrentEpoch(epoch)) {
|
|
3376
3465
|
this._updateMessaging({ loading: false });
|
|
3466
|
+
if (!this.state.session.error) {
|
|
3467
|
+
this._drainQueueIfReady(epoch);
|
|
3468
|
+
}
|
|
3377
3469
|
}
|
|
3378
3470
|
}
|
|
3379
3471
|
}
|
|
@@ -3383,9 +3475,10 @@ var ChatMachine = class {
|
|
|
3383
3475
|
if (!curSessionId || !curPendingQuestion) {
|
|
3384
3476
|
return;
|
|
3385
3477
|
}
|
|
3478
|
+
const epoch = this.viewEpoch;
|
|
3386
3479
|
try {
|
|
3387
3480
|
const result = await this.dataSource.rejectPendingQuestion(curSessionId);
|
|
3388
|
-
if (this.
|
|
3481
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3389
3482
|
return;
|
|
3390
3483
|
}
|
|
3391
3484
|
if (result.success) {
|
|
@@ -3404,7 +3497,7 @@ var ChatMachine = class {
|
|
|
3404
3497
|
await this._resumeAcceptedRunOrPoll(curSessionId, result.data.runId);
|
|
3405
3498
|
if (!this.state.messaging.generationInProgress && curSessionId !== "new") {
|
|
3406
3499
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
3407
|
-
if (this.
|
|
3500
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3408
3501
|
return;
|
|
3409
3502
|
}
|
|
3410
3503
|
if (fetchResult) {
|
|
@@ -3422,7 +3515,7 @@ var ChatMachine = class {
|
|
|
3422
3515
|
}
|
|
3423
3516
|
} else if (curSessionId !== "new") {
|
|
3424
3517
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
3425
|
-
if (this.
|
|
3518
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3426
3519
|
return;
|
|
3427
3520
|
}
|
|
3428
3521
|
if (fetchResult) {
|
|
@@ -3440,7 +3533,7 @@ var ChatMachine = class {
|
|
|
3440
3533
|
});
|
|
3441
3534
|
}
|
|
3442
3535
|
} catch (err) {
|
|
3443
|
-
if (this.
|
|
3536
|
+
if (!this._isCurrentEpoch(epoch)) {
|
|
3444
3537
|
return;
|
|
3445
3538
|
}
|
|
3446
3539
|
const normalized = normalizeRPCError(err, "Failed to reject question");
|
|
@@ -3448,6 +3541,10 @@ var ChatMachine = class {
|
|
|
3448
3541
|
error: normalized.userMessage,
|
|
3449
3542
|
errorRetryable: normalized.retryable
|
|
3450
3543
|
});
|
|
3544
|
+
} finally {
|
|
3545
|
+
if (this._isCurrentEpoch(epoch) && !this.state.session.error) {
|
|
3546
|
+
this._drainQueueIfReady(epoch);
|
|
3547
|
+
}
|
|
3451
3548
|
}
|
|
3452
3549
|
}
|
|
3453
3550
|
// ── Input / queue ───────────────────────────────────────────────────────
|
|
@@ -4304,6 +4401,167 @@ function AttachmentGrid({
|
|
|
4304
4401
|
var MemoizedAttachmentGrid = React__default.default.memo(AttachmentGrid);
|
|
4305
4402
|
MemoizedAttachmentGrid.displayName = "AttachmentGrid";
|
|
4306
4403
|
var AttachmentGrid_default = MemoizedAttachmentGrid;
|
|
4404
|
+
function deepActiveElement(root) {
|
|
4405
|
+
let active = root.activeElement;
|
|
4406
|
+
while (active?.shadowRoot?.activeElement) {
|
|
4407
|
+
active = active.shadowRoot.activeElement;
|
|
4408
|
+
}
|
|
4409
|
+
return active instanceof HTMLElement ? active : null;
|
|
4410
|
+
}
|
|
4411
|
+
function useFocusTrap(containerRef, isActive, restoreFocusOnDeactivate) {
|
|
4412
|
+
React.useEffect(() => {
|
|
4413
|
+
if (!isActive || !containerRef.current) {
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
const container = containerRef.current;
|
|
4417
|
+
const root = container.getRootNode();
|
|
4418
|
+
const previouslyFocused = deepActiveElement(root);
|
|
4419
|
+
const getFocusableElements = () => {
|
|
4420
|
+
const selector = [
|
|
4421
|
+
"button:not([disabled])",
|
|
4422
|
+
"[href]",
|
|
4423
|
+
"input:not([disabled])",
|
|
4424
|
+
"select:not([disabled])",
|
|
4425
|
+
"textarea:not([disabled])",
|
|
4426
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
4427
|
+
].join(", ");
|
|
4428
|
+
return Array.from(container.querySelectorAll(selector));
|
|
4429
|
+
};
|
|
4430
|
+
const focusableElements = getFocusableElements();
|
|
4431
|
+
const initialTarget = container.querySelector("[data-autofocus]") ?? focusableElements[0];
|
|
4432
|
+
initialTarget?.focus();
|
|
4433
|
+
const handleTabKey = (e) => {
|
|
4434
|
+
if (e.key !== "Tab") {
|
|
4435
|
+
return;
|
|
4436
|
+
}
|
|
4437
|
+
const focusableElements2 = getFocusableElements();
|
|
4438
|
+
if (focusableElements2.length === 0) {
|
|
4439
|
+
return;
|
|
4440
|
+
}
|
|
4441
|
+
const firstElement = focusableElements2[0];
|
|
4442
|
+
const lastElement = focusableElements2[focusableElements2.length - 1];
|
|
4443
|
+
const active = deepActiveElement(root);
|
|
4444
|
+
if (e.shiftKey) {
|
|
4445
|
+
if (active === firstElement) {
|
|
4446
|
+
e.preventDefault();
|
|
4447
|
+
lastElement.focus();
|
|
4448
|
+
}
|
|
4449
|
+
} else {
|
|
4450
|
+
if (active === lastElement) {
|
|
4451
|
+
e.preventDefault();
|
|
4452
|
+
firstElement.focus();
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
};
|
|
4456
|
+
container.addEventListener("keydown", handleTabKey);
|
|
4457
|
+
return () => {
|
|
4458
|
+
container.removeEventListener("keydown", handleTabKey);
|
|
4459
|
+
if (restoreFocusOnDeactivate) {
|
|
4460
|
+
restoreFocusOnDeactivate.focus();
|
|
4461
|
+
} else if (previouslyFocused) {
|
|
4462
|
+
previouslyFocused.focus();
|
|
4463
|
+
}
|
|
4464
|
+
};
|
|
4465
|
+
}, [containerRef, isActive, restoreFocusOnDeactivate]);
|
|
4466
|
+
}
|
|
4467
|
+
var lockCount = 0;
|
|
4468
|
+
var restorePreviousStyles = null;
|
|
4469
|
+
function applyBodyLock() {
|
|
4470
|
+
const body = document.body;
|
|
4471
|
+
const previousOverflow = body.style.overflow;
|
|
4472
|
+
const previousPaddingRight = body.style.paddingRight;
|
|
4473
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
4474
|
+
body.style.overflow = "hidden";
|
|
4475
|
+
if (scrollbarWidth > 0) {
|
|
4476
|
+
const currentPadding = parseFloat(window.getComputedStyle(body).paddingRight) || 0;
|
|
4477
|
+
body.style.paddingRight = `${currentPadding + scrollbarWidth}px`;
|
|
4478
|
+
}
|
|
4479
|
+
restorePreviousStyles = () => {
|
|
4480
|
+
body.style.overflow = previousOverflow;
|
|
4481
|
+
body.style.paddingRight = previousPaddingRight;
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
function useModalLock(isOpen) {
|
|
4485
|
+
React.useEffect(() => {
|
|
4486
|
+
if (!isOpen) {
|
|
4487
|
+
return;
|
|
4488
|
+
}
|
|
4489
|
+
if (lockCount === 0) {
|
|
4490
|
+
applyBodyLock();
|
|
4491
|
+
}
|
|
4492
|
+
lockCount += 1;
|
|
4493
|
+
return () => {
|
|
4494
|
+
lockCount -= 1;
|
|
4495
|
+
if (lockCount === 0 && restorePreviousStyles) {
|
|
4496
|
+
restorePreviousStyles();
|
|
4497
|
+
restorePreviousStyles = null;
|
|
4498
|
+
}
|
|
4499
|
+
};
|
|
4500
|
+
}, [isOpen]);
|
|
4501
|
+
}
|
|
4502
|
+
var DialogContext = React.createContext(null);
|
|
4503
|
+
function InlineDialog({ open, onClose, className, children }) {
|
|
4504
|
+
const containerRef = React.useRef(null);
|
|
4505
|
+
useModalLock(open);
|
|
4506
|
+
useFocusTrap(containerRef, open);
|
|
4507
|
+
React.useEffect(() => {
|
|
4508
|
+
if (!open) {
|
|
4509
|
+
return;
|
|
4510
|
+
}
|
|
4511
|
+
const container = containerRef.current;
|
|
4512
|
+
if (!container) {
|
|
4513
|
+
return;
|
|
4514
|
+
}
|
|
4515
|
+
const handler = (e) => {
|
|
4516
|
+
if (e.key === "Escape") {
|
|
4517
|
+
onClose();
|
|
4518
|
+
}
|
|
4519
|
+
};
|
|
4520
|
+
container.addEventListener("keydown", handler);
|
|
4521
|
+
return () => container.removeEventListener("keydown", handler);
|
|
4522
|
+
}, [open, onClose]);
|
|
4523
|
+
if (!open) {
|
|
4524
|
+
return null;
|
|
4525
|
+
}
|
|
4526
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4527
|
+
"div",
|
|
4528
|
+
{
|
|
4529
|
+
ref: containerRef,
|
|
4530
|
+
className,
|
|
4531
|
+
onClick: onClose,
|
|
4532
|
+
tabIndex: -1,
|
|
4533
|
+
children
|
|
4534
|
+
}
|
|
4535
|
+
) });
|
|
4536
|
+
}
|
|
4537
|
+
function InlineDialogBackdrop(props) {
|
|
4538
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", ...props });
|
|
4539
|
+
}
|
|
4540
|
+
function InlineDialogPanel({
|
|
4541
|
+
children,
|
|
4542
|
+
onClick,
|
|
4543
|
+
...rest
|
|
4544
|
+
}) {
|
|
4545
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4546
|
+
"div",
|
|
4547
|
+
{
|
|
4548
|
+
role: "dialog",
|
|
4549
|
+
"aria-modal": "true",
|
|
4550
|
+
onClick: (e) => {
|
|
4551
|
+
e.stopPropagation();
|
|
4552
|
+
onClick?.(e);
|
|
4553
|
+
},
|
|
4554
|
+
...rest,
|
|
4555
|
+
children
|
|
4556
|
+
}
|
|
4557
|
+
);
|
|
4558
|
+
}
|
|
4559
|
+
function InlineDialogTitle(props) {
|
|
4560
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h2", { ...props });
|
|
4561
|
+
}
|
|
4562
|
+
function InlineDialogDescription(props) {
|
|
4563
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { ...props });
|
|
4564
|
+
}
|
|
4307
4565
|
init_useTranslation();
|
|
4308
4566
|
function ToolbarButton({
|
|
4309
4567
|
onClick,
|
|
@@ -4506,16 +4764,16 @@ function ImageModal({
|
|
|
4506
4764
|
}, [isZoomed, onClose]);
|
|
4507
4765
|
const previewUrl = attachment.preview || createDataUrl(attachment.base64Data, attachment.mimeType);
|
|
4508
4766
|
const zoomPercent = Math.round(scale * 100);
|
|
4509
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4767
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(InlineDialog, { open: isOpen, onClose, className: "relative z-[99999]", children: [
|
|
4510
4768
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4511
|
-
|
|
4769
|
+
InlineDialogBackdrop,
|
|
4512
4770
|
{
|
|
4513
4771
|
className: "fixed inset-0 bg-black/90 backdrop-blur-sm",
|
|
4514
4772
|
style: { zIndex: 99999 }
|
|
4515
4773
|
}
|
|
4516
4774
|
),
|
|
4517
4775
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4518
|
-
|
|
4776
|
+
InlineDialogPanel,
|
|
4519
4777
|
{
|
|
4520
4778
|
className: "fixed inset-0 flex flex-col",
|
|
4521
4779
|
style: { zIndex: 1e5 },
|
|
@@ -4664,7 +4922,7 @@ var defaultClassNames = {
|
|
|
4664
4922
|
bubble: "bg-primary-600 text-white rounded-2xl rounded-br-sm px-4 py-3 shadow-sm",
|
|
4665
4923
|
content: "text-sm whitespace-pre-wrap break-words leading-relaxed",
|
|
4666
4924
|
attachments: "mb-2 w-full",
|
|
4667
|
-
actions: "flex items-center gap-1 mt-2
|
|
4925
|
+
actions: "flex items-center gap-1 mt-2",
|
|
4668
4926
|
actionButton: "cursor-pointer p-2 min-h-[44px] min-w-[44px] flex items-center justify-center text-gray-500 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 active:bg-gray-200 dark:active:bg-gray-700 rounded-md transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
4669
4927
|
timestamp: "text-xs text-gray-400 dark:text-gray-500 mr-1"
|
|
4670
4928
|
};
|
|
@@ -5063,6 +5321,7 @@ function UserTurnView({
|
|
|
5063
5321
|
}
|
|
5064
5322
|
);
|
|
5065
5323
|
}
|
|
5324
|
+
init_IotaContext();
|
|
5066
5325
|
init_useTranslation();
|
|
5067
5326
|
function toBase64(str) {
|
|
5068
5327
|
const bytes = new TextEncoder().encode(str);
|
|
@@ -6177,6 +6436,8 @@ function FullscreenOverlay({ title, onClose, closeLabel, children }) {
|
|
|
6177
6436
|
const panelRef = React.useRef(null);
|
|
6178
6437
|
const onCloseRef = React.useRef(onClose);
|
|
6179
6438
|
onCloseRef.current = onClose;
|
|
6439
|
+
useModalLock(true);
|
|
6440
|
+
useFocusTrap(panelRef, true);
|
|
6180
6441
|
React.useEffect(() => {
|
|
6181
6442
|
const onKeyDown = (e) => {
|
|
6182
6443
|
if (e.key === "Escape") {
|
|
@@ -6185,7 +6446,6 @@ function FullscreenOverlay({ title, onClose, closeLabel, children }) {
|
|
|
6185
6446
|
}
|
|
6186
6447
|
};
|
|
6187
6448
|
document.addEventListener("keydown", onKeyDown);
|
|
6188
|
-
panelRef.current?.focus();
|
|
6189
6449
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
6190
6450
|
}, []);
|
|
6191
6451
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0", style: { zIndex: 99999 }, children: [
|
|
@@ -7754,7 +8014,7 @@ var defaultClassNames2 = {
|
|
|
7754
8014
|
artifacts: "mb-1 flex flex-wrap gap-2",
|
|
7755
8015
|
sources: "",
|
|
7756
8016
|
explanation: "mt-4 border-t border-gray-100 dark:border-gray-700 pt-4",
|
|
7757
|
-
actions: "flex items-center gap-1
|
|
8017
|
+
actions: "flex items-center gap-1",
|
|
7758
8018
|
actionButton: "cursor-pointer p-2 min-h-[44px] min-w-[44px] flex items-center justify-center text-gray-500 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 active:bg-gray-200 dark:active:bg-gray-700 rounded-md transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
7759
8019
|
timestamp: "text-xs text-gray-400 dark:text-gray-500 mr-1"
|
|
7760
8020
|
};
|
|
@@ -7788,6 +8048,7 @@ function AssistantMessage({
|
|
|
7788
8048
|
classNames: classNameOverrides,
|
|
7789
8049
|
onCopy,
|
|
7790
8050
|
onRegenerate,
|
|
8051
|
+
regenerateModels,
|
|
7791
8052
|
onSendMessage,
|
|
7792
8053
|
sendDisabled = false,
|
|
7793
8054
|
hideAvatar = false,
|
|
@@ -7798,6 +8059,8 @@ function AssistantMessage({
|
|
|
7798
8059
|
const { t } = useTranslation();
|
|
7799
8060
|
const [explanationExpanded, setExplanationExpanded] = React.useState(false);
|
|
7800
8061
|
const [isCopied, setIsCopied] = React.useState(false);
|
|
8062
|
+
const [showRegenPicker, setShowRegenPicker] = React.useState(false);
|
|
8063
|
+
const regenPickerRef = React.useRef(null);
|
|
7801
8064
|
const copyFeedbackTimeoutRef = React.useRef(
|
|
7802
8065
|
null
|
|
7803
8066
|
);
|
|
@@ -7850,11 +8113,56 @@ function AssistantMessage({
|
|
|
7850
8113
|
console.error("Failed to copy:", err);
|
|
7851
8114
|
}
|
|
7852
8115
|
}, [onCopy, turn.content]);
|
|
8116
|
+
const hasRegenChoices = (regenerateModels?.length ?? 0) >= 2;
|
|
7853
8117
|
const handleRegenerateClick = React.useCallback(async () => {
|
|
7854
|
-
if (onRegenerate
|
|
7855
|
-
|
|
8118
|
+
if (!onRegenerate || !turnId) {
|
|
8119
|
+
return;
|
|
7856
8120
|
}
|
|
7857
|
-
|
|
8121
|
+
if (hasRegenChoices) {
|
|
8122
|
+
setShowRegenPicker((prev) => !prev);
|
|
8123
|
+
return;
|
|
8124
|
+
}
|
|
8125
|
+
await onRegenerate(turnId);
|
|
8126
|
+
}, [onRegenerate, turnId, hasRegenChoices]);
|
|
8127
|
+
const handleRegenerateWithModel = React.useCallback(
|
|
8128
|
+
async (modelId) => {
|
|
8129
|
+
setShowRegenPicker(false);
|
|
8130
|
+
if (onRegenerate && turnId) {
|
|
8131
|
+
await onRegenerate(turnId, modelId);
|
|
8132
|
+
}
|
|
8133
|
+
},
|
|
8134
|
+
[onRegenerate, turnId]
|
|
8135
|
+
);
|
|
8136
|
+
React.useEffect(() => {
|
|
8137
|
+
if (!showRegenPicker) {
|
|
8138
|
+
return;
|
|
8139
|
+
}
|
|
8140
|
+
const handlePointer = (e) => {
|
|
8141
|
+
const root = regenPickerRef.current;
|
|
8142
|
+
if (!root) {
|
|
8143
|
+
return;
|
|
8144
|
+
}
|
|
8145
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
8146
|
+
const insideViaPath = path.includes(root);
|
|
8147
|
+
const insideViaContains = root.contains(e.target);
|
|
8148
|
+
if (!insideViaPath && !insideViaContains) {
|
|
8149
|
+
setShowRegenPicker(false);
|
|
8150
|
+
}
|
|
8151
|
+
};
|
|
8152
|
+
const handleKey = (e) => {
|
|
8153
|
+
if (e.key === "Escape") {
|
|
8154
|
+
setShowRegenPicker(false);
|
|
8155
|
+
}
|
|
8156
|
+
};
|
|
8157
|
+
document.addEventListener("mousedown", handlePointer);
|
|
8158
|
+
document.addEventListener("touchstart", handlePointer);
|
|
8159
|
+
document.addEventListener("keydown", handleKey);
|
|
8160
|
+
return () => {
|
|
8161
|
+
document.removeEventListener("mousedown", handlePointer);
|
|
8162
|
+
document.removeEventListener("touchstart", handlePointer);
|
|
8163
|
+
document.removeEventListener("keydown", handleKey);
|
|
8164
|
+
};
|
|
8165
|
+
}, [showRegenPicker]);
|
|
7858
8166
|
const timestamp = formatRelativeTime(turn.createdAt, t);
|
|
7859
8167
|
const avatarSlotProps = {
|
|
7860
8168
|
text: isSystemMessage ? "SYS" : "AI"
|
|
@@ -7881,7 +8189,17 @@ function AssistantMessage({
|
|
|
7881
8189
|
};
|
|
7882
8190
|
const actionsSlotProps = {
|
|
7883
8191
|
onCopy: handleCopyClick,
|
|
7884
|
-
onRegenerate: canRegenerate ?
|
|
8192
|
+
onRegenerate: canRegenerate ? (model) => {
|
|
8193
|
+
if (!onRegenerate || !turnId) {
|
|
8194
|
+
return;
|
|
8195
|
+
}
|
|
8196
|
+
if (model) {
|
|
8197
|
+
void handleRegenerateWithModel(model);
|
|
8198
|
+
} else {
|
|
8199
|
+
void handleRegenerateClick();
|
|
8200
|
+
}
|
|
8201
|
+
} : void 0,
|
|
8202
|
+
regenerateModels,
|
|
7885
8203
|
timestamp,
|
|
7886
8204
|
canCopy: hasContent,
|
|
7887
8205
|
canRegenerate
|
|
@@ -8072,16 +8390,56 @@ function AssistantMessage({
|
|
|
8072
8390
|
children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 14, weight: "regular" })
|
|
8073
8391
|
}
|
|
8074
8392
|
),
|
|
8075
|
-
canRegenerate && /* @__PURE__ */ jsxRuntime.
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8393
|
+
canRegenerate && /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: regenPickerRef, className: "relative inline-flex", children: [
|
|
8394
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8395
|
+
"button",
|
|
8396
|
+
{
|
|
8397
|
+
onClick: handleRegenerateClick,
|
|
8398
|
+
className: `cursor-pointer ${classes.actionButton} ${showRegenPicker ? "bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-200" : ""}`,
|
|
8399
|
+
"aria-label": t("BiChat.Message.Regenerate"),
|
|
8400
|
+
title: t("BiChat.Message.Regenerate"),
|
|
8401
|
+
"aria-haspopup": hasRegenChoices ? "menu" : void 0,
|
|
8402
|
+
"aria-expanded": hasRegenChoices ? showRegenPicker : void 0,
|
|
8403
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.ArrowsClockwise, { size: 14, weight: "regular" })
|
|
8404
|
+
}
|
|
8405
|
+
),
|
|
8406
|
+
hasRegenChoices && showRegenPicker && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8407
|
+
"div",
|
|
8408
|
+
{
|
|
8409
|
+
role: "menu",
|
|
8410
|
+
"aria-label": t("BiChat.Message.Regenerate"),
|
|
8411
|
+
className: "animate-slide-up absolute left-0 top-full z-20 mt-1 flex flex-col gap-0.5 rounded-lg border border-gray-200 bg-white p-1 shadow-lg dark:border-gray-700 dark:bg-gray-800",
|
|
8412
|
+
children: regenerateModels.map((m, i) => {
|
|
8413
|
+
const isFast = i === 0;
|
|
8414
|
+
const Icon = isFast ? react.Lightning : react.Brain;
|
|
8415
|
+
const accent = isFast ? "text-amber-600 dark:text-amber-400" : "text-blue-600 dark:text-blue-400";
|
|
8416
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8417
|
+
"button",
|
|
8418
|
+
{
|
|
8419
|
+
role: "menuitem",
|
|
8420
|
+
type: "button",
|
|
8421
|
+
onClick: () => {
|
|
8422
|
+
void handleRegenerateWithModel(m.id);
|
|
8423
|
+
},
|
|
8424
|
+
className: "flex items-center gap-2 whitespace-nowrap rounded-md px-2.5 py-1.5 text-xs font-medium text-gray-700 transition-colors duration-150 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700",
|
|
8425
|
+
children: [
|
|
8426
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8427
|
+
Icon,
|
|
8428
|
+
{
|
|
8429
|
+
size: 14,
|
|
8430
|
+
weight: "fill",
|
|
8431
|
+
className: accent
|
|
8432
|
+
}
|
|
8433
|
+
),
|
|
8434
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t(m.label) })
|
|
8435
|
+
]
|
|
8436
|
+
},
|
|
8437
|
+
m.id
|
|
8438
|
+
);
|
|
8439
|
+
})
|
|
8440
|
+
}
|
|
8441
|
+
)
|
|
8442
|
+
] })
|
|
8085
8443
|
] })
|
|
8086
8444
|
)
|
|
8087
8445
|
}
|
|
@@ -8241,6 +8599,14 @@ function AssistantTurnView({
|
|
|
8241
8599
|
}) {
|
|
8242
8600
|
const { debugMode } = useChatSession();
|
|
8243
8601
|
const { handleCopy, handleRegenerate, pendingQuestion, sendMessage: sendMessage2, loading } = useChatMessaging();
|
|
8602
|
+
const iotaContext = useIotaContext();
|
|
8603
|
+
const regenerateModels = React.useMemo(() => {
|
|
8604
|
+
const models = iotaContext.extensions?.llm?.models;
|
|
8605
|
+
if (!models || models.length < 2) {
|
|
8606
|
+
return void 0;
|
|
8607
|
+
}
|
|
8608
|
+
return models.map((m) => ({ id: m.id, label: m.label }));
|
|
8609
|
+
}, [iotaContext.extensions?.llm?.models]);
|
|
8244
8610
|
const assistantTurn = turn.assistantTurn;
|
|
8245
8611
|
if (!assistantTurn) {
|
|
8246
8612
|
return null;
|
|
@@ -8269,6 +8635,7 @@ function AssistantTurnView({
|
|
|
8269
8635
|
classNames,
|
|
8270
8636
|
onCopy: handleCopy,
|
|
8271
8637
|
onRegenerate: allowRegenerate ? handleRegenerate : void 0,
|
|
8638
|
+
regenerateModels: allowRegenerate ? regenerateModels : void 0,
|
|
8272
8639
|
onSendMessage: sendMessage2,
|
|
8273
8640
|
sendDisabled: loading || isStreaming,
|
|
8274
8641
|
hideAvatar,
|
|
@@ -10569,8 +10936,6 @@ function SessionArtifactList({
|
|
|
10569
10936
|
}) })
|
|
10570
10937
|
] }, group.type)) });
|
|
10571
10938
|
}
|
|
10572
|
-
|
|
10573
|
-
// ui/src/bichat/components/SessionArtifactPreviewModal.tsx
|
|
10574
10939
|
init_useTranslation();
|
|
10575
10940
|
|
|
10576
10941
|
// ui/src/bichat/components/SessionArtifactPreview.tsx
|
|
@@ -10960,9 +11325,9 @@ function SessionArtifactPreviewModal({
|
|
|
10960
11325
|
if (!artifact) {
|
|
10961
11326
|
return null;
|
|
10962
11327
|
}
|
|
10963
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10964
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10965
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 overflow-y-auto p-4 lg:p-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex min-h-full w-full max-w-6xl items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11328
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(InlineDialog, { open: isOpen, onClose: handleClose, className: "relative z-50", children: [
|
|
11329
|
+
/* @__PURE__ */ jsxRuntime.jsx(InlineDialogBackdrop, { className: "fixed inset-0 bg-black/50 backdrop-blur-sm" }),
|
|
11330
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 overflow-y-auto p-4 lg:p-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex min-h-full w-full max-w-6xl items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs(InlineDialogPanel, { className: "flex max-h-[92vh] w-full flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-xl dark:border-gray-700 dark:bg-gray-900", children: [
|
|
10966
11331
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-gray-200 px-4 py-3 dark:border-gray-700", children: [
|
|
10967
11332
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
10968
11333
|
isEditingName ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -11462,109 +11827,6 @@ function SessionArtifactsPanel({
|
|
|
11462
11827
|
}
|
|
11463
11828
|
);
|
|
11464
11829
|
}
|
|
11465
|
-
var DialogContext = React.createContext(null);
|
|
11466
|
-
var FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
11467
|
-
function InlineDialog({ open, onClose, className, children }) {
|
|
11468
|
-
const containerRef = React.useRef(null);
|
|
11469
|
-
const previousFocusRef = React.useRef(null);
|
|
11470
|
-
React.useEffect(() => {
|
|
11471
|
-
if (!open) {
|
|
11472
|
-
return;
|
|
11473
|
-
}
|
|
11474
|
-
previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
11475
|
-
const container = containerRef.current;
|
|
11476
|
-
if (!container) {
|
|
11477
|
-
return;
|
|
11478
|
-
}
|
|
11479
|
-
const getFocusable = () => Array.from(
|
|
11480
|
-
container.querySelectorAll(FOCUSABLE_SELECTOR)
|
|
11481
|
-
);
|
|
11482
|
-
const handler = (e) => {
|
|
11483
|
-
if (e.key === "Escape") {
|
|
11484
|
-
onClose();
|
|
11485
|
-
return;
|
|
11486
|
-
}
|
|
11487
|
-
if (e.key !== "Tab") {
|
|
11488
|
-
return;
|
|
11489
|
-
}
|
|
11490
|
-
const focusable = getFocusable();
|
|
11491
|
-
if (focusable.length === 0) {
|
|
11492
|
-
e.preventDefault();
|
|
11493
|
-
container.focus();
|
|
11494
|
-
return;
|
|
11495
|
-
}
|
|
11496
|
-
const first = focusable[0];
|
|
11497
|
-
const last = focusable[focusable.length - 1];
|
|
11498
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
11499
|
-
e.preventDefault();
|
|
11500
|
-
last.focus();
|
|
11501
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
11502
|
-
e.preventDefault();
|
|
11503
|
-
first.focus();
|
|
11504
|
-
}
|
|
11505
|
-
};
|
|
11506
|
-
(getFocusable()[0] ?? container)?.focus();
|
|
11507
|
-
container.addEventListener("keydown", handler);
|
|
11508
|
-
return () => {
|
|
11509
|
-
container.removeEventListener("keydown", handler);
|
|
11510
|
-
previousFocusRef.current?.focus();
|
|
11511
|
-
};
|
|
11512
|
-
}, [open, onClose]);
|
|
11513
|
-
if (!open) {
|
|
11514
|
-
return null;
|
|
11515
|
-
}
|
|
11516
|
-
return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11517
|
-
"div",
|
|
11518
|
-
{
|
|
11519
|
-
ref: containerRef,
|
|
11520
|
-
className,
|
|
11521
|
-
onClick: onClose,
|
|
11522
|
-
tabIndex: -1,
|
|
11523
|
-
children
|
|
11524
|
-
}
|
|
11525
|
-
) });
|
|
11526
|
-
}
|
|
11527
|
-
function InlineDialogBackdrop(props) {
|
|
11528
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", ...props });
|
|
11529
|
-
}
|
|
11530
|
-
function InlineDialogPanel({
|
|
11531
|
-
children,
|
|
11532
|
-
onClick,
|
|
11533
|
-
...rest
|
|
11534
|
-
}) {
|
|
11535
|
-
const ref = React.useRef(null);
|
|
11536
|
-
React.useEffect(() => {
|
|
11537
|
-
if (!ref.current) {
|
|
11538
|
-
return;
|
|
11539
|
-
}
|
|
11540
|
-
const target = ref.current.querySelector("[data-autofocus]") ?? ref.current.querySelector(
|
|
11541
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
11542
|
-
);
|
|
11543
|
-
target?.focus();
|
|
11544
|
-
}, []);
|
|
11545
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11546
|
-
"div",
|
|
11547
|
-
{
|
|
11548
|
-
ref,
|
|
11549
|
-
role: "dialog",
|
|
11550
|
-
"aria-modal": "true",
|
|
11551
|
-
onClick: (e) => {
|
|
11552
|
-
e.stopPropagation();
|
|
11553
|
-
onClick?.(e);
|
|
11554
|
-
},
|
|
11555
|
-
...rest,
|
|
11556
|
-
children
|
|
11557
|
-
}
|
|
11558
|
-
);
|
|
11559
|
-
}
|
|
11560
|
-
function InlineDialogTitle(props) {
|
|
11561
|
-
return /* @__PURE__ */ jsxRuntime.jsx("h2", { ...props });
|
|
11562
|
-
}
|
|
11563
|
-
function InlineDialogDescription(props) {
|
|
11564
|
-
return /* @__PURE__ */ jsxRuntime.jsx("p", { ...props });
|
|
11565
|
-
}
|
|
11566
|
-
|
|
11567
|
-
// ui/src/bichat/components/SessionMembersModal.tsx
|
|
11568
11830
|
init_useTranslation();
|
|
11569
11831
|
init_useTranslation();
|
|
11570
11832
|
function ConfirmModalBase({
|
|
@@ -12451,7 +12713,7 @@ function ChatSessionCore({
|
|
|
12451
12713
|
"div",
|
|
12452
12714
|
{
|
|
12453
12715
|
ref: layoutContainerRef,
|
|
12454
|
-
className: "relative flex min-h-0 flex-1 overflow-
|
|
12716
|
+
className: "relative flex min-h-0 flex-1 overflow-clip",
|
|
12455
12717
|
children: [
|
|
12456
12718
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: showWelcome ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center px-4 py-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-5xl", children: [
|
|
12457
12719
|
welcomeSlot || /* @__PURE__ */ jsxRuntime.jsx(WelcomeContent_default, { onPromptSelect: handlePromptSelect, disabled: loading || composerDisabled }),
|
|
@@ -15903,61 +16165,6 @@ function useSidebarState() {
|
|
|
15903
16165
|
const toggleMobile = React.useCallback(() => setIsMobileOpen((v) => !v), []);
|
|
15904
16166
|
return { isMobile, isMobileOpen, openMobile, closeMobile, toggleMobile };
|
|
15905
16167
|
}
|
|
15906
|
-
function useFocusTrap(containerRef, isActive, restoreFocusOnDeactivate) {
|
|
15907
|
-
React.useEffect(() => {
|
|
15908
|
-
if (!isActive || !containerRef.current) {
|
|
15909
|
-
return;
|
|
15910
|
-
}
|
|
15911
|
-
const container = containerRef.current;
|
|
15912
|
-
const previouslyFocused = document.activeElement;
|
|
15913
|
-
const getFocusableElements = () => {
|
|
15914
|
-
const selector = [
|
|
15915
|
-
"button:not([disabled])",
|
|
15916
|
-
"[href]",
|
|
15917
|
-
"input:not([disabled])",
|
|
15918
|
-
"select:not([disabled])",
|
|
15919
|
-
"textarea:not([disabled])",
|
|
15920
|
-
'[tabindex]:not([tabindex="-1"])'
|
|
15921
|
-
].join(", ");
|
|
15922
|
-
return Array.from(container.querySelectorAll(selector));
|
|
15923
|
-
};
|
|
15924
|
-
const focusableElements = getFocusableElements();
|
|
15925
|
-
if (focusableElements.length > 0) {
|
|
15926
|
-
focusableElements[0].focus();
|
|
15927
|
-
}
|
|
15928
|
-
const handleTabKey = (e) => {
|
|
15929
|
-
if (e.key !== "Tab") {
|
|
15930
|
-
return;
|
|
15931
|
-
}
|
|
15932
|
-
const focusableElements2 = getFocusableElements();
|
|
15933
|
-
if (focusableElements2.length === 0) {
|
|
15934
|
-
return;
|
|
15935
|
-
}
|
|
15936
|
-
const firstElement = focusableElements2[0];
|
|
15937
|
-
const lastElement = focusableElements2[focusableElements2.length - 1];
|
|
15938
|
-
if (e.shiftKey) {
|
|
15939
|
-
if (document.activeElement === firstElement) {
|
|
15940
|
-
e.preventDefault();
|
|
15941
|
-
lastElement.focus();
|
|
15942
|
-
}
|
|
15943
|
-
} else {
|
|
15944
|
-
if (document.activeElement === lastElement) {
|
|
15945
|
-
e.preventDefault();
|
|
15946
|
-
firstElement.focus();
|
|
15947
|
-
}
|
|
15948
|
-
}
|
|
15949
|
-
};
|
|
15950
|
-
container.addEventListener("keydown", handleTabKey);
|
|
15951
|
-
return () => {
|
|
15952
|
-
container.removeEventListener("keydown", handleTabKey);
|
|
15953
|
-
if (restoreFocusOnDeactivate) {
|
|
15954
|
-
restoreFocusOnDeactivate.focus();
|
|
15955
|
-
} else if (previouslyFocused instanceof HTMLElement) {
|
|
15956
|
-
previouslyFocused.focus();
|
|
15957
|
-
}
|
|
15958
|
-
};
|
|
15959
|
-
}, [containerRef, isActive, restoreFocusOnDeactivate]);
|
|
15960
|
-
}
|
|
15961
16168
|
|
|
15962
16169
|
// ui/src/bichat/components/BiChatLayout.tsx
|
|
15963
16170
|
init_useTranslation();
|
|
@@ -17207,18 +17414,6 @@ function useActiveRuns(dataSource, options = {}) {
|
|
|
17207
17414
|
|
|
17208
17415
|
// ui/src/bichat/index.ts
|
|
17209
17416
|
init_useTranslation();
|
|
17210
|
-
function useModalLock(isOpen) {
|
|
17211
|
-
React.useEffect(() => {
|
|
17212
|
-
if (!isOpen) {
|
|
17213
|
-
return;
|
|
17214
|
-
}
|
|
17215
|
-
const originalOverflow = document.body.style.overflow;
|
|
17216
|
-
document.body.style.overflow = "hidden";
|
|
17217
|
-
return () => {
|
|
17218
|
-
document.body.style.overflow = originalOverflow;
|
|
17219
|
-
};
|
|
17220
|
-
}, [isOpen]);
|
|
17221
|
-
}
|
|
17222
17417
|
function useImageGallery(options = {}) {
|
|
17223
17418
|
const { images: initialImages = [], wrap = false, onOpen, onClose, onNavigate } = options;
|
|
17224
17419
|
const [isOpen, setIsOpen] = React.useState(false);
|