@xinghunm/ai-chat 1.4.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,17 @@
1
1
  // src/components/ai-chat/index.tsx
2
- import { useEffect as useEffect11 } from "react";
2
+ import { useEffect as useEffect12 } from "react";
3
3
  import styled17 from "@emotion/styled";
4
4
  import { ConfigProvider } from "@xinghunm/compass-ui";
5
5
 
6
6
  // src/components/ai-chat-provider/index.tsx
7
- import { useRef, useMemo, useState, useEffect, useLayoutEffect } from "react";
7
+ import {
8
+ useCallback,
9
+ useRef,
10
+ useMemo,
11
+ useState,
12
+ useEffect,
13
+ useLayoutEffect
14
+ } from "react";
8
15
  import axios2 from "axios";
9
16
 
10
17
  // src/context/chat-context.ts
@@ -49,6 +56,7 @@ var DEFAULT_AI_CHAT_LABELS = {
49
56
  questionnaireMultiSelectHint: "Multiple choice",
50
57
  questionnaireOtherOptionLabel: "Other",
51
58
  questionnaireOtherPlaceholder: "Other",
59
+ questionnaireExpired: "Selection expired.",
52
60
  modelLoading: "Loading models...",
53
61
  modelLoadFailed: "Failed to load models",
54
62
  modelUnavailable: "No model available",
@@ -57,7 +65,9 @@ var DEFAULT_AI_CHAT_LABELS = {
57
65
  removeSkillAriaLabel: "Remove skill",
58
66
  sessionHistoryLoading: "Loading conversations...",
59
67
  sessionHistoryLoadFailed: "Failed to load conversations",
60
- sessionHistoryEmpty: "No conversations yet"
68
+ sessionHistoryEmpty: "No conversations yet",
69
+ questionnaireTitle: "Plan Options",
70
+ questionnaireConfirmInTime: "Please confirm within {{seconds}} seconds"
61
71
  };
62
72
 
63
73
  // src/lib/chat-session.ts
@@ -97,6 +107,42 @@ var mergeOlderHistoryMessages = (sessionId, olderMessages, currentMessages) => {
97
107
  );
98
108
  return [...uniqueOlderMessages, ...currentMessages];
99
109
  };
110
+ var mergeLocalQuestionnaireState = (localMessages, incomingMessages) => {
111
+ if (!localMessages?.length)
112
+ return incomingMessages;
113
+ return incomingMessages.map((incomingMsg) => {
114
+ const localMsg = localMessages.find((lm) => lm.id === incomingMsg.id);
115
+ if (!localMsg)
116
+ return incomingMsg;
117
+ if (localMsg.blocks && !incomingMsg.blocks) {
118
+ return {
119
+ ...incomingMsg,
120
+ blocks: localMsg.blocks
121
+ };
122
+ }
123
+ if (localMsg.blocks && incomingMsg.blocks) {
124
+ const nextBlocks = incomingMsg.blocks.map((incomingBlock, index3) => {
125
+ const localBlock = localMsg.blocks?.[index3];
126
+ const isSameQuestionnaire = incomingBlock.type === "questionnaire" && localBlock?.type === "questionnaire" && (localBlock.questionnaire.questionnaireId === incomingBlock.questionnaire.questionnaireId || Boolean(localBlock.questionnaire.blockKey) && localBlock.questionnaire.blockKey === incomingBlock.questionnaire.blockKey);
127
+ if (isSameQuestionnaire) {
128
+ return {
129
+ ...incomingBlock,
130
+ questionnaire: {
131
+ ...incomingBlock.questionnaire,
132
+ questionnaireId: localBlock.questionnaire.questionnaireId ?? incomingBlock.questionnaire.questionnaireId,
133
+ answers: localBlock.questionnaire.answers ?? incomingBlock.questionnaire.answers,
134
+ status: localBlock.questionnaire.status ?? incomingBlock.questionnaire.status,
135
+ statusMessage: localBlock.questionnaire.statusMessage ?? incomingBlock.questionnaire.statusMessage
136
+ }
137
+ };
138
+ }
139
+ return incomingBlock;
140
+ });
141
+ return { ...incomingMsg, blocks: nextBlocks };
142
+ }
143
+ return incomingMsg;
144
+ });
145
+ };
100
146
  var resolveSessionTitleFromMessage = (message) => {
101
147
  const trimmedContent = message.content.trim();
102
148
  if (trimmedContent) {
@@ -479,10 +525,12 @@ var createChatStore = (initialState) => createStore((set, get) => ({
479
525
  },
480
526
  hydrateHistorySessionMessages: (sessionId, messages) => {
481
527
  const state = get();
528
+ const localMessages = state.messagesBySession[sessionId] ?? [];
529
+ const nextMessages = mergeLocalQuestionnaireState(localMessages, messages);
482
530
  set({
483
531
  messagesBySession: {
484
532
  ...state.messagesBySession,
485
- [sessionId]: normalizeHistoryMessages(sessionId, messages)
533
+ [sessionId]: normalizeHistoryMessages(sessionId, nextMessages)
486
534
  },
487
535
  sessionMessageLoadStatusBySession: {
488
536
  ...state.sessionMessageLoadStatusBySession,
@@ -500,10 +548,12 @@ var createChatStore = (initialState) => createStore((set, get) => ({
500
548
  },
501
549
  hydrateHistorySessionMessagesPage: (sessionId, page) => {
502
550
  const state = get();
551
+ const localMessages = state.messagesBySession[sessionId] ?? [];
552
+ const nextMessages = mergeLocalQuestionnaireState(localMessages, page.messages);
503
553
  set({
504
554
  messagesBySession: {
505
555
  ...state.messagesBySession,
506
- [sessionId]: normalizeHistoryMessages(sessionId, page.messages)
556
+ [sessionId]: normalizeHistoryMessages(sessionId, nextMessages)
507
557
  },
508
558
  sessionMessageLoadStatusBySession: {
509
559
  ...state.sessionMessageLoadStatusBySession,
@@ -662,7 +712,11 @@ var createChatStore = (initialState) => createStore((set, get) => ({
662
712
  hasUpdatedBlock = true;
663
713
  return {
664
714
  ...block,
665
- questionnaire: { ...block.questionnaire, answers: { ...answers } }
715
+ questionnaire: {
716
+ ...block.questionnaire,
717
+ answers: { ...answers },
718
+ status: "submitted"
719
+ }
666
720
  };
667
721
  });
668
722
  if (!hasUpdatedBlock)
@@ -1031,6 +1085,7 @@ var AiChatProvider = (props) => {
1031
1085
  onLoadMoreSessions,
1032
1086
  onSelectHistorySession,
1033
1087
  onLoadMoreHistoryMessages,
1088
+ renderSessionItemActions,
1034
1089
  enableImageAttachments = true,
1035
1090
  children
1036
1091
  } = props;
@@ -1043,6 +1098,17 @@ var AiChatProvider = (props) => {
1043
1098
  });
1044
1099
  const stopRef = useRef(async (_sessionId) => {
1045
1100
  });
1101
+ const [stopSession, setStopSession] = useState(
1102
+ () => async (_sessionId) => {
1103
+ }
1104
+ );
1105
+ const registerStopSession = useCallback(
1106
+ (handler) => {
1107
+ stopRef.current = handler;
1108
+ setStopSession(() => handler);
1109
+ },
1110
+ []
1111
+ );
1046
1112
  const defaultApiBaseUrl = "apiBaseUrl" in props ? props.apiBaseUrl : void 0;
1047
1113
  const defaultAuthToken = "authToken" in props ? props.authToken : void 0;
1048
1114
  const defaultTransformStreamPacket = "transformStreamPacket" in props ? props.transformStreamPacket : void 0;
@@ -1102,6 +1168,8 @@ var AiChatProvider = (props) => {
1102
1168
  sendRef,
1103
1169
  retryRef,
1104
1170
  stopRef,
1171
+ stopSession,
1172
+ registerStopSession,
1105
1173
  renderMessageBlock,
1106
1174
  handleQuestionnaireSubmit,
1107
1175
  handleConfirmationSubmit,
@@ -1111,7 +1179,8 @@ var AiChatProvider = (props) => {
1111
1179
  historySessionList,
1112
1180
  onLoadMoreSessions,
1113
1181
  onSelectHistorySession,
1114
- onLoadMoreHistoryMessages
1182
+ onLoadMoreHistoryMessages,
1183
+ renderSessionItemActions
1115
1184
  }),
1116
1185
  [
1117
1186
  axiosInstance,
@@ -1127,10 +1196,13 @@ var AiChatProvider = (props) => {
1127
1196
  onLoadMoreSessions,
1128
1197
  onLoadMoreHistoryMessages,
1129
1198
  onSelectHistorySession,
1199
+ renderSessionItemActions,
1130
1200
  renderMessageBlock,
1131
1201
  sendRef,
1132
1202
  retryRef,
1133
1203
  stopRef,
1204
+ stopSession,
1205
+ registerStopSession,
1134
1206
  store,
1135
1207
  transport
1136
1208
  ]
@@ -1139,7 +1211,7 @@ var AiChatProvider = (props) => {
1139
1211
  };
1140
1212
 
1141
1213
  // src/components/chat-thread/index.tsx
1142
- import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef5, useState as useState4 } from "react";
1214
+ import { useCallback as useCallback4, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
1143
1215
  import styled9 from "@emotion/styled";
1144
1216
 
1145
1217
  // src/context/use-chat-context.ts
@@ -1160,7 +1232,7 @@ var useChatStore = (selector) => {
1160
1232
  var CHAT_THREAD_SCROLL_TOP_GAP = 16;
1161
1233
 
1162
1234
  // src/components/chat-thread/components/chat-message-item.tsx
1163
- import { Fragment, memo, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useState as useState3 } from "react";
1235
+ import { Fragment, memo, useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useState as useState4 } from "react";
1164
1236
  import styled7 from "@emotion/styled";
1165
1237
  import { keyframes } from "@emotion/react";
1166
1238
  import ReactMarkdown from "react-markdown";
@@ -1169,7 +1241,7 @@ import remarkMath from "remark-math";
1169
1241
  import rehypeKatex from "rehype-katex";
1170
1242
 
1171
1243
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
1172
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useReducer, useRef as useRef2 } from "react";
1244
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useReducer, useRef as useRef2 } from "react";
1173
1245
 
1174
1246
  // src/components/chat-thread/lib/message-reveal.ts
1175
1247
  var STREAM_REVEAL_TICK_MS = 36;
@@ -1216,11 +1288,23 @@ var getNextDisplayedUnitCount = ({
1216
1288
  var splitMarkdownBlocks = (content) => content.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
1217
1289
 
1218
1290
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
1291
+ var revealedStreamingMessages = /* @__PURE__ */ new Set();
1292
+ var markMessageAsRevealed = (id) => {
1293
+ if (revealedStreamingMessages.size > 500) {
1294
+ const firstKey = revealedStreamingMessages.keys().next().value;
1295
+ if (firstKey !== void 0) {
1296
+ revealedStreamingMessages.delete(firstKey);
1297
+ }
1298
+ }
1299
+ revealedStreamingMessages.add(id);
1300
+ };
1219
1301
  var createRevealState = ({
1220
1302
  isAssistantStreaming,
1221
- targetUnitCount
1303
+ targetUnitCount,
1304
+ messageId
1222
1305
  }) => {
1223
- const initialDisplayedUnitCount = isAssistantStreaming ? 0 : targetUnitCount;
1306
+ const hasBeenRevealed = revealedStreamingMessages.has(messageId);
1307
+ const initialDisplayedUnitCount = isAssistantStreaming && !hasBeenRevealed ? 0 : targetUnitCount;
1224
1308
  return {
1225
1309
  batchedTargetUnitCount: initialDisplayedUnitCount,
1226
1310
  displayedUnitCount: initialDisplayedUnitCount,
@@ -1230,7 +1314,11 @@ var createRevealState = ({
1230
1314
  var revealReducer = (state, action) => {
1231
1315
  switch (action.type) {
1232
1316
  case "reset-message":
1233
- return createRevealState(action);
1317
+ return createRevealState({
1318
+ isAssistantStreaming: action.isAssistantStreaming,
1319
+ targetUnitCount: action.targetUnitCount,
1320
+ messageId: action.messageId
1321
+ });
1234
1322
  case "commit-batched-target": {
1235
1323
  const nextDisplayedUnitCount = action.role === "assistant" ? getNextDisplayedUnitCount({
1236
1324
  currentUnits: state.displayedUnitCount,
@@ -1287,12 +1375,13 @@ var useChatMessageReveal = (message) => {
1287
1375
  revealReducer,
1288
1376
  {
1289
1377
  isAssistantStreaming,
1290
- targetUnitCount: targetUnits.length
1378
+ targetUnitCount: targetUnits.length,
1379
+ messageId: message.id
1291
1380
  },
1292
1381
  createRevealState
1293
1382
  );
1294
1383
  const { batchedTargetUnitCount, displayedUnitCount, isFreshBlockActive } = state;
1295
- const commitBatchedTargetUnitCount = useCallback(
1384
+ const commitBatchedTargetUnitCount = useCallback2(
1296
1385
  (nextTargetUnitCount) => {
1297
1386
  batchedTargetUnitCountRef.current = nextTargetUnitCount;
1298
1387
  dispatch({
@@ -1319,7 +1408,8 @@ var useChatMessageReveal = (message) => {
1319
1408
  dispatch({
1320
1409
  type: "reset-message",
1321
1410
  isAssistantStreaming,
1322
- targetUnitCount: targetUnits.length
1411
+ targetUnitCount: targetUnits.length,
1412
+ messageId: message.id
1323
1413
  });
1324
1414
  }, [isAssistantStreaming, message.id, targetUnits.length]);
1325
1415
  useEffect2(() => {
@@ -1398,6 +1488,11 @@ var useChatMessageReveal = (message) => {
1398
1488
  tone: "settled"
1399
1489
  }
1400
1490
  ];
1491
+ useEffect2(() => {
1492
+ if (isAssistantStreaming && displayedUnitCount > 0) {
1493
+ markMessageAsRevealed(message.id);
1494
+ }
1495
+ }, [isAssistantStreaming, displayedUnitCount, message.id]);
1401
1496
  return {
1402
1497
  isAssistantStreaming,
1403
1498
  isFreshBlockActive,
@@ -1989,12 +2084,99 @@ var Value = styled3.span`
1989
2084
 
1990
2085
  // src/components/chat-thread/components/questionnaire-card.tsx
1991
2086
  import {
1992
- useEffect as useEffect4,
2087
+ useEffect as useEffect5,
1993
2088
  useRef as useRef3,
1994
- useState as useState2
2089
+ useState as useState3
1995
2090
  } from "react";
1996
2091
  import styled4 from "@emotion/styled";
1997
2092
 
2093
+ // src/components/chat-thread/components/use-countdown-seconds.ts
2094
+ import { useEffect as useEffect4, useState as useState2 } from "react";
2095
+ var countdownDeadlineByKey = /* @__PURE__ */ new Map();
2096
+ var countdownCleanupTimerByKey = /* @__PURE__ */ new Map();
2097
+ var countdownMountCountByKey = /* @__PURE__ */ new Map();
2098
+ var clearCountdownEntry = (storageKey) => {
2099
+ const cleanupTimerId = countdownCleanupTimerByKey.get(storageKey);
2100
+ if (cleanupTimerId !== void 0) {
2101
+ window.clearTimeout(cleanupTimerId);
2102
+ countdownCleanupTimerByKey.delete(storageKey);
2103
+ }
2104
+ countdownDeadlineByKey.delete(storageKey);
2105
+ };
2106
+ var scheduleCountdownCleanup = (storageKey, deadline) => {
2107
+ const existingCleanupTimerId = countdownCleanupTimerByKey.get(storageKey);
2108
+ if (existingCleanupTimerId !== void 0) {
2109
+ return;
2110
+ }
2111
+ const delayMs = Math.max(0, deadline - Date.now());
2112
+ const cleanupTimerId = window.setTimeout(() => {
2113
+ if ((countdownMountCountByKey.get(storageKey) ?? 0) > 0) {
2114
+ countdownCleanupTimerByKey.delete(storageKey);
2115
+ return;
2116
+ }
2117
+ clearCountdownEntry(storageKey);
2118
+ }, delayMs);
2119
+ countdownCleanupTimerByKey.set(storageKey, cleanupTimerId);
2120
+ };
2121
+ var getCountdownDeadline = (storageKey, initialSeconds) => {
2122
+ const existingDeadline = countdownDeadlineByKey.get(storageKey);
2123
+ if (existingDeadline !== void 0) {
2124
+ return existingDeadline;
2125
+ }
2126
+ const nextDeadline = Date.now() + Math.max(0, initialSeconds) * 1e3;
2127
+ countdownDeadlineByKey.set(storageKey, nextDeadline);
2128
+ scheduleCountdownCleanup(storageKey, nextDeadline);
2129
+ return nextDeadline;
2130
+ };
2131
+ var getRemainingSeconds = (deadline) => Math.max(0, Math.ceil((deadline - Date.now()) / 1e3));
2132
+ var useCountdownSeconds = ({
2133
+ storageKey,
2134
+ initialSeconds,
2135
+ active
2136
+ }) => {
2137
+ const [remainingSeconds, setRemainingSeconds] = useState2(
2138
+ () => getRemainingSeconds(getCountdownDeadline(storageKey, initialSeconds))
2139
+ );
2140
+ useEffect4(() => {
2141
+ countdownMountCountByKey.set(storageKey, (countdownMountCountByKey.get(storageKey) ?? 0) + 1);
2142
+ return () => {
2143
+ const nextCount = (countdownMountCountByKey.get(storageKey) ?? 1) - 1;
2144
+ if (nextCount <= 0) {
2145
+ countdownMountCountByKey.delete(storageKey);
2146
+ return;
2147
+ }
2148
+ countdownMountCountByKey.set(storageKey, nextCount);
2149
+ };
2150
+ }, [storageKey]);
2151
+ useEffect4(() => {
2152
+ setRemainingSeconds(getRemainingSeconds(getCountdownDeadline(storageKey, initialSeconds)));
2153
+ }, [initialSeconds, storageKey]);
2154
+ useEffect4(() => {
2155
+ if (active) {
2156
+ return;
2157
+ }
2158
+ clearCountdownEntry(storageKey);
2159
+ }, [active, storageKey]);
2160
+ useEffect4(() => {
2161
+ if (remainingSeconds > 0) {
2162
+ return;
2163
+ }
2164
+ clearCountdownEntry(storageKey);
2165
+ }, [remainingSeconds, storageKey]);
2166
+ useEffect4(() => {
2167
+ if (!active || remainingSeconds <= 0) {
2168
+ return;
2169
+ }
2170
+ const timeoutId = window.setTimeout(() => {
2171
+ setRemainingSeconds(getRemainingSeconds(getCountdownDeadline(storageKey, initialSeconds)));
2172
+ }, 1e3);
2173
+ return () => {
2174
+ window.clearTimeout(timeoutId);
2175
+ };
2176
+ }, [active, initialSeconds, remainingSeconds, storageKey]);
2177
+ return remainingSeconds;
2178
+ };
2179
+
1998
2180
  // src/components/chat-thread/components/questionnaire-card-helpers.ts
1999
2181
  var OTHER_OPTION_VALUE = "__other__";
2000
2182
  var getQuestionnaireQuestion = (questionnaire) => questionnaire.question;
@@ -2301,7 +2483,10 @@ var DEFAULT_QUESTIONNAIRE_CARD_LABELS = {
2301
2483
  submitFailed: "Failed to submit. Please try again.",
2302
2484
  multiSelectHint: "Multiple choice",
2303
2485
  otherOptionLabel: "Other",
2304
- otherPlaceholder: "Other"
2486
+ otherPlaceholder: "Other",
2487
+ expired: "Selection expired.",
2488
+ questionnaireTitle: "Plan Options",
2489
+ questionnaireConfirmInTime: "Please confirm within {{seconds}} seconds"
2305
2490
  };
2306
2491
  var stopInputClickPropagation = (event) => {
2307
2492
  event.stopPropagation();
@@ -2363,30 +2548,39 @@ var QuestionnaireCardInner = ({
2363
2548
  }) => {
2364
2549
  const questionnaireRef = useRef3(questionnaire);
2365
2550
  const otherInputRefs = useRef3({});
2366
- const [answers, setAnswers] = useState2(
2551
+ const [answers, setAnswers] = useState3(
2367
2552
  () => createInitialAnswers(questionnaire)
2368
2553
  );
2369
- const [otherDrafts, setOtherDrafts] = useState2(
2554
+ const [otherDrafts, setOtherDrafts] = useState3(
2370
2555
  () => createInitialOtherDrafts(questionnaire)
2371
2556
  );
2372
- const [errorMessage, setErrorMessage] = useState2(null);
2373
- const [isSubmitting, setIsSubmitting] = useState2(false);
2374
- const [isSubmitted, setIsSubmitted] = useState2(false);
2375
- const [pendingFocusQuestionId, setPendingFocusQuestionId] = useState2(null);
2557
+ const [errorMessage, setErrorMessage] = useState3(null);
2558
+ const [isSubmitting, setIsSubmitting] = useState3(false);
2559
+ const [isSubmitted, setIsSubmitted] = useState3(() => questionnaire.status === "submitted");
2560
+ const [pendingFocusQuestionId, setPendingFocusQuestionId] = useState3(null);
2376
2561
  const resolvedLabels = {
2377
2562
  ...DEFAULT_QUESTIONNAIRE_CARD_LABELS,
2378
2563
  ...labels
2379
2564
  };
2380
- const hasExternalFailureStatus = questionnaire.status === "expired" || questionnaire.status === "failed";
2565
+ const remainingSeconds = useCountdownSeconds({
2566
+ storageKey: questionnaire.blockKey ?? `questionnaire:${questionnaire.questionnaireId}`,
2567
+ initialSeconds: questionnaire.timeoutSec ?? 0,
2568
+ active: questionnaire.timeoutSec !== void 0 && questionnaire.timeoutSec > 0 && !isSubmitted && questionnaire.status !== "expired" && questionnaire.status !== "failed" && interactive
2569
+ });
2570
+ const isExpired = questionnaire.status === "expired" || questionnaire.timeoutSec !== void 0 && questionnaire.timeoutSec > 0 && remainingSeconds <= 0;
2571
+ const hasExternalFailureStatus = isExpired || questionnaire.status === "failed";
2381
2572
  const question = getQuestionnaireQuestion(questionnaire);
2382
- const visibleErrorMessage = questionnaire.statusMessage ?? errorMessage;
2383
- const isInteractionLocked = !interactive || isSubmitting || isSubmitted || hasExternalFailureStatus;
2573
+ const visibleErrorMessage = questionnaire.statusMessage ?? (isExpired ? null : errorMessage);
2574
+ const isInteractionLocked = !interactive || isSubmitting || isSubmitted || hasExternalFailureStatus || questionnaire.status === "submitted";
2384
2575
  questionnaireRef.current = questionnaire;
2385
- useEffect4(() => {
2576
+ useEffect5(() => {
2386
2577
  setAnswers(createInitialAnswers(questionnaireRef.current));
2387
2578
  setOtherDrafts(createInitialOtherDrafts(questionnaireRef.current));
2388
- }, [questionnaire.answers]);
2389
- useEffect4(() => {
2579
+ if (questionnaireRef.current.status === "submitted") {
2580
+ setIsSubmitted(true);
2581
+ }
2582
+ }, [questionnaire.answers, questionnaire.status]);
2583
+ useEffect5(() => {
2390
2584
  if (!pendingFocusQuestionId || isInteractionLocked) {
2391
2585
  return;
2392
2586
  }
@@ -2636,42 +2830,69 @@ var QuestionnaireCardInner = ({
2636
2830
  }
2637
2831
  };
2638
2832
  return /* @__PURE__ */ jsxs3(Card4, { "data-testid": "questionnaire-card", children: [
2639
- questionnaire.description ? /* @__PURE__ */ jsx5(Description, { children: questionnaire.description }) : null,
2640
- /* @__PURE__ */ jsx5(QuestionList, { children: /* @__PURE__ */ jsxs3(QuestionCard, { children: [
2641
- /* @__PURE__ */ jsxs3(QuestionLabel, { children: [
2642
- question.label,
2643
- question.required ? /* @__PURE__ */ jsx5(Required, { children: "*" }) : null
2644
- ] }),
2645
- question.kind === "multi_select" ? /* @__PURE__ */ jsx5(QuestionHint, { children: resolvedLabels.multiSelectHint }) : null,
2646
- renderQuestion(question)
2647
- ] }, question.id) }),
2648
- visibleErrorMessage ? /* @__PURE__ */ jsx5(ErrorMessage, { "data-testid": "questionnaire-error", children: visibleErrorMessage }) : null,
2649
- isSubmitted ? /* @__PURE__ */ jsx5(SuccessMessage, { "data-testid": "questionnaire-success", children: resolvedLabels.submitted }) : interactive && !hasExternalFailureStatus ? /* @__PURE__ */ jsx5(
2650
- SubmitButton,
2651
- {
2652
- type: "button",
2653
- "data-testid": "questionnaire-submit",
2654
- disabled: isInteractionLocked,
2655
- onClick: () => {
2656
- void handleSubmit();
2657
- },
2658
- children: isSubmitting ? resolvedLabels.submitting : questionnaire.submitLabel ?? "Submit"
2659
- }
2660
- ) : null
2833
+ /* @__PURE__ */ jsx5(Header2, { children: /* @__PURE__ */ jsx5(Eyebrow2, { children: resolvedLabels.questionnaireTitle }) }),
2834
+ /* @__PURE__ */ jsxs3(Content, { children: [
2835
+ questionnaire.description ? /* @__PURE__ */ jsx5(Description, { children: questionnaire.description }) : null,
2836
+ /* @__PURE__ */ jsx5(QuestionList, { children: /* @__PURE__ */ jsxs3(QuestionCard, { children: [
2837
+ /* @__PURE__ */ jsxs3(QuestionLabel, { children: [
2838
+ question.label,
2839
+ question.required ? /* @__PURE__ */ jsx5(Required, { children: "*" }) : null
2840
+ ] }),
2841
+ question.kind === "multi_select" ? /* @__PURE__ */ jsx5(QuestionHint, { children: resolvedLabels.multiSelectHint }) : null,
2842
+ renderQuestion(question)
2843
+ ] }, question.id) }),
2844
+ visibleErrorMessage ? /* @__PURE__ */ jsx5(ErrorMessage, { "data-testid": "questionnaire-error", children: visibleErrorMessage }) : null,
2845
+ /* @__PURE__ */ jsx5(Footer, { children: isSubmitted ? /* @__PURE__ */ jsx5(SuccessMessage, { "data-testid": "questionnaire-success", children: resolvedLabels.submitted }) : isExpired ? /* @__PURE__ */ jsx5(ExpiredTag, { "data-testid": "questionnaire-expired", children: resolvedLabels.expired }) : interactive ? /* @__PURE__ */ jsxs3(FooterActionRow, { "data-testid": "questionnaire-card-actions", children: [
2846
+ remainingSeconds > 0 && /* @__PURE__ */ jsx5(MetaText, { "data-testid": "questionnaire-countdown", children: resolvedLabels.questionnaireConfirmInTime?.replace(
2847
+ "{{seconds}}",
2848
+ String(remainingSeconds)
2849
+ ) ?? `\u8BF7\u5728 ${remainingSeconds} \u79D2\u5185\u786E\u8BA4` }),
2850
+ /* @__PURE__ */ jsx5(
2851
+ SubmitButton,
2852
+ {
2853
+ type: "button",
2854
+ "data-testid": "questionnaire-submit",
2855
+ disabled: isInteractionLocked,
2856
+ onClick: () => {
2857
+ void handleSubmit();
2858
+ },
2859
+ children: isSubmitting ? resolvedLabels.submitting : questionnaire.submitLabel ?? "Submit"
2860
+ }
2861
+ )
2862
+ ] }) : null })
2863
+ ] })
2661
2864
  ] });
2662
2865
  };
2663
2866
  var QuestionnaireCard = (props) => /* @__PURE__ */ jsx5(QuestionnaireCardInner, { ...props }, getQuestionnaireStateKey(props.questionnaire));
2664
2867
  var Card4 = styled4.section`
2665
- display: grid;
2666
- gap: 14px;
2667
- padding: 16px;
2668
- border-radius: 20px;
2669
- border: 1px solid rgba(255, 255, 255, 0.08);
2670
- background: rgba(255, 255, 255, 0.03);
2868
+ overflow: hidden;
2869
+ background: var(--bg-primary);
2870
+ border-radius: 16px;
2871
+ border: 1px solid var(--border-hover);
2872
+ `;
2873
+ var Header2 = styled4.div`
2874
+ display: flex;
2875
+ flex-wrap: wrap;
2876
+ align-items: center;
2877
+ gap: 10px;
2878
+ padding: 22px 12px 16px;
2879
+ border-bottom: 1px solid var(--border-hover);
2880
+ `;
2881
+ var Eyebrow2 = styled4.div`
2882
+ font-weight: 500;
2883
+ font-size: 16px;
2884
+ color: var(--text-primary);
2885
+ line-height: 22px;
2886
+ `;
2887
+ var Content = styled4.div`
2888
+ display: flex;
2889
+ flex-direction: column;
2890
+ gap: 16px;
2891
+ padding: 16px 12px;
2671
2892
  `;
2672
2893
  var Description = styled4.p`
2673
2894
  margin: 0;
2674
- color: rgba(255, 255, 255, 0.72);
2895
+ color: var(--text-secondary);
2675
2896
  font-size: 13px;
2676
2897
  `;
2677
2898
  var QuestionList = styled4.div`
@@ -2682,11 +2903,11 @@ var QuestionCard = styled4.div`
2682
2903
  display: grid;
2683
2904
  gap: 10px;
2684
2905
  padding: 12px;
2685
- border-radius: 16px;
2686
- background: rgba(255, 255, 255, 0.04);
2906
+ border-radius: 8px;
2907
+ background: rgba(255, 255, 255, 0.02);
2687
2908
  `;
2688
2909
  var QuestionLabel = styled4.div`
2689
- color: rgba(255, 255, 255, 0.9);
2910
+ color: var(--text-primary);
2690
2911
  font-size: 14px;
2691
2912
  font-weight: 600;
2692
2913
  `;
@@ -2713,11 +2934,11 @@ var OptionChoiceItem = styled4.div`
2713
2934
  gap: 12px;
2714
2935
  width: 100%;
2715
2936
  text-align: left;
2716
- border: 1px solid rgba(255, 255, 255, 0.1);
2717
- border-radius: 14px;
2718
- background: rgba(255, 255, 255, 0.03);
2937
+ border: 1px solid var(--border-hover);
2938
+ border-radius: 8px;
2939
+ background: rgba(255, 255, 255, 0.02);
2719
2940
  padding: 2px 12px;
2720
- color: rgba(255, 255, 255, 0.9);
2941
+ color: var(--text-primary);
2721
2942
  cursor: pointer;
2722
2943
  transition:
2723
2944
  border-color 140ms ease,
@@ -2726,8 +2947,8 @@ var OptionChoiceItem = styled4.div`
2726
2947
  outline: none;
2727
2948
 
2728
2949
  &[data-selected='true'] {
2729
- border-color: rgba(126, 160, 255, 0.42);
2730
- background: linear-gradient(180deg, rgba(82, 114, 255, 0.18) 0%, rgba(82, 114, 255, 0.1) 100%);
2950
+ border-color: rgba(51, 133, 255, 0.42);
2951
+ background: linear-gradient(180deg, rgba(51, 133, 255, 0.18) 0%, rgba(51, 133, 255, 0.1) 100%);
2731
2952
  transform: translateY(-1px);
2732
2953
  }
2733
2954
 
@@ -2736,8 +2957,8 @@ var OptionChoiceItem = styled4.div`
2736
2957
  }
2737
2958
 
2738
2959
  &:focus-visible {
2739
- border-color: rgba(126, 160, 255, 0.52);
2740
- box-shadow: 0 0 0 1px rgba(126, 160, 255, 0.18);
2960
+ border-color: rgba(51, 133, 255, 0.52);
2961
+ box-shadow: 0 0 0 1px rgba(51, 133, 255, 0.18);
2741
2962
  }
2742
2963
 
2743
2964
  &[tabindex='-1'] {
@@ -2754,16 +2975,16 @@ var OptionChoiceMarker = styled4.span`
2754
2975
  width: 28px;
2755
2976
  height: 28px;
2756
2977
  border-radius: 8px;
2757
- border: 1px solid rgba(255, 255, 255, 0.14);
2758
- background: rgba(255, 255, 255, 0.04);
2759
- color: rgba(255, 255, 255, 0.65);
2978
+ border: 1px solid var(--border-hover);
2979
+ background: rgba(255, 255, 255, 0.02);
2980
+ color: var(--text-secondary);
2760
2981
  font-size: 12px;
2761
2982
  font-weight: 700;
2762
2983
 
2763
2984
  &[data-selected='true'] {
2764
- border-color: rgba(126, 160, 255, 0.38);
2765
- background: rgba(126, 160, 255, 0.2);
2766
- color: rgba(255, 255, 255, 0.96);
2985
+ border-color: rgba(51, 133, 255, 0.38);
2986
+ background: rgba(51, 133, 255, 0.2);
2987
+ color: var(--text-primary);
2767
2988
  }
2768
2989
  `;
2769
2990
  var OptionChoiceContent = styled4.span`
@@ -2783,47 +3004,41 @@ var OptionChoiceLabel = styled4.span`
2783
3004
  `;
2784
3005
  var TextInput = styled4.input`
2785
3006
  width: 100%;
2786
- border: 1px solid rgba(255, 255, 255, 0.1);
2787
- border-radius: 12px;
3007
+ border: 1px solid var(--border-hover);
3008
+ border-radius: 8px;
2788
3009
  background: rgba(13, 15, 21, 0.55);
2789
- color: rgba(255, 255, 255, 0.92);
3010
+ color: var(--text-primary);
2790
3011
  font-size: 13px;
2791
3012
  padding: 10px 12px;
2792
3013
 
2793
3014
  &::placeholder {
2794
- color: rgba(255, 255, 255, 0.34);
3015
+ color: var(--text-secondary);
3016
+ opacity: 0.5;
2795
3017
  }
2796
3018
  `;
2797
3019
  var InlineOtherInput = styled4.input`
2798
3020
  width: 100%;
2799
- margin-top: 0;
2800
-
2801
- .compass-input-wrapper {
2802
- min-height: 30px;
2803
- border: 1px solid rgba(255, 255, 255, 0.1);
2804
- border-radius: 10px;
2805
- background: rgba(13, 15, 21, 0.55);
2806
- box-shadow: none;
2807
- padding: 2px 9px;
2808
- }
2809
-
2810
- .compass-input-wrapper:hover {
2811
- border-color: rgba(126, 160, 255, 0.28);
2812
- }
3021
+ border: 1px solid var(--border-hover);
3022
+ border-radius: 6px;
3023
+ background: rgba(13, 15, 21, 0.45);
3024
+ color: var(--text-primary);
3025
+ font-size: 13px;
3026
+ padding: 6px 10px;
3027
+ outline: none;
3028
+ transition: border-color 140ms ease;
2813
3029
 
2814
- .compass-input-wrapper:focus-within {
2815
- border-color: rgba(126, 160, 255, 0.42);
2816
- box-shadow: 0 0 0 1px rgba(126, 160, 255, 0.14);
3030
+ &:hover {
3031
+ border-color: rgba(51, 133, 255, 0.28);
2817
3032
  }
2818
3033
 
2819
- .compass-input-input {
2820
- color: rgba(255, 255, 255, 0.92);
2821
- font-size: 13px;
2822
- line-height: 1.2;
3034
+ &:focus {
3035
+ border-color: rgba(51, 133, 255, 0.42);
3036
+ box-shadow: 0 0 0 1px rgba(51, 133, 255, 0.14);
2823
3037
  }
2824
3038
 
2825
- .compass-input-input::placeholder {
2826
- color: rgba(255, 255, 255, 0.34);
3039
+ &::placeholder {
3040
+ color: var(--text-secondary);
3041
+ opacity: 0.5;
2827
3042
  }
2828
3043
  `;
2829
3044
  var NumberInputRow = styled4.div`
@@ -2832,32 +3047,80 @@ var NumberInputRow = styled4.div`
2832
3047
  gap: 10px;
2833
3048
  `;
2834
3049
  var Unit = styled4.span`
2835
- color: rgba(255, 255, 255, 0.58);
3050
+ color: var(--text-secondary);
2836
3051
  font-size: 12px;
2837
3052
  white-space: nowrap;
2838
3053
  `;
2839
3054
  var ErrorMessage = styled4.div`
2840
- color: rgba(255, 145, 145, 0.96);
3055
+ font-size: 13px;
3056
+ line-height: 1.5;
3057
+ color: rgba(255, 207, 207, 0.92);
3058
+ `;
3059
+ var Footer = styled4.div`
3060
+ display: flex;
3061
+ flex-wrap: wrap;
3062
+ align-items: center;
3063
+ justify-content: flex-start;
3064
+ gap: 12px;
3065
+ `;
3066
+ var FooterActionRow = styled4.div`
3067
+ display: flex;
3068
+ align-items: center;
3069
+ flex-wrap: wrap;
3070
+ gap: 8px;
3071
+ margin-left: auto;
3072
+ justify-content: flex-end;
3073
+ `;
3074
+ var MetaText = styled4.span`
3075
+ font-weight: 500;
2841
3076
  font-size: 12px;
3077
+ color: var(--text-secondary);
3078
+ line-height: 17px;
3079
+ `;
3080
+ var ExpiredTag = styled4.span`
3081
+ display: inline-flex;
3082
+ align-items: center;
3083
+ padding: 8px 12px;
3084
+ border-radius: 8px;
3085
+ font-size: 13px;
3086
+ font-weight: 600;
3087
+ background: rgba(255, 255, 255, 0.05);
3088
+ border: 1px solid rgba(255, 255, 255, 0.12);
3089
+ color: rgba(214, 224, 236, 0.9);
2842
3090
  `;
2843
3091
  var SuccessMessage = styled4.div`
2844
- color: rgba(164, 255, 210, 0.96);
2845
- font-size: 12px;
3092
+ display: inline-flex;
3093
+ align-items: center;
3094
+ padding: 8px 12px;
3095
+ border-radius: 8px;
3096
+ font-size: 13px;
3097
+ font-weight: 600;
3098
+ background: rgba(190, 246, 202, 0.14);
3099
+ border: 1px solid rgba(190, 246, 202, 0.24);
3100
+ color: rgba(227, 255, 233, 0.94);
2846
3101
  `;
2847
3102
  var SubmitButton = styled4.button`
2848
- justify-self: flex-start;
2849
- border: none;
2850
- border-radius: 999px;
2851
- background: linear-gradient(180deg, #7ea0ff 0%, #4a6fff 100%);
2852
- color: #081127;
2853
- font-size: 12px;
2854
- font-weight: 700;
2855
- padding: 10px 14px;
3103
+ padding: 9px 16px;
3104
+ border-radius: 8px;
3105
+ border: 1px solid transparent;
3106
+ font-weight: 500;
3107
+ font-size: 14px;
3108
+ color: rgba(255, 255, 255, 0.96);
3109
+ background: linear-gradient(180deg, #3385ff 0%, #1f6ef2 100%);
3110
+ line-height: 14px;
2856
3111
  cursor: pointer;
3112
+ transition:
3113
+ transform 160ms ease,
3114
+ opacity 160ms ease;
2857
3115
 
2858
3116
  &:disabled {
2859
- cursor: default;
2860
- opacity: 0.72;
3117
+ cursor: progress;
3118
+ opacity: 0.68;
3119
+ transform: none;
3120
+ }
3121
+
3122
+ &:not(:disabled):hover {
3123
+ transform: translateY(-1px);
2861
3124
  }
2862
3125
  `;
2863
3126
 
@@ -2865,7 +3128,7 @@ var SubmitButton = styled4.button`
2865
3128
  import styled5 from "@emotion/styled";
2866
3129
  import { jsx as jsx6, jsxs as jsxs4 } from "@emotion/react/jsx-runtime";
2867
3130
  var ResultSummaryCard = ({ summary }) => /* @__PURE__ */ jsxs4(Card5, { "data-testid": "result-summary-card", "data-status": summary.status, children: [
2868
- /* @__PURE__ */ jsx6(Eyebrow2, { children: summary.status }),
3131
+ /* @__PURE__ */ jsx6(Eyebrow3, { children: summary.status }),
2869
3132
  /* @__PURE__ */ jsx6(Headline, { children: summary.headline }),
2870
3133
  /* @__PURE__ */ jsx6(Details, { children: summary.details.map((detail) => /* @__PURE__ */ jsx6(Detail, { children: detail }, detail)) })
2871
3134
  ] });
@@ -2885,7 +3148,7 @@ var Card5 = styled5.section`
2885
3148
  border-color: rgba(255, 122, 122, 0.24);
2886
3149
  }
2887
3150
  `;
2888
- var Eyebrow2 = styled5.span`
3151
+ var Eyebrow3 = styled5.span`
2889
3152
  color: rgba(255, 255, 255, 0.58);
2890
3153
  font-size: 11px;
2891
3154
  font-weight: 700;
@@ -2910,7 +3173,7 @@ var Detail = styled5.li`
2910
3173
 
2911
3174
  // src/components/chat-thread/components/image-viewer.tsx
2912
3175
  import styled6 from "@emotion/styled";
2913
- import { useEffect as useEffect5, useRef as useRef4 } from "react";
3176
+ import { useEffect as useEffect6, useRef as useRef4 } from "react";
2914
3177
  import { jsx as jsx7 } from "@emotion/react/jsx-runtime";
2915
3178
  var Overlay = styled6.div`
2916
3179
  position: fixed;
@@ -2930,7 +3193,7 @@ var Img = styled6.img`
2930
3193
  `;
2931
3194
  var ImageViewer = ({ src, alt, onClose }) => {
2932
3195
  const overlayRef = useRef4(null);
2933
- useEffect5(() => {
3196
+ useEffect6(() => {
2934
3197
  const handleKey = (e) => {
2935
3198
  if (e.key === "Escape")
2936
3199
  onClose();
@@ -2938,7 +3201,7 @@ var ImageViewer = ({ src, alt, onClose }) => {
2938
3201
  document.addEventListener("keydown", handleKey);
2939
3202
  return () => document.removeEventListener("keydown", handleKey);
2940
3203
  }, [onClose]);
2941
- useEffect5(() => {
3204
+ useEffect6(() => {
2942
3205
  overlayRef.current?.focus();
2943
3206
  }, []);
2944
3207
  const stopPropagation = (e) => e.stopPropagation();
@@ -3004,10 +3267,10 @@ var useUserMessageCollapse = ({
3004
3267
  freshContent,
3005
3268
  settledContent
3006
3269
  }) => {
3007
- const [isCollapsible, setIsCollapsible] = useState3(false);
3008
- const [isExpanded, setIsExpanded] = useState3(false);
3009
- const [bodyStackElement, setBodyStackElement] = useState3(null);
3010
- const syncCollapseState = useCallback2(
3270
+ const [isCollapsible, setIsCollapsible] = useState4(false);
3271
+ const [isExpanded, setIsExpanded] = useState4(false);
3272
+ const [bodyStackElement, setBodyStackElement] = useState4(null);
3273
+ const syncCollapseState = useCallback3(
3011
3274
  (element) => {
3012
3275
  const nextCollapsible = enabled && (element?.scrollHeight ?? 0) > USER_MESSAGE_COLLAPSE_HEIGHT_PX;
3013
3276
  setIsCollapsible(nextCollapsible);
@@ -3017,7 +3280,7 @@ var useUserMessageCollapse = ({
3017
3280
  },
3018
3281
  [enabled]
3019
3282
  );
3020
- const bodyStackRef = useCallback2(
3283
+ const bodyStackRef = useCallback3(
3021
3284
  (node) => {
3022
3285
  setBodyStackElement(node);
3023
3286
  syncCollapseState(node);
@@ -3192,7 +3455,7 @@ var ChatMessageItemView = ({
3192
3455
  renderMessageBlock
3193
3456
  }) => {
3194
3457
  const { labels, messageRenderOrder = "blocks-first" } = useChatContext();
3195
- const [activeImage, setActiveImage] = useState3(void 0);
3458
+ const [activeImage, setActiveImage] = useState4(void 0);
3196
3459
  const {
3197
3460
  displayedBlocks,
3198
3461
  displayedContent,
@@ -3209,8 +3472,9 @@ var ChatMessageItemView = ({
3209
3472
  const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
3210
3473
  const hasTextContent = Boolean(settledContent || freshContent || displayedContent);
3211
3474
  const shouldRenderStructuredBlocks = hasStructuredBlocks && !(isAssistantStreaming && hasMarkdownOnlyBlocks && hasTextContent);
3212
- const canSubmitConfirmation = typeof onConfirmationSubmit === "function";
3213
- const canSubmitQuestionnaire = typeof onQuestionnaireSubmit === "function";
3475
+ const isInteractionDisabled = message.status === "stopped" || message.status === "error";
3476
+ const canSubmitConfirmation = typeof onConfirmationSubmit === "function" && !isInteractionDisabled;
3477
+ const canSubmitQuestionnaire = typeof onQuestionnaireSubmit === "function" && !isInteractionDisabled;
3214
3478
  const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
3215
3479
  const isUserMessage = message.role === "user";
3216
3480
  const messageRenderMode = isUserMessage ? "plain-text" : "markdown";
@@ -3291,7 +3555,10 @@ var ChatMessageItemView = ({
3291
3555
  submitFailed: labels.questionnaireSubmitFailed,
3292
3556
  multiSelectHint: labels.questionnaireMultiSelectHint,
3293
3557
  otherOptionLabel: labels.questionnaireOtherOptionLabel,
3294
- otherPlaceholder: labels.questionnaireOtherPlaceholder
3558
+ otherPlaceholder: labels.questionnaireOtherPlaceholder,
3559
+ expired: labels.questionnaireExpired,
3560
+ questionnaireTitle: labels.questionnaireTitle,
3561
+ questionnaireConfirmInTime: labels.questionnaireConfirmInTime
3295
3562
  },
3296
3563
  onSubmit: canSubmitQuestionnaire ? (submission) => onQuestionnaireSubmit({
3297
3564
  ...submission,
@@ -3394,7 +3661,7 @@ var ChatMessageItemView = ({
3394
3661
  })();
3395
3662
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
3396
3663
  /* @__PURE__ */ jsxs5(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
3397
- shouldRenderHeader ? /* @__PURE__ */ jsxs5(Header2, { children: [
3664
+ shouldRenderHeader ? /* @__PURE__ */ jsxs5(Header3, { children: [
3398
3665
  isAssistantStreaming ? /* @__PURE__ */ jsxs5(
3399
3666
  StreamingIndicator,
3400
3667
  {
@@ -3419,7 +3686,7 @@ var ChatMessageItemView = ({
3419
3686
  ) : null,
3420
3687
  isStoppedAssistant ? /* @__PURE__ */ jsx8(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
3421
3688
  ] }) : null,
3422
- /* @__PURE__ */ jsxs5(Content, { "data-testid": "chat-message-content", children: [
3689
+ /* @__PURE__ */ jsxs5(Content2, { "data-testid": "chat-message-content", children: [
3423
3690
  skills.length ? /* @__PURE__ */ jsx8(SkillTagList, { "data-testid": "chat-message-skill-tags", children: skills.map((skill) => /* @__PURE__ */ jsx8(SkillTag, { children: skill }, skill)) }) : null,
3424
3691
  shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsx8(
3425
3692
  ContentStack,
@@ -3499,7 +3766,7 @@ var Bubble = styled7.article`
3499
3766
  border-radius: 16px;
3500
3767
  }
3501
3768
  `;
3502
- var Header2 = styled7.div`
3769
+ var Header3 = styled7.div`
3503
3770
  display: flex;
3504
3771
  align-items: center;
3505
3772
  gap: 10px;
@@ -3541,7 +3808,7 @@ var CollapseToggle = styled7.button`
3541
3808
  color: rgba(255, 255, 255, 0.92);
3542
3809
  }
3543
3810
  `;
3544
- var Content = styled7.div`
3811
+ var Content2 = styled7.div`
3545
3812
  color: rgba(255, 255, 255, 0.92);
3546
3813
  font-size: 14px;
3547
3814
  line-height: 1.6;
@@ -3927,10 +4194,10 @@ var ChatThreadView = ({
3927
4194
  const isPinnedRef = useRef5(true);
3928
4195
  const lastHistoryMessageIdRef = useRef5(latestHistoryMessage?.id);
3929
4196
  const lastStreamingMessageIdRef = useRef5(streamingMessage?.id);
3930
- const [latestTurnMinHeight, setLatestTurnMinHeight] = useState4(0);
3931
- const [isDetached, setIsDetached] = useState4(false);
3932
- const [pendingNewMessageCount, setPendingNewMessageCount] = useState4(0);
3933
- const measureLatestTurnMinHeight = useCallback3(() => {
4197
+ const [latestTurnMinHeight, setLatestTurnMinHeight] = useState5(0);
4198
+ const [isDetached, setIsDetached] = useState5(false);
4199
+ const [pendingNewMessageCount, setPendingNewMessageCount] = useState5(0);
4200
+ const measureLatestTurnMinHeight = useCallback4(() => {
3934
4201
  const container = containerRef.current;
3935
4202
  if (!container)
3936
4203
  return;
@@ -3940,7 +4207,7 @@ var ChatThreadView = ({
3940
4207
  const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
3941
4208
  setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
3942
4209
  }, []);
3943
- const scrollToBottom = useCallback3((force = false) => {
4210
+ const scrollToBottom = useCallback4((force = false) => {
3944
4211
  const container = containerRef.current;
3945
4212
  if (!container)
3946
4213
  return false;
@@ -3956,12 +4223,12 @@ var ChatThreadView = ({
3956
4223
  }
3957
4224
  return true;
3958
4225
  }, []);
3959
- const markThreadPinned = useCallback3(() => {
4226
+ const markThreadPinned = useCallback4(() => {
3960
4227
  isPinnedRef.current = true;
3961
4228
  setIsDetached(false);
3962
4229
  setPendingNewMessageCount(0);
3963
4230
  }, []);
3964
- const scrollToBottomAndPin = useCallback3(
4231
+ const scrollToBottomAndPin = useCallback4(
3965
4232
  (force = false) => {
3966
4233
  const didScroll = scrollToBottom(force);
3967
4234
  if (!didScroll)
@@ -3970,7 +4237,7 @@ var ChatThreadView = ({
3970
4237
  },
3971
4238
  [markThreadPinned, scrollToBottom]
3972
4239
  );
3973
- const handleLoadPreviousMessages = useCallback3(async () => {
4240
+ const handleLoadPreviousMessages = useCallback4(async () => {
3974
4241
  const container = containerRef.current;
3975
4242
  if (!container || !onLoadPreviousMessages || isLoadingPreviousMessages) {
3976
4243
  return;
@@ -3995,7 +4262,7 @@ var ChatThreadView = ({
3995
4262
  nextContainer.scrollTop = nextContainer.scrollHeight - previousScrollHeight + previousScrollTop;
3996
4263
  });
3997
4264
  }, [isLoadingPreviousMessages, onLoadPreviousMessages]);
3998
- const handleContainerScroll = useCallback3(() => {
4265
+ const handleContainerScroll = useCallback4(() => {
3999
4266
  const container = containerRef.current;
4000
4267
  if (!container)
4001
4268
  return;
@@ -4224,13 +4491,13 @@ var ChatThread = () => {
4224
4491
  onLoadMoreHistoryMessages,
4225
4492
  labels
4226
4493
  } = useChatContext();
4227
- const handleRetry = useCallback3(() => {
4494
+ const handleRetry = useCallback4(() => {
4228
4495
  if (!activeSessionId)
4229
4496
  return;
4230
4497
  clearSessionError(activeSessionId);
4231
4498
  void retryRef.current(activeSessionId);
4232
4499
  }, [activeSessionId, clearSessionError, retryRef]);
4233
- const handleQuestionnaireSubmit = useCallback3(
4500
+ const handleQuestionnaireSubmit = useCallback4(
4234
4501
  async (submission) => {
4235
4502
  const sourceSessionId = activeSessionId;
4236
4503
  if (customQuestionnaireSubmit) {
@@ -4265,7 +4532,7 @@ var ChatThread = () => {
4265
4532
  },
4266
4533
  [activeSessionId, customQuestionnaireSubmit, preferredMode, sendRef, updateQA]
4267
4534
  );
4268
- const handleConfirmation = useCallback3(
4535
+ const handleConfirmation = useCallback4(
4269
4536
  async (submission) => {
4270
4537
  const sourceSessionId = activeSessionId;
4271
4538
  if (customConfirmationSubmit) {
@@ -4284,7 +4551,7 @@ var ChatThread = () => {
4284
4551
  },
4285
4552
  [activeSessionId, customConfirmationSubmit, preferredMode, sendRef]
4286
4553
  );
4287
- const handleLoadPreviousMessages = useCallback3(async () => {
4554
+ const handleLoadPreviousMessages = useCallback4(async () => {
4288
4555
  if (!activeSession || !onLoadMoreHistoryMessages || !historyMessagePagination?.hasMorePrevious || !historyMessagePagination.previousCursor || historyMessagePagination.isLoadingPrevious) {
4289
4556
  return;
4290
4557
  }
@@ -4323,13 +4590,14 @@ var ChatThread = () => {
4323
4590
  if (!hasSessions || messages.length === 0 && !streamingMessage) {
4324
4591
  return /* @__PURE__ */ jsx10(ChatThreadEmptyState, {});
4325
4592
  }
4593
+ const displayError = error2 && !(error2.toLowerCase().includes("plan option timeout") || error2.toLowerCase().includes("plan_option_timeout") || error2.toLowerCase().includes("selection expired") || labels.questionnaireExpired && error2.includes(labels.questionnaireExpired)) ? error2 : void 0;
4326
4594
  return /* @__PURE__ */ jsx10(
4327
4595
  ChatThreadView,
4328
4596
  {
4329
4597
  activeMode: preferredMode,
4330
4598
  historyMessages: messages,
4331
4599
  streamingMessage,
4332
- error: error2,
4600
+ error: displayError,
4333
4601
  isLoadingPreviousMessages: historyMessagePagination?.isLoadingPrevious,
4334
4602
  previousMessagesError: historyMessagePagination?.error,
4335
4603
  retryButtonLabel: labels.retryButton,
@@ -4478,7 +4746,7 @@ var ScrollToLatestBadge = styled9.span`
4478
4746
  `;
4479
4747
 
4480
4748
  // src/components/chat-composer/index.tsx
4481
- import { useCallback as useCallback8, useEffect as useEffect9, useLayoutEffect as useLayoutEffect6, useRef as useRef11, useState as useState10 } from "react";
4749
+ import { useCallback as useCallback9, useEffect as useEffect10, useLayoutEffect as useLayoutEffect6, useRef as useRef11, useState as useState11 } from "react";
4482
4750
  import styled14 from "@emotion/styled";
4483
4751
 
4484
4752
  // ../../node_modules/.pnpm/@floating-ui+react@0.27.16_react-dom@18.3.1_react@18.3.1/node_modules/@floating-ui/react/dist/floating-ui.react.mjs
@@ -7076,10 +7344,10 @@ var resolveSendSession = ({
7076
7344
  };
7077
7345
 
7078
7346
  // src/components/chat-composer/hooks/use-chat-composer.ts
7079
- import { useCallback as useCallback7, useEffect as useEffect8, useRef as useRef10, useState as useState8 } from "react";
7347
+ import { useCallback as useCallback8, useEffect as useEffect9, useRef as useRef10, useState as useState9 } from "react";
7080
7348
 
7081
7349
  // src/components/chat-composer/hooks/use-composer-attachments.ts
7082
- import { useEffect as useEffect7, useRef as useRef9, useState as useState7 } from "react";
7350
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState8 } from "react";
7083
7351
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
7084
7352
  var MAX_COMPOSER_ATTACHMENTS = 10;
7085
7353
  var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
@@ -7093,12 +7361,12 @@ var releaseComposerAttachments = (attachments) => {
7093
7361
  attachments.forEach((attachment) => revokeObjectUrl(attachment.previewUrl));
7094
7362
  };
7095
7363
  var useComposerAttachments = () => {
7096
- const [attachments, setAttachments] = useState7([]);
7364
+ const [attachments, setAttachments] = useState8([]);
7097
7365
  const attachmentsRef = useRef9([]);
7098
- useEffect7(() => {
7366
+ useEffect8(() => {
7099
7367
  attachmentsRef.current = attachments;
7100
7368
  }, [attachments]);
7101
- useEffect7(
7369
+ useEffect8(
7102
7370
  () => () => {
7103
7371
  releaseComposerAttachments(attachmentsRef.current);
7104
7372
  },
@@ -7246,16 +7514,16 @@ var useChatComposer = () => {
7246
7514
  const setSessionError = useChatStore((s) => s.setSessionError);
7247
7515
  const clearSessionError = useChatStore((s) => s.clearSessionError);
7248
7516
  const setPreferredMode = useChatStore((s) => s.setPreferredMode);
7249
- const [availableModels, setAvailableModels] = useState8([]);
7250
- const [isModelsLoading, setIsModelsLoading] = useState8(true);
7251
- const [isModelsError, setIsModelsError] = useState8(false);
7252
- const [availableSkills, setAvailableSkills] = useState8(
7517
+ const [availableModels, setAvailableModels] = useState9([]);
7518
+ const [isModelsLoading, setIsModelsLoading] = useState9(true);
7519
+ const [isModelsError, setIsModelsError] = useState9(false);
7520
+ const [availableSkills, setAvailableSkills] = useState9(
7253
7521
  () => getCachedSkills(skillsLoader).skills
7254
7522
  );
7255
- const [isSkillsLoading, setIsSkillsLoading] = useState8(
7523
+ const [isSkillsLoading, setIsSkillsLoading] = useState9(
7256
7524
  () => Boolean(skillsLoader) && !getCachedSkills(skillsLoader).resolved
7257
7525
  );
7258
- const fetchModels = useCallback7(async () => {
7526
+ const fetchModels = useCallback8(async () => {
7259
7527
  setIsModelsLoading(true);
7260
7528
  setIsModelsError(false);
7261
7529
  try {
@@ -7267,16 +7535,16 @@ var useChatComposer = () => {
7267
7535
  setIsModelsLoading(false);
7268
7536
  }
7269
7537
  }, [modelsLoader]);
7270
- useEffect8(() => {
7538
+ useEffect9(() => {
7271
7539
  void fetchModels();
7272
7540
  }, [fetchModels]);
7273
- useEffect8(() => {
7541
+ useEffect9(() => {
7274
7542
  activeSkillsLoaderRef.current = skillsLoader;
7275
7543
  const cachedSkills = getCachedSkills(skillsLoader);
7276
7544
  setAvailableSkills(cachedSkills.skills);
7277
7545
  setIsSkillsLoading(Boolean(skillsLoader) && !cachedSkills.resolved);
7278
7546
  }, [skillsLoader]);
7279
- const fetchSkills = useCallback7(async () => {
7547
+ const fetchSkills = useCallback8(async () => {
7280
7548
  if (!skillsLoader) {
7281
7549
  setAvailableSkills([]);
7282
7550
  setIsSkillsLoading(false);
@@ -7306,35 +7574,35 @@ var useChatComposer = () => {
7306
7574
  }
7307
7575
  }
7308
7576
  }, [skillsLoader]);
7309
- useEffect8(() => {
7577
+ useEffect9(() => {
7310
7578
  void fetchSkills();
7311
7579
  }, [fetchSkills]);
7312
7580
  const hasModels = availableModels.length > 0;
7313
- const [value, setValue] = useState8("");
7314
- const [selectedModel, setSelectedModel] = useState8("");
7315
- const [selectedMode, setSelectedModeLocal] = useState8(DEFAULT_CHAT_AGENT_MODE);
7316
- const [selectedSkills, setSelectedSkills] = useState8([]);
7317
- const [attachmentNotice, setAttachmentNotice] = useState8(null);
7581
+ const [value, setValue] = useState9("");
7582
+ const [selectedModel, setSelectedModel] = useState9("");
7583
+ const [selectedMode, setSelectedModeLocal] = useState9(DEFAULT_CHAT_AGENT_MODE);
7584
+ const [selectedSkills, setSelectedSkills] = useState9([]);
7585
+ const [attachmentNotice, setAttachmentNotice] = useState9(null);
7318
7586
  const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
7319
7587
  const abortControllerBySessionRef = useRef10(/* @__PURE__ */ new Map());
7320
7588
  const stopRequestBySessionRef = useRef10(/* @__PURE__ */ new Map());
7321
7589
  const lastRequestBySessionRef = useRef10(/* @__PURE__ */ new Map());
7322
7590
  const previousActiveSessionIdRef = useRef10(activeSessionId);
7323
- useEffect8(() => {
7591
+ useEffect9(() => {
7324
7592
  setSelectedModel(
7325
7593
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
7326
7594
  );
7327
7595
  }, [availableModels, isModelsLoading]);
7328
- useEffect8(() => {
7596
+ useEffect9(() => {
7329
7597
  setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
7330
7598
  }, [preferredMode]);
7331
- useEffect8(() => {
7599
+ useEffect9(() => {
7332
7600
  if (previousActiveSessionIdRef.current !== activeSessionId) {
7333
7601
  setSelectedSkills([]);
7334
7602
  previousActiveSessionIdRef.current = activeSessionId;
7335
7603
  }
7336
7604
  }, [activeSessionId]);
7337
- useEffect8(() => {
7605
+ useEffect9(() => {
7338
7606
  if (!attachmentNotice)
7339
7607
  return;
7340
7608
  const timeoutId = window.setTimeout(
@@ -7351,11 +7619,11 @@ var useChatComposer = () => {
7351
7619
  window.clearTimeout(stopRequest.timeoutId);
7352
7620
  stopRequest.timeoutId = null;
7353
7621
  };
7354
- const clearStopRequest = useCallback7((sessionId) => {
7622
+ const clearStopRequest = useCallback8((sessionId) => {
7355
7623
  clearStopTimeout(sessionId);
7356
7624
  stopRequestBySessionRef.current.delete(sessionId);
7357
7625
  }, []);
7358
- const moveSessionRuntimeState = useCallback7(
7626
+ const moveSessionRuntimeState = useCallback8(
7359
7627
  (previousSessionId, nextSessionId) => {
7360
7628
  if (previousSessionId === nextSessionId) {
7361
7629
  return;
@@ -7373,7 +7641,7 @@ var useChatComposer = () => {
7373
7641
  },
7374
7642
  []
7375
7643
  );
7376
- const finalizeStop = useCallback7(
7644
+ const finalizeStop = useCallback8(
7377
7645
  (sessionId) => {
7378
7646
  const stopRequest = stopRequestBySessionRef.current.get(sessionId);
7379
7647
  if (stopRequest) {
@@ -7390,7 +7658,7 @@ var useChatComposer = () => {
7390
7658
  },
7391
7659
  [clearStopRequest, finalizeStoppedStreamingMessage]
7392
7660
  );
7393
- const runStream = useCallback7(
7661
+ const runStream = useCallback8(
7394
7662
  async ({
7395
7663
  localSessionId,
7396
7664
  sessionId,
@@ -7515,7 +7783,7 @@ var useChatComposer = () => {
7515
7783
  store
7516
7784
  ]
7517
7785
  );
7518
- const send = useCallback7(
7786
+ const send = useCallback8(
7519
7787
  async (contentOverride, options) => {
7520
7788
  const content = (contentOverride ?? value).trim();
7521
7789
  const includeComposerAttachments = options?.includeComposerAttachments ?? true;
@@ -7595,7 +7863,7 @@ var useChatComposer = () => {
7595
7863
  store
7596
7864
  ]
7597
7865
  );
7598
- const openSkillPicker = useCallback7(() => {
7866
+ const openSkillPicker = useCallback8(() => {
7599
7867
  setValue((current) => {
7600
7868
  const matchedSkillQuery = current.match(/(^|\s)\/([^\s/]*)$/);
7601
7869
  if (matchedSkillQuery && matchedSkillQuery.index !== void 0) {
@@ -7608,7 +7876,7 @@ var useChatComposer = () => {
7608
7876
  return /\s$/.test(current) ? `${current}/` : `${current} /`;
7609
7877
  });
7610
7878
  }, []);
7611
- const stopSession = useCallback7(
7879
+ const stopSession = useCallback8(
7612
7880
  async (sessionId) => {
7613
7881
  const storeState = store.getState();
7614
7882
  const isSessionStreaming = storeState.isStreamingBySession[sessionId] ?? false;
@@ -7718,14 +7986,14 @@ var useChatComposer = () => {
7718
7986
  };
7719
7987
 
7720
7988
  // src/components/chat-composer/components/chat-composer-attachment-list.tsx
7721
- import { useState as useState9 } from "react";
7989
+ import { useState as useState10 } from "react";
7722
7990
  import styled10 from "@emotion/styled";
7723
7991
  import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs9 } from "@emotion/react/jsx-runtime";
7724
7992
  var ChatComposerAttachmentList = ({
7725
7993
  attachments,
7726
7994
  onRemoveAttachment
7727
7995
  }) => {
7728
- const [activeImage, setActiveImage] = useState9(null);
7996
+ const [activeImage, setActiveImage] = useState10(null);
7729
7997
  if (!attachments.length) {
7730
7998
  return null;
7731
7999
  }
@@ -8391,6 +8659,7 @@ var ChatComposerView = ({
8391
8659
  modelLoadingLabel,
8392
8660
  modelLoadFailedLabel,
8393
8661
  modelUnavailableLabel,
8662
+ modeAccessory,
8394
8663
  onValueChange,
8395
8664
  onPickImages,
8396
8665
  onPasteImages,
@@ -8406,9 +8675,9 @@ var ChatComposerView = ({
8406
8675
  }) => {
8407
8676
  const imageInputRef = useRef11(null);
8408
8677
  const inputRef = useRef11(null);
8409
- const [isComposerExpandable, setIsComposerExpandable] = useState10(false);
8410
- const [isComposerExpanded, setIsComposerExpanded] = useState10(false);
8411
- const [activeSkillNavigation, setActiveSkillNavigation] = useState10({
8678
+ const [isComposerExpandable, setIsComposerExpandable] = useState11(false);
8679
+ const [isComposerExpanded, setIsComposerExpanded] = useState11(false);
8680
+ const [activeSkillNavigation, setActiveSkillNavigation] = useState11({
8412
8681
  queryKey: "",
8413
8682
  index: 0
8414
8683
  });
@@ -8451,13 +8720,13 @@ var ChatComposerView = ({
8451
8720
  ],
8452
8721
  whileElementsMounted: autoUpdate
8453
8722
  });
8454
- const setSkillMenuReference = useCallback8(
8723
+ const setSkillMenuReference = useCallback9(
8455
8724
  (element) => {
8456
8725
  refs.setReference(element);
8457
8726
  },
8458
8727
  [refs]
8459
8728
  );
8460
- const setSkillMenuFloating = useCallback8(
8729
+ const setSkillMenuFloating = useCallback9(
8461
8730
  (element) => {
8462
8731
  refs.setFloating(element);
8463
8732
  },
@@ -8614,7 +8883,7 @@ var ChatComposerView = ({
8614
8883
  }
8615
8884
  )
8616
8885
  ] }),
8617
- /* @__PURE__ */ jsxs11(Footer, { children: [
8886
+ /* @__PURE__ */ jsxs11(Footer2, { children: [
8618
8887
  /* @__PURE__ */ jsxs11(LeadingActions, { "data-testid": "chat-composer-leading-actions", children: [
8619
8888
  enableImageAttachments ? /* @__PURE__ */ jsx16(
8620
8889
  AttachButton,
@@ -8644,7 +8913,8 @@ var ChatComposerView = ({
8644
8913
  labels: modeLabels,
8645
8914
  onChange: onSelectedModeChange
8646
8915
  }
8647
- )
8916
+ ),
8917
+ modeAccessory
8648
8918
  ] }),
8649
8919
  /* @__PURE__ */ jsxs11(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8650
8920
  /* @__PURE__ */ jsx16(
@@ -8702,22 +8972,35 @@ var ChatComposerView = ({
8702
8972
  ) }) : null
8703
8973
  ] });
8704
8974
  };
8705
- var ChatComposer = () => {
8706
- const { labels, sendRef, retryRef, stopRef, enableImageAttachments } = useChatContext();
8975
+ var ChatComposer = ({
8976
+ renderModeAccessory
8977
+ } = {}) => {
8978
+ const {
8979
+ labels,
8980
+ sendRef,
8981
+ retryRef,
8982
+ registerStopSession = () => {
8983
+ },
8984
+ enableImageAttachments
8985
+ } = useChatContext();
8707
8986
  const { state, actions } = useChatComposer();
8708
8987
  const { send, retry } = actions;
8709
- useEffect9(() => {
8988
+ useEffect10(() => {
8710
8989
  sendRef.current = send;
8711
8990
  retryRef.current = async (sessionId) => {
8712
8991
  retry(sessionId);
8713
8992
  };
8714
- stopRef.current = actions.stopSession;
8715
- }, [actions.stopSession, retry, retryRef, send, sendRef, stopRef]);
8993
+ registerStopSession(actions.stopSession);
8994
+ }, [actions.stopSession, registerStopSession, retry, retryRef, send, sendRef]);
8716
8995
  const modeLabels = {
8717
8996
  ask: labels.modeLabelAsk,
8718
8997
  plan: labels.modeLabelPlan,
8719
8998
  agent: labels.modeLabelAgent
8720
8999
  };
9000
+ const modeAccessory = renderModeAccessory?.({
9001
+ selectedMode: state.selectedMode,
9002
+ disabled: state.isStreaming
9003
+ });
8721
9004
  return /* @__PURE__ */ jsx16(
8722
9005
  ChatComposerView,
8723
9006
  {
@@ -8747,6 +9030,7 @@ var ChatComposer = () => {
8747
9030
  modelLoadingLabel: labels.modelLoading,
8748
9031
  modelLoadFailedLabel: labels.modelLoadFailed,
8749
9032
  modelUnavailableLabel: labels.modelUnavailable,
9033
+ modeAccessory,
8750
9034
  onValueChange: actions.setValue,
8751
9035
  onPickImages: actions.pickImages,
8752
9036
  onPasteImages: actions.pasteImages,
@@ -8954,7 +9238,7 @@ var ComposerExpandButton = styled14.button`
8954
9238
  color: rgba(255, 255, 255, 0.92);
8955
9239
  }
8956
9240
  `;
8957
- var Footer = styled14.div`
9241
+ var Footer2 = styled14.div`
8958
9242
  grid-area: footer;
8959
9243
  display: flex;
8960
9244
  align-items: center;
@@ -9035,26 +9319,37 @@ var SkillButton = styled14.button`
9035
9319
  `;
9036
9320
 
9037
9321
  // src/components/chat-conversation-list/index.tsx
9038
- import { useEffect as useEffect10, useRef as useRef12 } from "react";
9322
+ import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef12 } from "react";
9039
9323
  import styled16 from "@emotion/styled";
9040
9324
 
9041
9325
  // src/components/chat-conversation-list/components/chat-session-item.tsx
9042
9326
  import { memo as memo2 } from "react";
9043
9327
  import styled15 from "@emotion/styled";
9044
- import { jsx as jsx17 } from "@emotion/react/jsx-runtime";
9045
- var ChatSessionItem = memo2(({ session, isActive, onClick }) => {
9046
- return /* @__PURE__ */ jsx17(
9047
- SessionButton,
9048
- {
9049
- type: "button",
9050
- "data-active": isActive,
9051
- "data-testid": `chat-session-item-${session.sessionId}`,
9052
- onClick: () => onClick(session.sessionId),
9053
- children: /* @__PURE__ */ jsx17(SessionTitle, { children: session.title })
9054
- }
9055
- );
9056
- });
9328
+ import { jsx as jsx17, jsxs as jsxs12 } from "@emotion/react/jsx-runtime";
9329
+ var ChatSessionItem = memo2(
9330
+ ({ session, isActive, onClick, actions }) => {
9331
+ return /* @__PURE__ */ jsxs12(SessionRow, { "data-active": isActive, children: [
9332
+ /* @__PURE__ */ jsx17(
9333
+ SessionButton,
9334
+ {
9335
+ type: "button",
9336
+ "data-active": isActive,
9337
+ "data-testid": `chat-session-item-${session.sessionId}`,
9338
+ onClick: () => onClick(session.sessionId),
9339
+ children: /* @__PURE__ */ jsx17(SessionTitle, { children: session.title })
9340
+ }
9341
+ ),
9342
+ actions ? /* @__PURE__ */ jsx17(SessionActions, { "data-testid": `chat-session-item-actions-${session.sessionId}`, children: actions }) : null
9343
+ ] });
9344
+ }
9345
+ );
9057
9346
  ChatSessionItem.displayName = "ChatSessionItem";
9347
+ var SessionRow = styled15.div`
9348
+ display: flex;
9349
+ align-items: center;
9350
+ gap: 8px;
9351
+ min-width: 0;
9352
+ `;
9058
9353
  var SessionTitle = styled15.span`
9059
9354
  display: block;
9060
9355
  min-width: 0;
@@ -9063,6 +9358,8 @@ var SessionTitle = styled15.span`
9063
9358
  white-space: nowrap;
9064
9359
  `;
9065
9360
  var SessionButton = styled15.button`
9361
+ flex: 1;
9362
+ min-width: 0;
9066
9363
  border: 1px solid transparent;
9067
9364
  border-radius: 12px;
9068
9365
  padding: 12px;
@@ -9095,6 +9392,12 @@ var SessionButton = styled15.button`
9095
9392
  background: rgba(255, 255, 255, 0.08);
9096
9393
  }
9097
9394
  `;
9395
+ var SessionActions = styled15.div`
9396
+ display: flex;
9397
+ align-items: center;
9398
+ gap: 6px;
9399
+ flex: 0 0 auto;
9400
+ `;
9098
9401
 
9099
9402
  // src/components/chat-conversation-list/lib/history-session-selection.ts
9100
9403
  var shouldLoadHistorySessionMessages = ({
@@ -9115,7 +9418,7 @@ var shouldLoadHistorySessionMessages = ({
9115
9418
  };
9116
9419
 
9117
9420
  // src/components/chat-conversation-list/index.tsx
9118
- import { jsx as jsx18, jsxs as jsxs12 } from "@emotion/react/jsx-runtime";
9421
+ import { jsx as jsx18, jsxs as jsxs13 } from "@emotion/react/jsx-runtime";
9119
9422
  var SCROLL_LOAD_MORE_THRESHOLD_PX = 80;
9120
9423
  var shouldLoadMoreSessions = ({
9121
9424
  scrollTop,
@@ -9124,12 +9427,37 @@ var shouldLoadMoreSessions = ({
9124
9427
  threshold = SCROLL_LOAD_MORE_THRESHOLD_PX
9125
9428
  }) => scrollHeight - scrollTop - clientHeight <= threshold;
9126
9429
  var isHistorySessionMessagesPage = (value) => typeof value === "object" && value !== null && Array.isArray(value.messages);
9430
+ var matchesHydratedHistorySessions = (renderedHistorySessionIds, historySessionIds) => {
9431
+ if (renderedHistorySessionIds.length === historySessionIds.length) {
9432
+ return renderedHistorySessionIds.every(
9433
+ (sessionId, index3) => sessionId === historySessionIds[index3]
9434
+ );
9435
+ }
9436
+ if (renderedHistorySessionIds.length !== historySessionIds.length + 1) {
9437
+ return false;
9438
+ }
9439
+ const matchesWithoutLeadingExtra = renderedHistorySessionIds.slice(1).every((sessionId, index3) => sessionId === historySessionIds[index3]);
9440
+ if (matchesWithoutLeadingExtra) {
9441
+ return true;
9442
+ }
9443
+ return renderedHistorySessionIds.slice(0, -1).every((sessionId, index3) => sessionId === historySessionIds[index3]);
9444
+ };
9127
9445
  var ChatConversationList = () => {
9128
- const { labels, historySessionList, onLoadMoreSessions, onSelectHistorySession, store } = useChatContext();
9446
+ const {
9447
+ labels,
9448
+ historySessionList,
9449
+ onLoadMoreSessions,
9450
+ onSelectHistorySession,
9451
+ renderSessionItemActions,
9452
+ stopSession = async (_sessionId) => {
9453
+ },
9454
+ store
9455
+ } = useChatContext();
9129
9456
  const localSessions = useChatStore((s) => s.sessions);
9130
9457
  const activeSessionId = useChatStore((s) => s.activeSessionId);
9131
9458
  const startNewChat = useChatStore((s) => s.startNewChat);
9132
9459
  const setActiveSession = useChatStore((s) => s.setActiveSession);
9460
+ const isStreamingBySession = useChatStore((s) => s.isStreamingBySession);
9133
9461
  const hydrateHistorySessions = useChatStore((s) => s.hydrateHistorySessions);
9134
9462
  const hydrateHistorySessionMessages = useChatStore((s) => s.hydrateHistorySessionMessages);
9135
9463
  const hydrateHistorySessionMessagesPage = useChatStore((s) => s.hydrateHistorySessionMessagesPage);
@@ -9138,12 +9466,13 @@ var ChatConversationList = () => {
9138
9466
  );
9139
9467
  const isLoadingMoreRef = useRef12(false);
9140
9468
  const hasSeenLoadingMoreRef = useRef12(false);
9141
- useEffect10(() => {
9469
+ const listRef = useRef12(null);
9470
+ useEffect11(() => {
9142
9471
  if (!historySessionList)
9143
9472
  return;
9144
9473
  hydrateHistorySessions(historySessionList.sessions);
9145
9474
  }, [historySessionList, hydrateHistorySessions]);
9146
- useEffect10(() => {
9475
+ useEffect11(() => {
9147
9476
  if (historySessionList?.isLoading) {
9148
9477
  hasSeenLoadingMoreRef.current = true;
9149
9478
  return;
@@ -9153,28 +9482,55 @@ var ChatConversationList = () => {
9153
9482
  isLoadingMoreRef.current = false;
9154
9483
  }
9155
9484
  }, [historySessionList?.isLoading]);
9156
- useEffect10(() => {
9485
+ useEffect11(() => {
9157
9486
  isLoadingMoreRef.current = false;
9158
9487
  hasSeenLoadingMoreRef.current = false;
9159
9488
  }, [historySessionList?.sessions.length, historySessionList?.hasMore]);
9160
9489
  const sessions = localSessions;
9161
- const handleSessionListScroll = (event) => {
9162
- if (!historySessionList?.hasMore || historySessionList.isLoading || !onLoadMoreSessions || isLoadingMoreRef.current) {
9490
+ const hasMoreSessions = historySessionList?.hasMore ?? false;
9491
+ const isHistorySessionListLoading = historySessionList?.isLoading ?? false;
9492
+ const renderedHistorySessionIds = sessions.filter((session) => !isDraftChatSessionId(session.sessionId)).map((session) => session.sessionId);
9493
+ const isHistorySessionListHydrated = !historySessionList || matchesHydratedHistorySessions(
9494
+ renderedHistorySessionIds,
9495
+ historySessionList.sessions.map((session) => session.sessionId)
9496
+ );
9497
+ const requestLoadMoreSessions = useCallback10(() => {
9498
+ if (!hasMoreSessions || isHistorySessionListLoading || !onLoadMoreSessions || isLoadingMoreRef.current) {
9163
9499
  return;
9164
9500
  }
9165
- const target = event.currentTarget;
9166
- if (shouldLoadMoreSessions({
9167
- scrollTop: target.scrollTop,
9168
- clientHeight: target.clientHeight,
9169
- scrollHeight: target.scrollHeight
9170
- })) {
9171
- isLoadingMoreRef.current = true;
9172
- void Promise.resolve(onLoadMoreSessions()).catch(() => {
9173
- isLoadingMoreRef.current = false;
9174
- hasSeenLoadingMoreRef.current = false;
9175
- });
9501
+ isLoadingMoreRef.current = true;
9502
+ void Promise.resolve(onLoadMoreSessions()).catch(() => {
9503
+ isLoadingMoreRef.current = false;
9504
+ hasSeenLoadingMoreRef.current = false;
9505
+ });
9506
+ }, [hasMoreSessions, isHistorySessionListLoading, onLoadMoreSessions]);
9507
+ const maybeLoadMoreSessions = useCallback10(
9508
+ (target) => {
9509
+ if (target.clientHeight <= 0 || target.scrollHeight <= 0) {
9510
+ return;
9511
+ }
9512
+ if (shouldLoadMoreSessions({
9513
+ scrollTop: target.scrollTop,
9514
+ clientHeight: target.clientHeight,
9515
+ scrollHeight: target.scrollHeight
9516
+ })) {
9517
+ requestLoadMoreSessions();
9518
+ }
9519
+ },
9520
+ [requestLoadMoreSessions]
9521
+ );
9522
+ useEffect11(() => {
9523
+ if (!listRef.current || !isHistorySessionListHydrated) {
9524
+ return;
9176
9525
  }
9177
- };
9526
+ maybeLoadMoreSessions(listRef.current);
9527
+ }, [isHistorySessionListHydrated, hasMoreSessions, localSessions.length, maybeLoadMoreSessions]);
9528
+ const handleSessionListScroll = useCallback10(
9529
+ (event) => {
9530
+ maybeLoadMoreSessions(event.currentTarget);
9531
+ },
9532
+ [maybeLoadMoreSessions]
9533
+ );
9178
9534
  const handleSelectSession = async (sessionId) => {
9179
9535
  setActiveSession(sessionId);
9180
9536
  const session = sessions.find((item) => item.sessionId === sessionId);
@@ -9211,18 +9567,26 @@ var ChatConversationList = () => {
9211
9567
  );
9212
9568
  }
9213
9569
  };
9214
- return /* @__PURE__ */ jsxs12(Container3, { children: [
9215
- /* @__PURE__ */ jsxs12(Toolbar, { children: [
9570
+ return /* @__PURE__ */ jsxs13(Container3, { children: [
9571
+ /* @__PURE__ */ jsxs13(Toolbar, { children: [
9216
9572
  /* @__PURE__ */ jsx18(Title3, { children: labels.sessionsTitle }),
9217
9573
  /* @__PURE__ */ jsx18(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: startNewChat, children: labels.newChat })
9218
9574
  ] }),
9219
- /* @__PURE__ */ jsxs12(List2, { "data-testid": "chat-session-list", onScroll: handleSessionListScroll, children: [
9575
+ /* @__PURE__ */ jsxs13(List2, { ref: listRef, "data-testid": "chat-session-list", onScroll: handleSessionListScroll, children: [
9220
9576
  sessions.map((session) => /* @__PURE__ */ jsx18(
9221
9577
  ChatSessionItem,
9222
9578
  {
9223
9579
  session,
9224
9580
  isActive: activeSessionId === session.sessionId,
9225
- onClick: (sessionId) => void handleSelectSession(sessionId)
9581
+ onClick: (sessionId) => void handleSelectSession(sessionId),
9582
+ actions: isDraftChatSessionId(session.sessionId) ? void 0 : renderSessionItemActions?.({
9583
+ session,
9584
+ isActive: activeSessionId === session.sessionId,
9585
+ isStreaming: isStreamingBySession[session.sessionId] ?? false,
9586
+ selectSession: () => handleSelectSession(session.sessionId),
9587
+ startNewChat,
9588
+ stopSession: () => stopSession(session.sessionId)
9589
+ })
9226
9590
  },
9227
9591
  session.sessionId
9228
9592
  )),
@@ -9235,6 +9599,7 @@ var ChatConversationList = () => {
9235
9599
  var Container3 = styled16.aside`
9236
9600
  width: 280px;
9237
9601
  min-width: 280px;
9602
+ min-height: 0;
9238
9603
  border-right: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
9239
9604
  display: flex;
9240
9605
  flex-direction: column;
@@ -9280,6 +9645,8 @@ var CreateButton = styled16.button`
9280
9645
  }
9281
9646
  `;
9282
9647
  var List2 = styled16.div`
9648
+ flex: 1 1 0;
9649
+ min-height: 0;
9283
9650
  padding: 0 12px 16px;
9284
9651
  display: flex;
9285
9652
  flex-direction: column;
@@ -9293,7 +9660,7 @@ var StateRow = styled16.div`
9293
9660
  `;
9294
9661
 
9295
9662
  // src/components/ai-chat/index.tsx
9296
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs13 } from "@emotion/react/jsx-runtime";
9663
+ import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "@emotion/react/jsx-runtime";
9297
9664
  var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9298
9665
  "svg",
9299
9666
  {
@@ -9304,7 +9671,7 @@ var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9304
9671
  fill: "none",
9305
9672
  style: { display: "block" },
9306
9673
  xmlns: "http://www.w3.org/2000/svg",
9307
- children: /* @__PURE__ */ jsxs13(
9674
+ children: /* @__PURE__ */ jsxs14(
9308
9675
  "g",
9309
9676
  {
9310
9677
  transform: "translate(1.8909 2.0364)",
@@ -9313,7 +9680,7 @@ var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9313
9680
  strokeLinejoin: "round",
9314
9681
  strokeWidth: "1.36533333",
9315
9682
  children: [
9316
- /* @__PURE__ */ jsxs13("g", { transform: "translate(9.8909 2.3273) rotate(-315) translate(-9.8909 -2.3273) translate(8.2909 0.7273)", children: [
9683
+ /* @__PURE__ */ jsxs14("g", { transform: "translate(9.8909 2.3273) rotate(-315) translate(-9.8909 -2.3273) translate(8.2909 0.7273)", children: [
9317
9684
  /* @__PURE__ */ jsx19("path", { d: "M0 0C0 0 1.06666667 1.06666667 3.2 3.2" }),
9318
9685
  /* @__PURE__ */ jsx19("path", { d: "M3.2 0C3.2 0 2.13333333 1.06666667 0 3.2" })
9319
9686
  ] }),
@@ -9341,7 +9708,8 @@ var AiChatWorkspaceContent = ({
9341
9708
  showNewChatButton,
9342
9709
  renderNewChatTrigger,
9343
9710
  showComposerOnlyBeforeFirstMessage = false,
9344
- onConversationStartedChange
9711
+ onConversationStartedChange,
9712
+ renderComposerModeAccessory
9345
9713
  }) => {
9346
9714
  const isConversationStarted = useChatStore(
9347
9715
  (state) => hasStartedConversation({
@@ -9352,20 +9720,21 @@ var AiChatWorkspaceContent = ({
9352
9720
  })
9353
9721
  );
9354
9722
  const shouldShowComposerOnly = showComposerOnlyBeforeFirstMessage && !showConversationList && !isConversationStarted;
9355
- useEffect11(() => {
9723
+ useEffect12(() => {
9356
9724
  onConversationStartedChange?.(isConversationStarted);
9357
9725
  }, [isConversationStarted, onConversationStartedChange]);
9358
- return /* @__PURE__ */ jsxs13(Root, { "data-testid": "ai-chat", children: [
9726
+ return /* @__PURE__ */ jsxs14(Root, { "data-testid": "ai-chat", children: [
9359
9727
  showConversationList ? /* @__PURE__ */ jsx19(ChatConversationList, {}) : null,
9360
- /* @__PURE__ */ jsxs13(Workspace, { children: [
9728
+ /* @__PURE__ */ jsxs14(Workspace, { children: [
9361
9729
  showNewChatButton && !showConversationList && !shouldShowComposerOnly ? /* @__PURE__ */ jsx19(QuickActions, { renderNewChatTrigger }) : null,
9362
9730
  shouldShowComposerOnly ? null : /* @__PURE__ */ jsx19(ChatThread, {}),
9363
- /* @__PURE__ */ jsx19(ChatComposer, {})
9731
+ /* @__PURE__ */ jsx19(ChatComposer, { renderModeAccessory: renderComposerModeAccessory })
9364
9732
  ] })
9365
9733
  ] });
9366
9734
  };
9367
9735
  var QuickActions = ({ renderNewChatTrigger }) => {
9368
- const { labels, stopRef, store } = useChatContext();
9736
+ const { labels, stopSession = async (_sessionId) => {
9737
+ }, store } = useChatContext();
9369
9738
  const startNewChat = useChatStore((state) => state.startNewChat);
9370
9739
  const activeSessionId = useChatStore((state) => state.activeSessionId);
9371
9740
  const isActiveSessionStreaming = useChatStore(
@@ -9384,14 +9753,14 @@ var QuickActions = ({ renderNewChatTrigger }) => {
9384
9753
  if (!currentSessionId || !isCurrentSessionStreaming) {
9385
9754
  return;
9386
9755
  }
9387
- await stopRef.current(currentSessionId);
9756
+ await stopSession(currentSessionId);
9388
9757
  };
9389
9758
  const handleStartNewChat = async () => {
9390
9759
  const currentState = store.getState();
9391
9760
  const currentSessionId = currentState.activeSessionId;
9392
9761
  const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
9393
9762
  if (currentSessionId && isCurrentSessionStreaming) {
9394
- void stopRef.current(currentSessionId);
9763
+ void stopSession(currentSessionId);
9395
9764
  }
9396
9765
  createNewSession();
9397
9766
  };
@@ -9435,6 +9804,7 @@ var AiChat = ({
9435
9804
  renderNewChatTrigger,
9436
9805
  showComposerOnlyBeforeFirstMessage = false,
9437
9806
  onConversationStartedChange,
9807
+ renderComposerModeAccessory,
9438
9808
  ...providerProps
9439
9809
  }) => /* @__PURE__ */ jsx19(
9440
9810
  ConfigProvider,
@@ -9478,7 +9848,8 @@ var AiChat = ({
9478
9848
  showNewChatButton,
9479
9849
  renderNewChatTrigger,
9480
9850
  showComposerOnlyBeforeFirstMessage,
9481
- onConversationStartedChange
9851
+ onConversationStartedChange,
9852
+ renderComposerModeAccessory
9482
9853
  }
9483
9854
  ) })
9484
9855
  }