@xinghunm/ai-chat 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/index.d.mts +63 -6
- package/dist/index.d.ts +63 -6
- package/dist/index.js +391 -154
- package/dist/index.mjs +383 -146
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
47
47
|
|
|
48
48
|
// src/components/ai-chat/index.tsx
|
|
49
49
|
var import_styled17 = __toESM(require("@emotion/styled"));
|
|
50
|
-
var
|
|
50
|
+
var import_compass_ui4 = require("@xinghunm/compass-ui");
|
|
51
51
|
|
|
52
52
|
// src/components/ai-chat-provider/index.tsx
|
|
53
53
|
var import_react2 = require("react");
|
|
@@ -96,6 +96,28 @@ var DEFAULT_AI_CHAT_LABELS = {
|
|
|
96
96
|
questionnaireOtherPlaceholder: "Other"
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
+
// src/lib/chat-session.ts
|
|
100
|
+
var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
|
|
101
|
+
var draftChatSessionSequence = 0;
|
|
102
|
+
var createDraftChatSessionId = () => `${DRAFT_CHAT_SESSION_ID_PREFIX}${Date.now()}-${draftChatSessionSequence++}`;
|
|
103
|
+
var isDraftChatSessionId = (sessionId) => Boolean(sessionId?.startsWith(DRAFT_CHAT_SESSION_ID_PREFIX));
|
|
104
|
+
var createDraftChatSession = ({
|
|
105
|
+
model,
|
|
106
|
+
mode = DEFAULT_CHAT_AGENT_MODE,
|
|
107
|
+
nowIso: nowIso2,
|
|
108
|
+
createSessionId = createDraftChatSessionId
|
|
109
|
+
}) => {
|
|
110
|
+
const iso = nowIso2();
|
|
111
|
+
return {
|
|
112
|
+
sessionId: createSessionId(),
|
|
113
|
+
title: "New Chat",
|
|
114
|
+
createdAt: iso,
|
|
115
|
+
updatedAt: iso,
|
|
116
|
+
model,
|
|
117
|
+
mode
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
99
121
|
// src/store/chat-store.ts
|
|
100
122
|
var DEFAULT_CHAT_SESSION_TITLE = "New Chat";
|
|
101
123
|
var IMAGE_MESSAGE_SESSION_TITLE = "Image message";
|
|
@@ -229,6 +251,15 @@ var createChatStore = (initialState) => (0, import_vanilla.createStore)((set, ge
|
|
|
229
251
|
isStoppingBySession: nextIsStoppingBySession
|
|
230
252
|
});
|
|
231
253
|
},
|
|
254
|
+
startNewChat: () => {
|
|
255
|
+
const state = get();
|
|
256
|
+
const session = createDraftChatSession({
|
|
257
|
+
model: "",
|
|
258
|
+
mode: state.preferredMode,
|
|
259
|
+
nowIso: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
260
|
+
});
|
|
261
|
+
get().createSession(session);
|
|
262
|
+
},
|
|
232
263
|
setActiveSession: (sessionId) => {
|
|
233
264
|
set({ activeSessionId: sessionId });
|
|
234
265
|
},
|
|
@@ -491,6 +522,7 @@ var startChatStream = async ({
|
|
|
491
522
|
sessionId,
|
|
492
523
|
authToken,
|
|
493
524
|
requestHeaders,
|
|
525
|
+
requestBody,
|
|
494
526
|
model,
|
|
495
527
|
mode,
|
|
496
528
|
content,
|
|
@@ -512,12 +544,14 @@ var startChatStream = async ({
|
|
|
512
544
|
const response = await fetch(`${apiBaseUrl}${endpointPath}`, {
|
|
513
545
|
method: "POST",
|
|
514
546
|
headers,
|
|
515
|
-
body: JSON.stringify(
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
547
|
+
body: JSON.stringify(
|
|
548
|
+
requestBody ?? {
|
|
549
|
+
model,
|
|
550
|
+
mode,
|
|
551
|
+
stream: true,
|
|
552
|
+
messages: [{ role: "user", content }]
|
|
553
|
+
}
|
|
554
|
+
),
|
|
521
555
|
signal
|
|
522
556
|
});
|
|
523
557
|
const contentType = response.headers.get("content-type") ?? "";
|
|
@@ -593,11 +627,70 @@ var createModeDefaultHeaders = (mode) => {
|
|
|
593
627
|
}
|
|
594
628
|
return {};
|
|
595
629
|
};
|
|
630
|
+
var readFileAsDataUrl = (file) => new Promise((resolve, reject) => {
|
|
631
|
+
const reader = new FileReader();
|
|
632
|
+
reader.onload = () => {
|
|
633
|
+
if (typeof reader.result === "string") {
|
|
634
|
+
resolve(reader.result);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
638
|
+
};
|
|
639
|
+
reader.onerror = () => {
|
|
640
|
+
reject(new Error(`Failed to read image attachment: ${file.name}`));
|
|
641
|
+
};
|
|
642
|
+
reader.readAsDataURL(file);
|
|
643
|
+
});
|
|
644
|
+
var resolveAttachmentDataUrl = async (attachment) => {
|
|
645
|
+
if (attachment.file) {
|
|
646
|
+
return readFileAsDataUrl(attachment.file);
|
|
647
|
+
}
|
|
648
|
+
if (attachment.previewUrl.startsWith("data:image/")) {
|
|
649
|
+
return attachment.previewUrl;
|
|
650
|
+
}
|
|
651
|
+
throw new Error(`Attachment is missing file data: ${attachment.name}`);
|
|
652
|
+
};
|
|
653
|
+
var createDefaultRequestBody = async ({
|
|
654
|
+
model,
|
|
655
|
+
mode,
|
|
656
|
+
content,
|
|
657
|
+
attachments
|
|
658
|
+
}) => {
|
|
659
|
+
const hasAttachments = Boolean(attachments?.length);
|
|
660
|
+
if (!hasAttachments) {
|
|
661
|
+
return {
|
|
662
|
+
model,
|
|
663
|
+
mode,
|
|
664
|
+
stream: true,
|
|
665
|
+
messages: [{ role: "user", content }]
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
const imageParts = await Promise.all(
|
|
669
|
+
(attachments ?? []).map(async (attachment) => ({
|
|
670
|
+
type: "image_url",
|
|
671
|
+
image_url: {
|
|
672
|
+
url: await resolveAttachmentDataUrl(attachment)
|
|
673
|
+
}
|
|
674
|
+
}))
|
|
675
|
+
);
|
|
676
|
+
const messageContent = [
|
|
677
|
+
...content ? [{ type: "text", text: content }] : [],
|
|
678
|
+
...imageParts
|
|
679
|
+
];
|
|
680
|
+
return {
|
|
681
|
+
model,
|
|
682
|
+
mode,
|
|
683
|
+
stream: true,
|
|
684
|
+
messages: [{ role: "user", content: messageContent }]
|
|
685
|
+
};
|
|
686
|
+
};
|
|
596
687
|
var createDefaultChatTransport = ({
|
|
597
688
|
apiBaseUrl,
|
|
598
689
|
authToken,
|
|
599
690
|
toolExecutionPolicy,
|
|
600
691
|
streamHeaders,
|
|
692
|
+
resolveModels,
|
|
693
|
+
buildRequestBody,
|
|
601
694
|
transformStreamPacket,
|
|
602
695
|
endpoints,
|
|
603
696
|
axiosInstance
|
|
@@ -612,12 +705,13 @@ var createDefaultChatTransport = ({
|
|
|
612
705
|
...streamHeaders
|
|
613
706
|
};
|
|
614
707
|
return {
|
|
615
|
-
getModels: () => getChatModels(client, resolvedEndpoints.models),
|
|
708
|
+
getModels: () => resolveModels?.() ?? getChatModels(client, resolvedEndpoints.models),
|
|
616
709
|
startStream: async ({
|
|
617
710
|
sessionId,
|
|
618
711
|
model,
|
|
619
712
|
mode,
|
|
620
713
|
content,
|
|
714
|
+
attachments,
|
|
621
715
|
signal,
|
|
622
716
|
onUpdate,
|
|
623
717
|
onSessionId,
|
|
@@ -628,12 +722,20 @@ var createDefaultChatTransport = ({
|
|
|
628
722
|
...createModeDefaultHeaders(mode),
|
|
629
723
|
...resolvedStreamHeaders
|
|
630
724
|
};
|
|
725
|
+
const requestBody = buildRequestBody ? buildRequestBody({
|
|
726
|
+
sessionId,
|
|
727
|
+
model,
|
|
728
|
+
mode,
|
|
729
|
+
content,
|
|
730
|
+
attachments
|
|
731
|
+
}) : await createDefaultRequestBody({ model, mode, content, attachments });
|
|
631
732
|
await startChatStream({
|
|
632
733
|
apiBaseUrl,
|
|
633
734
|
endpointPath: resolvedEndpoints.completions,
|
|
634
735
|
sessionId,
|
|
635
736
|
authToken,
|
|
636
737
|
requestHeaders,
|
|
738
|
+
requestBody,
|
|
637
739
|
model,
|
|
638
740
|
mode,
|
|
639
741
|
content,
|
|
@@ -675,6 +777,8 @@ var AiChatProvider = (props) => {
|
|
|
675
777
|
});
|
|
676
778
|
const retryRef = (0, import_react2.useRef)(async () => {
|
|
677
779
|
});
|
|
780
|
+
const stopRef = (0, import_react2.useRef)(async (_sessionId) => {
|
|
781
|
+
});
|
|
678
782
|
const defaultApiBaseUrl = "apiBaseUrl" in props ? props.apiBaseUrl : void 0;
|
|
679
783
|
const defaultAuthToken = "authToken" in props ? props.authToken : void 0;
|
|
680
784
|
const defaultTransformStreamPacket = "transformStreamPacket" in props ? props.transformStreamPacket : void 0;
|
|
@@ -718,6 +822,7 @@ var AiChatProvider = (props) => {
|
|
|
718
822
|
labels: { ...DEFAULT_AI_CHAT_LABELS, ...labels },
|
|
719
823
|
sendRef,
|
|
720
824
|
retryRef,
|
|
825
|
+
stopRef,
|
|
721
826
|
renderMessageBlock,
|
|
722
827
|
handleQuestionnaireSubmit,
|
|
723
828
|
handleConfirmationSubmit,
|
|
@@ -738,6 +843,7 @@ var AiChatProvider = (props) => {
|
|
|
738
843
|
renderMessageBlock,
|
|
739
844
|
sendRef,
|
|
740
845
|
retryRef,
|
|
846
|
+
stopRef,
|
|
741
847
|
store,
|
|
742
848
|
transport
|
|
743
849
|
]
|
|
@@ -1538,7 +1644,6 @@ var Value = import_styled3.default.span`
|
|
|
1538
1644
|
// src/components/chat-thread/components/questionnaire-card.tsx
|
|
1539
1645
|
var import_react7 = require("react");
|
|
1540
1646
|
var import_styled4 = __toESM(require("@emotion/styled"));
|
|
1541
|
-
var import_compass_ui = require("@xinghunm/compass-ui");
|
|
1542
1647
|
|
|
1543
1648
|
// src/components/chat-thread/components/questionnaire-card-helpers.ts
|
|
1544
1649
|
var OTHER_OPTION_VALUE = "__other__";
|
|
@@ -2339,7 +2444,7 @@ var TextInput = import_styled4.default.input`
|
|
|
2339
2444
|
color: rgba(255, 255, 255, 0.34);
|
|
2340
2445
|
}
|
|
2341
2446
|
`;
|
|
2342
|
-
var InlineOtherInput =
|
|
2447
|
+
var InlineOtherInput = import_styled4.default.input`
|
|
2343
2448
|
width: 100%;
|
|
2344
2449
|
margin-top: 0;
|
|
2345
2450
|
|
|
@@ -3727,19 +3832,20 @@ var ChatThread = () => {
|
|
|
3727
3832
|
if (!activeSessionId)
|
|
3728
3833
|
return;
|
|
3729
3834
|
clearSessionError(activeSessionId);
|
|
3730
|
-
void retryRef.current();
|
|
3835
|
+
void retryRef.current(activeSessionId);
|
|
3731
3836
|
}, [activeSessionId, clearSessionError, retryRef]);
|
|
3732
3837
|
const handleQuestionnaireSubmit = (0, import_react11.useCallback)(
|
|
3733
3838
|
async (submission) => {
|
|
3839
|
+
const sourceSessionId = activeSessionId;
|
|
3734
3840
|
if (customQuestionnaireSubmit) {
|
|
3735
3841
|
const handled = await customQuestionnaireSubmit(submission, {
|
|
3736
|
-
sessionId:
|
|
3842
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3737
3843
|
mode: activeSessionMode
|
|
3738
3844
|
});
|
|
3739
3845
|
if (handled !== false) {
|
|
3740
|
-
if (
|
|
3846
|
+
if (sourceSessionId && submission.sourceMessageId) {
|
|
3741
3847
|
updateQA(
|
|
3742
|
-
|
|
3848
|
+
sourceSessionId,
|
|
3743
3849
|
submission.sourceMessageId,
|
|
3744
3850
|
submission.questionnaireId,
|
|
3745
3851
|
submission.answers
|
|
@@ -3748,10 +3854,13 @@ var ChatThread = () => {
|
|
|
3748
3854
|
return;
|
|
3749
3855
|
}
|
|
3750
3856
|
}
|
|
3751
|
-
await sendRef.current(submission.content
|
|
3752
|
-
|
|
3857
|
+
await sendRef.current(submission.content, {
|
|
3858
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3859
|
+
includeComposerAttachments: false
|
|
3860
|
+
});
|
|
3861
|
+
if (sourceSessionId && submission.sourceMessageId) {
|
|
3753
3862
|
updateQA(
|
|
3754
|
-
|
|
3863
|
+
sourceSessionId,
|
|
3755
3864
|
submission.sourceMessageId,
|
|
3756
3865
|
submission.questionnaireId,
|
|
3757
3866
|
submission.answers
|
|
@@ -3762,16 +3871,20 @@ var ChatThread = () => {
|
|
|
3762
3871
|
);
|
|
3763
3872
|
const handleConfirmation = (0, import_react11.useCallback)(
|
|
3764
3873
|
async (submission) => {
|
|
3874
|
+
const sourceSessionId = activeSessionId;
|
|
3765
3875
|
if (customConfirmationSubmit) {
|
|
3766
3876
|
const handled = await customConfirmationSubmit(submission, {
|
|
3767
|
-
sessionId:
|
|
3877
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3768
3878
|
mode: activeSessionMode
|
|
3769
3879
|
});
|
|
3770
3880
|
if (handled !== false) {
|
|
3771
3881
|
return;
|
|
3772
3882
|
}
|
|
3773
3883
|
}
|
|
3774
|
-
await sendRef.current(submission.content
|
|
3884
|
+
await sendRef.current(submission.content, {
|
|
3885
|
+
sessionId: sourceSessionId ?? void 0,
|
|
3886
|
+
includeComposerAttachments: false
|
|
3887
|
+
});
|
|
3775
3888
|
},
|
|
3776
3889
|
[activeSessionId, activeSessionMode, sendRef, customConfirmationSubmit]
|
|
3777
3890
|
);
|
|
@@ -3908,25 +4021,6 @@ var import_react15 = require("react");
|
|
|
3908
4021
|
var import_styled14 = __toESM(require("@emotion/styled"));
|
|
3909
4022
|
|
|
3910
4023
|
// src/components/chat-composer/lib/chat-composer.ts
|
|
3911
|
-
var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
|
|
3912
|
-
var createDraftChatSessionId = () => `${DRAFT_CHAT_SESSION_ID_PREFIX}${Date.now()}`;
|
|
3913
|
-
var isDraftChatSessionId = (sessionId) => Boolean(sessionId?.startsWith(DRAFT_CHAT_SESSION_ID_PREFIX));
|
|
3914
|
-
var createDraftChatSession = ({
|
|
3915
|
-
model,
|
|
3916
|
-
mode = DEFAULT_CHAT_AGENT_MODE,
|
|
3917
|
-
nowIso: nowIso2,
|
|
3918
|
-
createSessionId
|
|
3919
|
-
}) => {
|
|
3920
|
-
const iso = nowIso2();
|
|
3921
|
-
return {
|
|
3922
|
-
sessionId: createSessionId(),
|
|
3923
|
-
title: "New Chat",
|
|
3924
|
-
createdAt: iso,
|
|
3925
|
-
updatedAt: iso,
|
|
3926
|
-
model,
|
|
3927
|
-
mode
|
|
3928
|
-
};
|
|
3929
|
-
};
|
|
3930
4024
|
var createUserMessage = ({
|
|
3931
4025
|
sessionId,
|
|
3932
4026
|
content,
|
|
@@ -4087,7 +4181,8 @@ var useComposerAttachments = () => {
|
|
|
4087
4181
|
return [];
|
|
4088
4182
|
}
|
|
4089
4183
|
const nextMessageAttachments = currentAttachments.map(({ file: _file, ...attachment }) => ({
|
|
4090
|
-
...attachment
|
|
4184
|
+
...attachment,
|
|
4185
|
+
file: _file
|
|
4091
4186
|
}));
|
|
4092
4187
|
attachmentsRef.current = [];
|
|
4093
4188
|
setAttachments([]);
|
|
@@ -4126,19 +4221,20 @@ var normalizeChatErrorMessage = (message, labels) => {
|
|
|
4126
4221
|
return trimmedMessage;
|
|
4127
4222
|
};
|
|
4128
4223
|
var useChatComposer = () => {
|
|
4129
|
-
const { transport, enableImageAttachments, labels } = useChatContext();
|
|
4224
|
+
const { transport, enableImageAttachments, labels, store } = useChatContext();
|
|
4130
4225
|
const activeSessionId = useChatStore((s) => s.activeSessionId);
|
|
4131
4226
|
const activeSession = useChatStore(
|
|
4132
4227
|
(s) => s.sessions.find((x) => x.sessionId === s.activeSessionId) ?? null
|
|
4133
4228
|
);
|
|
4134
4229
|
const preferredMode = useChatStore((s) => s.preferredMode);
|
|
4135
4230
|
const streamingSessionId = useChatStore(
|
|
4136
|
-
(s) =>
|
|
4231
|
+
(s) => s.activeSessionId && s.isStreamingBySession[s.activeSessionId] ? s.activeSessionId : null
|
|
4137
4232
|
);
|
|
4138
4233
|
const isStreaming = Boolean(streamingSessionId);
|
|
4139
|
-
const isStopping = useChatStore(
|
|
4140
|
-
|
|
4141
|
-
|
|
4234
|
+
const isStopping = useChatStore((s) => {
|
|
4235
|
+
const currentStreamingSessionId = s.activeSessionId && s.isStreamingBySession[s.activeSessionId] ? s.activeSessionId : null;
|
|
4236
|
+
return currentStreamingSessionId ? s.isStoppingBySession[currentStreamingSessionId] ?? false : false;
|
|
4237
|
+
});
|
|
4142
4238
|
const createSession = useChatStore((s) => s.createSession);
|
|
4143
4239
|
const replaceSessionId = useChatStore((s) => s.replaceSessionId);
|
|
4144
4240
|
const appendMessage = useChatStore((s) => s.appendMessage);
|
|
@@ -4175,9 +4271,9 @@ var useChatComposer = () => {
|
|
|
4175
4271
|
const [selectedMode, setSelectedModeLocal] = (0, import_react13.useState)(DEFAULT_CHAT_AGENT_MODE);
|
|
4176
4272
|
const [attachmentNotice, setAttachmentNotice] = (0, import_react13.useState)(null);
|
|
4177
4273
|
const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
|
|
4178
|
-
const
|
|
4179
|
-
const
|
|
4180
|
-
const
|
|
4274
|
+
const abortControllerBySessionRef = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
|
|
4275
|
+
const stopRequestBySessionRef = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
|
|
4276
|
+
const lastRequestBySessionRef = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
|
|
4181
4277
|
(0, import_react13.useEffect)(() => {
|
|
4182
4278
|
setSelectedModel(
|
|
4183
4279
|
(current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
|
|
@@ -4200,33 +4296,47 @@ var useChatComposer = () => {
|
|
|
4200
4296
|
return () => window.clearTimeout(timeoutId);
|
|
4201
4297
|
}, [attachmentNotice]);
|
|
4202
4298
|
const clearStopTimeout = (sessionId) => {
|
|
4203
|
-
|
|
4299
|
+
const stopRequest = stopRequestBySessionRef.current.get(sessionId);
|
|
4300
|
+
if (!stopRequest || stopRequest.timeoutId === null) {
|
|
4204
4301
|
return;
|
|
4205
|
-
if (sessionId && stopRequestRef.current.sessionId !== sessionId)
|
|
4206
|
-
return;
|
|
4207
|
-
if (stopRequestRef.current.timeoutId !== null) {
|
|
4208
|
-
window.clearTimeout(stopRequestRef.current.timeoutId);
|
|
4209
|
-
stopRequestRef.current.timeoutId = null;
|
|
4210
4302
|
}
|
|
4303
|
+
window.clearTimeout(stopRequest.timeoutId);
|
|
4304
|
+
stopRequest.timeoutId = null;
|
|
4211
4305
|
};
|
|
4212
4306
|
const clearStopRequest = (0, import_react13.useCallback)((sessionId) => {
|
|
4213
|
-
if (!stopRequestRef.current)
|
|
4214
|
-
return;
|
|
4215
|
-
if (sessionId && stopRequestRef.current.sessionId !== sessionId)
|
|
4216
|
-
return;
|
|
4217
4307
|
clearStopTimeout(sessionId);
|
|
4218
|
-
|
|
4308
|
+
stopRequestBySessionRef.current.delete(sessionId);
|
|
4219
4309
|
}, []);
|
|
4310
|
+
const moveSessionRuntimeState = (0, import_react13.useCallback)(
|
|
4311
|
+
(previousSessionId, nextSessionId) => {
|
|
4312
|
+
if (previousSessionId === nextSessionId) {
|
|
4313
|
+
return;
|
|
4314
|
+
}
|
|
4315
|
+
const abortController = abortControllerBySessionRef.current.get(previousSessionId);
|
|
4316
|
+
if (abortController) {
|
|
4317
|
+
abortControllerBySessionRef.current.set(nextSessionId, abortController);
|
|
4318
|
+
abortControllerBySessionRef.current.delete(previousSessionId);
|
|
4319
|
+
}
|
|
4320
|
+
const stopRequest = stopRequestBySessionRef.current.get(previousSessionId);
|
|
4321
|
+
if (stopRequest) {
|
|
4322
|
+
stopRequestBySessionRef.current.set(nextSessionId, stopRequest);
|
|
4323
|
+
stopRequestBySessionRef.current.delete(previousSessionId);
|
|
4324
|
+
}
|
|
4325
|
+
},
|
|
4326
|
+
[]
|
|
4327
|
+
);
|
|
4220
4328
|
const finalizeStop = (0, import_react13.useCallback)(
|
|
4221
4329
|
(sessionId) => {
|
|
4222
|
-
|
|
4223
|
-
|
|
4330
|
+
const stopRequest = stopRequestBySessionRef.current.get(sessionId);
|
|
4331
|
+
if (stopRequest) {
|
|
4332
|
+
if (stopRequest.finalized) {
|
|
4224
4333
|
return;
|
|
4225
|
-
|
|
4334
|
+
}
|
|
4335
|
+
stopRequest.finalized = true;
|
|
4226
4336
|
}
|
|
4227
4337
|
clearStopTimeout(sessionId);
|
|
4228
|
-
|
|
4229
|
-
|
|
4338
|
+
abortControllerBySessionRef.current.get(sessionId)?.abort();
|
|
4339
|
+
abortControllerBySessionRef.current.delete(sessionId);
|
|
4230
4340
|
finalizeStoppedStreamingMessage(sessionId);
|
|
4231
4341
|
clearStopRequest(sessionId);
|
|
4232
4342
|
},
|
|
@@ -4237,10 +4347,11 @@ var useChatComposer = () => {
|
|
|
4237
4347
|
localSessionId,
|
|
4238
4348
|
sessionId,
|
|
4239
4349
|
content,
|
|
4350
|
+
attachments: attachments2,
|
|
4240
4351
|
model,
|
|
4241
4352
|
mode
|
|
4242
4353
|
}) => {
|
|
4243
|
-
clearStopRequest();
|
|
4354
|
+
clearStopRequest(localSessionId);
|
|
4244
4355
|
let currentSessionId = localSessionId;
|
|
4245
4356
|
clearSessionError(currentSessionId);
|
|
4246
4357
|
const assistantMessage = createAssistantStreamingMessage({
|
|
@@ -4249,29 +4360,44 @@ var useChatComposer = () => {
|
|
|
4249
4360
|
createMessageId: () => `assistant-${Date.now()}`
|
|
4250
4361
|
});
|
|
4251
4362
|
startStreamingMessage(currentSessionId, assistantMessage);
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4363
|
+
abortControllerBySessionRef.current.get(currentSessionId)?.abort();
|
|
4364
|
+
const abortController = new AbortController();
|
|
4365
|
+
abortControllerBySessionRef.current.set(currentSessionId, abortController);
|
|
4366
|
+
lastRequestBySessionRef.current.set(currentSessionId, {
|
|
4367
|
+
localSessionId,
|
|
4368
|
+
sessionId,
|
|
4369
|
+
content,
|
|
4370
|
+
attachments: attachments2,
|
|
4371
|
+
model,
|
|
4372
|
+
mode
|
|
4373
|
+
});
|
|
4255
4374
|
let accumulated = "";
|
|
4375
|
+
let streamSettled = false;
|
|
4256
4376
|
try {
|
|
4257
4377
|
await transport.startStream({
|
|
4258
4378
|
sessionId,
|
|
4259
4379
|
model,
|
|
4260
4380
|
mode,
|
|
4261
4381
|
content,
|
|
4262
|
-
|
|
4382
|
+
attachments: attachments2,
|
|
4383
|
+
signal: abortController.signal,
|
|
4263
4384
|
onSessionId: (nextSessionId) => {
|
|
4264
4385
|
if (!nextSessionId || nextSessionId === currentSessionId)
|
|
4265
4386
|
return;
|
|
4266
|
-
|
|
4387
|
+
const previousSessionId = currentSessionId;
|
|
4388
|
+
replaceSessionId(previousSessionId, nextSessionId);
|
|
4389
|
+
moveSessionRuntimeState(previousSessionId, nextSessionId);
|
|
4267
4390
|
currentSessionId = nextSessionId;
|
|
4268
|
-
|
|
4391
|
+
const nextRequest = {
|
|
4269
4392
|
localSessionId: nextSessionId,
|
|
4270
4393
|
sessionId: nextSessionId,
|
|
4271
4394
|
content,
|
|
4395
|
+
attachments: attachments2,
|
|
4272
4396
|
model,
|
|
4273
4397
|
mode
|
|
4274
4398
|
};
|
|
4399
|
+
lastRequestBySessionRef.current.delete(previousSessionId);
|
|
4400
|
+
lastRequestBySessionRef.current.set(nextSessionId, nextRequest);
|
|
4275
4401
|
},
|
|
4276
4402
|
onUpdate: (update) => {
|
|
4277
4403
|
accumulated = resolveAccumulatedContent(accumulated, update);
|
|
@@ -4281,16 +4407,18 @@ var useChatComposer = () => {
|
|
|
4281
4407
|
});
|
|
4282
4408
|
},
|
|
4283
4409
|
onDone: () => {
|
|
4284
|
-
|
|
4410
|
+
streamSettled = true;
|
|
4411
|
+
if (stopRequestBySessionRef.current.has(currentSessionId)) {
|
|
4285
4412
|
finalizeStop(currentSessionId);
|
|
4286
4413
|
return;
|
|
4287
4414
|
}
|
|
4288
4415
|
completeStreamingMessage(currentSessionId);
|
|
4289
|
-
|
|
4416
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4290
4417
|
clearStopRequest(currentSessionId);
|
|
4291
4418
|
},
|
|
4292
4419
|
onError: (streamError) => {
|
|
4293
|
-
|
|
4420
|
+
streamSettled = true;
|
|
4421
|
+
if (stopRequestBySessionRef.current.has(currentSessionId)) {
|
|
4294
4422
|
finalizeStop(currentSessionId);
|
|
4295
4423
|
return;
|
|
4296
4424
|
}
|
|
@@ -4299,12 +4427,24 @@ var useChatComposer = () => {
|
|
|
4299
4427
|
currentSessionId,
|
|
4300
4428
|
normalizeChatErrorMessage(streamError.message, labels)
|
|
4301
4429
|
);
|
|
4302
|
-
|
|
4430
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4303
4431
|
clearStopRequest(currentSessionId);
|
|
4304
4432
|
}
|
|
4305
4433
|
});
|
|
4306
|
-
} catch {
|
|
4307
|
-
|
|
4434
|
+
} catch (streamError) {
|
|
4435
|
+
abortControllerBySessionRef.current.delete(currentSessionId);
|
|
4436
|
+
if (streamSettled || abortController.signal.aborted || !store.getState().isStreamingBySession[currentSessionId]) {
|
|
4437
|
+
return;
|
|
4438
|
+
}
|
|
4439
|
+
finalizeStoppedStreamingMessage(currentSessionId);
|
|
4440
|
+
setSessionError(
|
|
4441
|
+
currentSessionId,
|
|
4442
|
+
normalizeChatErrorMessage(
|
|
4443
|
+
streamError instanceof Error ? streamError.message : void 0,
|
|
4444
|
+
labels
|
|
4445
|
+
)
|
|
4446
|
+
);
|
|
4447
|
+
clearStopRequest(currentSessionId);
|
|
4308
4448
|
}
|
|
4309
4449
|
},
|
|
4310
4450
|
[
|
|
@@ -4313,61 +4453,68 @@ var useChatComposer = () => {
|
|
|
4313
4453
|
clearStopRequest,
|
|
4314
4454
|
finalizeStop,
|
|
4315
4455
|
labels,
|
|
4456
|
+
moveSessionRuntimeState,
|
|
4316
4457
|
startStreamingMessage,
|
|
4317
4458
|
replaceSessionId,
|
|
4318
4459
|
patchStreamingMessage,
|
|
4319
4460
|
completeStreamingMessage,
|
|
4320
4461
|
finalizeStoppedStreamingMessage,
|
|
4321
|
-
setSessionError
|
|
4462
|
+
setSessionError,
|
|
4463
|
+
store
|
|
4322
4464
|
]
|
|
4323
4465
|
);
|
|
4324
4466
|
const send = (0, import_react13.useCallback)(
|
|
4325
|
-
async (contentOverride) => {
|
|
4467
|
+
async (contentOverride, options) => {
|
|
4326
4468
|
const content = (contentOverride ?? value).trim();
|
|
4327
|
-
const
|
|
4328
|
-
const
|
|
4469
|
+
const includeComposerAttachments = options?.includeComposerAttachments ?? true;
|
|
4470
|
+
const composerAttachmentCount = includeComposerAttachments ? attachments.length : 0;
|
|
4329
4471
|
if (!canSendChatMessage({
|
|
4330
4472
|
value: content,
|
|
4331
|
-
attachmentCount:
|
|
4473
|
+
attachmentCount: composerAttachmentCount,
|
|
4332
4474
|
isModelsLoading,
|
|
4333
4475
|
isModelsError,
|
|
4334
4476
|
hasModels
|
|
4335
4477
|
})) {
|
|
4336
4478
|
return;
|
|
4337
4479
|
}
|
|
4338
|
-
|
|
4480
|
+
const storeState = store.getState();
|
|
4481
|
+
const currentActiveSessionId = options?.sessionId ?? storeState.activeSessionId;
|
|
4482
|
+
const currentActiveSession = storeState.sessions.find((session2) => session2.sessionId === currentActiveSessionId) ?? null;
|
|
4483
|
+
const currentMode = currentActiveSession?.mode ?? selectedMode;
|
|
4484
|
+
if (!(selectedModel || currentActiveSession?.model || availableModels[0]?.id)) {
|
|
4339
4485
|
return;
|
|
4340
4486
|
}
|
|
4341
|
-
const resolvedModel = selectedModel ||
|
|
4487
|
+
const resolvedModel = selectedModel || currentActiveSession?.model || availableModels[0]?.id || "local-image";
|
|
4342
4488
|
const { localSessionId, sessionId, session } = resolveSendSession({
|
|
4343
|
-
activeSessionId,
|
|
4489
|
+
activeSessionId: currentActiveSessionId,
|
|
4344
4490
|
selectedModel: resolvedModel,
|
|
4345
|
-
selectedMode,
|
|
4491
|
+
selectedMode: currentMode,
|
|
4346
4492
|
nowIso,
|
|
4347
4493
|
createSessionId: createDraftChatSessionId
|
|
4348
4494
|
});
|
|
4349
4495
|
if (session)
|
|
4350
4496
|
createSession(session);
|
|
4351
|
-
const messageAttachments = takeMessageAttachments();
|
|
4497
|
+
const messageAttachments = includeComposerAttachments ? takeMessageAttachments() : void 0;
|
|
4352
4498
|
const userMessage = createUserMessage({
|
|
4353
4499
|
sessionId: localSessionId,
|
|
4354
4500
|
content,
|
|
4355
4501
|
attachments: messageAttachments,
|
|
4356
|
-
localOnly:
|
|
4502
|
+
localOnly: false,
|
|
4357
4503
|
createdAt: nowIso(),
|
|
4358
4504
|
createMessageId: () => `user-${Date.now()}`
|
|
4359
4505
|
});
|
|
4360
4506
|
appendMessage(localSessionId, userMessage);
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4507
|
+
if (includeComposerAttachments) {
|
|
4508
|
+
setAttachmentNotice(null);
|
|
4509
|
+
setValue("");
|
|
4510
|
+
}
|
|
4365
4511
|
await runStream({
|
|
4366
4512
|
localSessionId,
|
|
4367
4513
|
sessionId,
|
|
4368
4514
|
content,
|
|
4515
|
+
attachments: messageAttachments,
|
|
4369
4516
|
model: resolvedModel,
|
|
4370
|
-
mode:
|
|
4517
|
+
mode: currentMode
|
|
4371
4518
|
});
|
|
4372
4519
|
},
|
|
4373
4520
|
[
|
|
@@ -4377,16 +4524,47 @@ var useChatComposer = () => {
|
|
|
4377
4524
|
isModelsError,
|
|
4378
4525
|
hasModels,
|
|
4379
4526
|
selectedModel,
|
|
4380
|
-
activeSession,
|
|
4381
4527
|
availableModels,
|
|
4382
|
-
activeSessionId,
|
|
4383
4528
|
selectedMode,
|
|
4384
4529
|
createSession,
|
|
4385
4530
|
takeMessageAttachments,
|
|
4386
4531
|
appendMessage,
|
|
4387
|
-
runStream
|
|
4532
|
+
runStream,
|
|
4533
|
+
store
|
|
4388
4534
|
]
|
|
4389
4535
|
);
|
|
4536
|
+
const stopSession = (0, import_react13.useCallback)(
|
|
4537
|
+
async (sessionId) => {
|
|
4538
|
+
const storeState = store.getState();
|
|
4539
|
+
const isSessionStreaming = storeState.isStreamingBySession[sessionId] ?? false;
|
|
4540
|
+
const isSessionStopping = storeState.isStoppingBySession[sessionId] ?? false;
|
|
4541
|
+
if (!isSessionStreaming || isSessionStopping) {
|
|
4542
|
+
return;
|
|
4543
|
+
}
|
|
4544
|
+
if (isDraftChatSessionId(sessionId)) {
|
|
4545
|
+
finalizeStop(sessionId);
|
|
4546
|
+
return;
|
|
4547
|
+
}
|
|
4548
|
+
requestStopStreaming(sessionId);
|
|
4549
|
+
stopRequestBySessionRef.current.set(sessionId, {
|
|
4550
|
+
timeoutId: window.setTimeout(() => {
|
|
4551
|
+
finalizeStop(sessionId);
|
|
4552
|
+
}, STOP_WAIT_TIMEOUT_MS),
|
|
4553
|
+
finalized: false
|
|
4554
|
+
});
|
|
4555
|
+
try {
|
|
4556
|
+
const result = await transport.terminateStream(sessionId);
|
|
4557
|
+
if (!result.terminated) {
|
|
4558
|
+
console.error("Failed to terminate chat session: server returned not terminated");
|
|
4559
|
+
}
|
|
4560
|
+
finalizeStop(sessionId);
|
|
4561
|
+
} catch (err) {
|
|
4562
|
+
console.error("Failed to terminate chat session", err);
|
|
4563
|
+
finalizeStop(sessionId);
|
|
4564
|
+
}
|
|
4565
|
+
},
|
|
4566
|
+
[finalizeStop, requestStopStreaming, store, transport]
|
|
4567
|
+
);
|
|
4390
4568
|
return {
|
|
4391
4569
|
state: {
|
|
4392
4570
|
value,
|
|
@@ -4426,43 +4604,27 @@ var useChatComposer = () => {
|
|
|
4426
4604
|
setPreferredMode(mode);
|
|
4427
4605
|
if (activeSessionId)
|
|
4428
4606
|
setSessionMode(activeSessionId, mode);
|
|
4429
|
-
if (
|
|
4430
|
-
|
|
4607
|
+
if (activeSessionId) {
|
|
4608
|
+
const previousRequest = lastRequestBySessionRef.current.get(activeSessionId);
|
|
4609
|
+
if (previousRequest) {
|
|
4610
|
+
lastRequestBySessionRef.current.set(activeSessionId, { ...previousRequest, mode });
|
|
4611
|
+
}
|
|
4431
4612
|
}
|
|
4432
4613
|
},
|
|
4433
4614
|
reloadModels: () => void fetchModels(),
|
|
4615
|
+
stopSession,
|
|
4434
4616
|
stop: async () => {
|
|
4435
4617
|
if (!streamingSessionId)
|
|
4436
4618
|
return;
|
|
4437
|
-
|
|
4438
|
-
return;
|
|
4439
|
-
if (isDraftChatSessionId(streamingSessionId)) {
|
|
4440
|
-
finalizeStop(streamingSessionId);
|
|
4441
|
-
return;
|
|
4442
|
-
}
|
|
4443
|
-
requestStopStreaming(streamingSessionId);
|
|
4444
|
-
stopRequestRef.current = {
|
|
4445
|
-
sessionId: streamingSessionId,
|
|
4446
|
-
timeoutId: window.setTimeout(() => {
|
|
4447
|
-
finalizeStop(streamingSessionId);
|
|
4448
|
-
}, STOP_WAIT_TIMEOUT_MS),
|
|
4449
|
-
finalized: false
|
|
4450
|
-
};
|
|
4451
|
-
try {
|
|
4452
|
-
const result = await transport.terminateStream(streamingSessionId);
|
|
4453
|
-
if (!result.terminated) {
|
|
4454
|
-
console.error("Failed to terminate chat session: server returned not terminated");
|
|
4455
|
-
}
|
|
4456
|
-
finalizeStop(streamingSessionId);
|
|
4457
|
-
} catch (err) {
|
|
4458
|
-
console.error("Failed to terminate chat session", err);
|
|
4459
|
-
finalizeStop(streamingSessionId);
|
|
4460
|
-
}
|
|
4619
|
+
await stopSession(streamingSessionId);
|
|
4461
4620
|
},
|
|
4462
|
-
retry: () => {
|
|
4463
|
-
if (!
|
|
4621
|
+
retry: (sessionId) => {
|
|
4622
|
+
if (!sessionId)
|
|
4623
|
+
return;
|
|
4624
|
+
const request = lastRequestBySessionRef.current.get(sessionId);
|
|
4625
|
+
if (!request)
|
|
4464
4626
|
return;
|
|
4465
|
-
void runStream(
|
|
4627
|
+
void runStream(request);
|
|
4466
4628
|
}
|
|
4467
4629
|
}
|
|
4468
4630
|
};
|
|
@@ -4627,7 +4789,7 @@ var CloseGlyph = import_styled10.default.span`
|
|
|
4627
4789
|
|
|
4628
4790
|
// src/components/chat-composer/components/chat-model-control.tsx
|
|
4629
4791
|
var import_styled11 = __toESM(require("@emotion/styled"));
|
|
4630
|
-
var
|
|
4792
|
+
var import_compass_ui = require("@xinghunm/compass-ui");
|
|
4631
4793
|
var import_jsx_runtime12 = require("@emotion/react/jsx-runtime");
|
|
4632
4794
|
var ChatModelControl = ({
|
|
4633
4795
|
selectedModel,
|
|
@@ -4728,7 +4890,7 @@ var ModelReloadButton = import_styled11.default.button`
|
|
|
4728
4890
|
var ReloadIcon = import_styled11.default.svg`
|
|
4729
4891
|
flex-shrink: 0;
|
|
4730
4892
|
`;
|
|
4731
|
-
var ModelSelect = (0, import_styled11.default)(
|
|
4893
|
+
var ModelSelect = (0, import_styled11.default)(import_compass_ui.Select)`
|
|
4732
4894
|
&& {
|
|
4733
4895
|
width: auto;
|
|
4734
4896
|
min-width: 0;
|
|
@@ -4749,7 +4911,7 @@ var ModelSelect = (0, import_styled11.default)(import_compass_ui2.Select)`
|
|
|
4749
4911
|
|
|
4750
4912
|
// src/components/chat-composer/components/chat-mode-control.tsx
|
|
4751
4913
|
var import_styled12 = __toESM(require("@emotion/styled"));
|
|
4752
|
-
var
|
|
4914
|
+
var import_compass_ui2 = require("@xinghunm/compass-ui");
|
|
4753
4915
|
var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
|
|
4754
4916
|
var ChatModeControl = ({
|
|
4755
4917
|
value,
|
|
@@ -4772,7 +4934,7 @@ var ChatModeControl = ({
|
|
|
4772
4934
|
}
|
|
4773
4935
|
);
|
|
4774
4936
|
};
|
|
4775
|
-
var ModeSelect = (0, import_styled12.default)(
|
|
4937
|
+
var ModeSelect = (0, import_styled12.default)(import_compass_ui2.Select)`
|
|
4776
4938
|
&& {
|
|
4777
4939
|
flex: 0 1 auto;
|
|
4778
4940
|
width: auto;
|
|
@@ -4794,7 +4956,7 @@ var ModeSelect = (0, import_styled12.default)(import_compass_ui3.Select)`
|
|
|
4794
4956
|
|
|
4795
4957
|
// src/components/chat-composer/components/chat-send-actions.tsx
|
|
4796
4958
|
var import_styled13 = __toESM(require("@emotion/styled"));
|
|
4797
|
-
var
|
|
4959
|
+
var import_compass_ui3 = require("@xinghunm/compass-ui");
|
|
4798
4960
|
var import_jsx_runtime14 = require("@emotion/react/jsx-runtime");
|
|
4799
4961
|
var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4800
4962
|
"svg",
|
|
@@ -4848,7 +5010,7 @@ var ChatSendActions = ({
|
|
|
4848
5010
|
onClick: () => void onSend()
|
|
4849
5011
|
}
|
|
4850
5012
|
) });
|
|
4851
|
-
var PrimaryButton = (0, import_styled13.default)(
|
|
5013
|
+
var PrimaryButton = (0, import_styled13.default)(import_compass_ui3.Button)`
|
|
4852
5014
|
&& {
|
|
4853
5015
|
min-width: 24px;
|
|
4854
5016
|
width: 24px;
|
|
@@ -4874,7 +5036,7 @@ var PrimaryButton = (0, import_styled13.default)(import_compass_ui4.Button)`
|
|
|
4874
5036
|
}
|
|
4875
5037
|
}
|
|
4876
5038
|
`;
|
|
4877
|
-
var StopButton = (0, import_styled13.default)(
|
|
5039
|
+
var StopButton = (0, import_styled13.default)(import_compass_ui3.Button)`
|
|
4878
5040
|
&& {
|
|
4879
5041
|
min-width: 24px;
|
|
4880
5042
|
width: 24px;
|
|
@@ -5109,7 +5271,7 @@ var ChatComposerView = ({
|
|
|
5109
5271
|
}
|
|
5110
5272
|
) : null,
|
|
5111
5273
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
5112
|
-
|
|
5274
|
+
Input,
|
|
5113
5275
|
{
|
|
5114
5276
|
ref: inputRef,
|
|
5115
5277
|
"data-testid": "chat-composer-input",
|
|
@@ -5170,15 +5332,16 @@ var ChatComposerView = ({
|
|
|
5170
5332
|
] }) });
|
|
5171
5333
|
};
|
|
5172
5334
|
var ChatComposer = () => {
|
|
5173
|
-
const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
|
|
5335
|
+
const { labels, sendRef, retryRef, stopRef, enableImageAttachments } = useChatContext();
|
|
5174
5336
|
const { state, actions } = useChatComposer();
|
|
5175
5337
|
const { send, retry } = actions;
|
|
5176
5338
|
(0, import_react15.useEffect)(() => {
|
|
5177
5339
|
sendRef.current = send;
|
|
5178
|
-
retryRef.current = async () => {
|
|
5179
|
-
retry();
|
|
5340
|
+
retryRef.current = async (sessionId) => {
|
|
5341
|
+
retry(sessionId);
|
|
5180
5342
|
};
|
|
5181
|
-
|
|
5343
|
+
stopRef.current = actions.stopSession;
|
|
5344
|
+
}, [actions.stopSession, retry, retryRef, send, sendRef, stopRef]);
|
|
5182
5345
|
const modeLabels = {
|
|
5183
5346
|
ask: labels.modeLabelAsk,
|
|
5184
5347
|
plan: labels.modeLabelPlan,
|
|
@@ -5253,7 +5416,7 @@ var InputArea = import_styled14.default.div`
|
|
|
5253
5416
|
grid-area: input;
|
|
5254
5417
|
position: relative;
|
|
5255
5418
|
`;
|
|
5256
|
-
var
|
|
5419
|
+
var Input = import_styled14.default.textarea`
|
|
5257
5420
|
--textarea-line-height: ${CHAT_COMPOSER_LINE_HEIGHT_PX}px;
|
|
5258
5421
|
--textarea-min-rows: ${CHAT_COMPOSER_MIN_ROWS};
|
|
5259
5422
|
--textarea-max-rows: ${CHAT_COMPOSER_MAX_ROWS};
|
|
@@ -5324,9 +5487,9 @@ var ComposerExpandButton = import_styled14.default.button`
|
|
|
5324
5487
|
var Footer = import_styled14.default.div`
|
|
5325
5488
|
grid-area: footer;
|
|
5326
5489
|
display: grid;
|
|
5327
|
-
grid-template-columns: minmax(0, 1fr)
|
|
5490
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
5328
5491
|
align-items: flex-end;
|
|
5329
|
-
gap:
|
|
5492
|
+
gap: 8px;
|
|
5330
5493
|
padding: 0 14px 14px;
|
|
5331
5494
|
`;
|
|
5332
5495
|
var LeadingActions = import_styled14.default.div`
|
|
@@ -5341,6 +5504,9 @@ var TrailingActions = import_styled14.default.div`
|
|
|
5341
5504
|
align-items: center;
|
|
5342
5505
|
flex-wrap: wrap;
|
|
5343
5506
|
min-width: 0;
|
|
5507
|
+
width: fit-content;
|
|
5508
|
+
max-width: 100%;
|
|
5509
|
+
justify-self: end;
|
|
5344
5510
|
justify-content: flex-end;
|
|
5345
5511
|
gap: 8px;
|
|
5346
5512
|
`;
|
|
@@ -5427,28 +5593,17 @@ var ChatConversationList = () => {
|
|
|
5427
5593
|
const { labels } = useChatContext();
|
|
5428
5594
|
const sessions = useChatStore((s) => s.sessions);
|
|
5429
5595
|
const activeSessionId = useChatStore((s) => s.activeSessionId);
|
|
5430
|
-
const
|
|
5431
|
-
const createSession = useChatStore((s) => s.createSession);
|
|
5596
|
+
const startNewChat = useChatStore((s) => s.startNewChat);
|
|
5432
5597
|
const setActiveSession = useChatStore((s) => s.setActiveSession);
|
|
5433
5598
|
const modeLabels = {
|
|
5434
5599
|
ask: labels.modeLabelAsk,
|
|
5435
5600
|
plan: labels.modeLabelPlan,
|
|
5436
5601
|
agent: labels.modeLabelAgent
|
|
5437
5602
|
};
|
|
5438
|
-
const handleCreateSession = () => {
|
|
5439
|
-
const session = createDraftChatSession({
|
|
5440
|
-
// Model is intentionally deferred: ChatComposer resolves selectedModel at send time.
|
|
5441
|
-
model: "",
|
|
5442
|
-
mode: preferredMode,
|
|
5443
|
-
nowIso: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
5444
|
-
createSessionId: createDraftChatSessionId
|
|
5445
|
-
});
|
|
5446
|
-
createSession(session);
|
|
5447
|
-
};
|
|
5448
5603
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Container3, { children: [
|
|
5449
5604
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Toolbar, { children: [
|
|
5450
5605
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Title3, { children: "Sessions" }),
|
|
5451
|
-
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick:
|
|
5606
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: startNewChat, children: labels.newChat })
|
|
5452
5607
|
] }),
|
|
5453
5608
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
5454
5609
|
ChatSessionItem,
|
|
@@ -5500,8 +5655,70 @@ var List2 = import_styled16.default.div`
|
|
|
5500
5655
|
|
|
5501
5656
|
// src/components/ai-chat/index.tsx
|
|
5502
5657
|
var import_jsx_runtime18 = require("@emotion/react/jsx-runtime");
|
|
5503
|
-
var
|
|
5504
|
-
|
|
5658
|
+
var QuickActions = ({ renderNewChatTrigger }) => {
|
|
5659
|
+
const { labels, stopRef, store } = useChatContext();
|
|
5660
|
+
const startNewChat = useChatStore((state) => state.startNewChat);
|
|
5661
|
+
const activeSessionId = useChatStore((state) => state.activeSessionId);
|
|
5662
|
+
const isActiveSessionStreaming = useChatStore(
|
|
5663
|
+
(state) => state.activeSessionId ? state.isStreamingBySession[state.activeSessionId] ?? false : false
|
|
5664
|
+
);
|
|
5665
|
+
const isActiveSessionStopping = useChatStore(
|
|
5666
|
+
(state) => state.activeSessionId ? state.isStoppingBySession[state.activeSessionId] ?? false : false
|
|
5667
|
+
);
|
|
5668
|
+
const createNewSession = () => {
|
|
5669
|
+
startNewChat();
|
|
5670
|
+
};
|
|
5671
|
+
const stopActiveSession = async () => {
|
|
5672
|
+
const currentState = store.getState();
|
|
5673
|
+
const currentSessionId = currentState.activeSessionId;
|
|
5674
|
+
const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
|
|
5675
|
+
if (!currentSessionId || !isCurrentSessionStreaming) {
|
|
5676
|
+
return;
|
|
5677
|
+
}
|
|
5678
|
+
await stopRef.current(currentSessionId);
|
|
5679
|
+
};
|
|
5680
|
+
const handleStartNewChat = async () => {
|
|
5681
|
+
const currentState = store.getState();
|
|
5682
|
+
const currentSessionId = currentState.activeSessionId;
|
|
5683
|
+
const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
|
|
5684
|
+
if (currentSessionId && isCurrentSessionStreaming) {
|
|
5685
|
+
void stopRef.current(currentSessionId);
|
|
5686
|
+
}
|
|
5687
|
+
createNewSession();
|
|
5688
|
+
};
|
|
5689
|
+
const triggerProps = {
|
|
5690
|
+
activeSessionId,
|
|
5691
|
+
isStreaming: isActiveSessionStreaming,
|
|
5692
|
+
isStopping: isActiveSessionStopping,
|
|
5693
|
+
createNewSession,
|
|
5694
|
+
stopActiveSession,
|
|
5695
|
+
startNewChat: handleStartNewChat
|
|
5696
|
+
};
|
|
5697
|
+
if (renderNewChatTrigger) {
|
|
5698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(QuickActionsRow, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NewChatTriggerRenderer, { renderNewChatTrigger, triggerProps }) });
|
|
5699
|
+
}
|
|
5700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(QuickActionsRow, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5701
|
+
QuickActionButton,
|
|
5702
|
+
{
|
|
5703
|
+
type: "button",
|
|
5704
|
+
"data-testid": "chat-start-new-session",
|
|
5705
|
+
onClick: () => void handleStartNewChat(),
|
|
5706
|
+
disabled: isActiveSessionStopping,
|
|
5707
|
+
children: labels.newChat
|
|
5708
|
+
}
|
|
5709
|
+
) });
|
|
5710
|
+
};
|
|
5711
|
+
var NewChatTriggerRenderer = ({
|
|
5712
|
+
renderNewChatTrigger,
|
|
5713
|
+
triggerProps
|
|
5714
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_jsx_runtime18.Fragment, { children: renderNewChatTrigger(triggerProps) });
|
|
5715
|
+
var AiChat = ({
|
|
5716
|
+
showConversationList = false,
|
|
5717
|
+
showNewChatButton = false,
|
|
5718
|
+
renderNewChatTrigger,
|
|
5719
|
+
...providerProps
|
|
5720
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
5721
|
+
import_compass_ui4.ConfigProvider,
|
|
5505
5722
|
{
|
|
5506
5723
|
theme: {
|
|
5507
5724
|
token: {
|
|
@@ -5538,6 +5755,7 @@ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE_
|
|
|
5538
5755
|
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Root, { "data-testid": "ai-chat", children: [
|
|
5539
5756
|
showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatConversationList, {}) : null,
|
|
5540
5757
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Workspace, { children: [
|
|
5758
|
+
showNewChatButton && !showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(QuickActions, { renderNewChatTrigger }) : null,
|
|
5541
5759
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatThread, {}),
|
|
5542
5760
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatComposer, {})
|
|
5543
5761
|
] })
|
|
@@ -5555,9 +5773,28 @@ var Workspace = import_styled17.default.section`
|
|
|
5555
5773
|
flex: 1;
|
|
5556
5774
|
display: flex;
|
|
5557
5775
|
flex-direction: column;
|
|
5776
|
+
gap: 12px;
|
|
5558
5777
|
min-height: 0;
|
|
5559
5778
|
overflow: hidden;
|
|
5560
5779
|
`;
|
|
5780
|
+
var QuickActionsRow = import_styled17.default.div`
|
|
5781
|
+
display: flex;
|
|
5782
|
+
justify-content: flex-end;
|
|
5783
|
+
padding: 12px 12px 0;
|
|
5784
|
+
`;
|
|
5785
|
+
var QuickActionButton = import_styled17.default.button`
|
|
5786
|
+
border: none;
|
|
5787
|
+
border-radius: 12px;
|
|
5788
|
+
padding: 10px 14px;
|
|
5789
|
+
background: rgba(255, 255, 255, 0.08);
|
|
5790
|
+
color: var(--text-primary, #fcfbf8);
|
|
5791
|
+
cursor: pointer;
|
|
5792
|
+
|
|
5793
|
+
&:disabled {
|
|
5794
|
+
opacity: 0.5;
|
|
5795
|
+
cursor: not-allowed;
|
|
5796
|
+
}
|
|
5797
|
+
`;
|
|
5561
5798
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5562
5799
|
0 && (module.exports = {
|
|
5563
5800
|
AiChat,
|