@xinghunm/ai-chat 0.2.0 → 0.2.2

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.
Files changed (3) hide show
  1. package/dist/index.js +471 -334
  2. package/dist/index.mjs +443 -306
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ __export(src_exports, {
45
45
  module.exports = __toCommonJS(src_exports);
46
46
 
47
47
  // src/components/ai-chat/index.tsx
48
- var import_styled18 = __toESM(require("@emotion/styled"));
48
+ var import_styled17 = __toESM(require("@emotion/styled"));
49
49
  var import_compass_ui4 = require("@xinghunm/compass-ui");
50
50
 
51
51
  // src/components/ai-chat-provider/index.tsx
@@ -636,8 +636,8 @@ var AiChatProvider = (props) => {
636
636
  };
637
637
 
638
638
  // src/components/chat-thread/index.tsx
639
- var import_react11 = require("react");
640
- var import_styled10 = __toESM(require("@emotion/styled"));
639
+ var import_react10 = require("react");
640
+ var import_styled9 = __toESM(require("@emotion/styled"));
641
641
 
642
642
  // src/context/use-chat-context.ts
643
643
  var import_react3 = require("react");
@@ -655,22 +655,6 @@ var useChatStore = (selector) => {
655
655
 
656
656
  // src/components/chat-thread/lib/chat-thread.ts
657
657
  var CHAT_THREAD_SCROLL_TOP_GAP = 16;
658
- var findLatestUserMessageId = (historyMessages) => {
659
- for (let index = historyMessages.length - 1; index >= 0; index -= 1) {
660
- if (historyMessages[index]?.role === "user") {
661
- return historyMessages[index]?.id;
662
- }
663
- }
664
- return void 0;
665
- };
666
- var calculateChatThreadScrollSpacerHeight = ({
667
- containerClientHeight,
668
- containerScrollHeight,
669
- targetOffsetTop
670
- }) => Math.max(
671
- 0,
672
- targetOffsetTop - CHAT_THREAD_SCROLL_TOP_GAP - (containerScrollHeight - containerClientHeight)
673
- );
674
658
 
675
659
  // src/components/chat-thread/components/chat-message-item.tsx
676
660
  var import_react8 = require("react");
@@ -729,6 +713,64 @@ var getNextDisplayedUnitCount = ({
729
713
  var splitMarkdownBlocks = (content) => content.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
730
714
 
731
715
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
716
+ var createRevealState = ({
717
+ isAssistantStreaming,
718
+ targetUnitCount
719
+ }) => {
720
+ const initialDisplayedUnitCount = isAssistantStreaming ? 0 : targetUnitCount;
721
+ return {
722
+ batchedTargetUnitCount: initialDisplayedUnitCount,
723
+ displayedUnitCount: initialDisplayedUnitCount,
724
+ isFreshBlockActive: false
725
+ };
726
+ };
727
+ var revealReducer = (state, action) => {
728
+ switch (action.type) {
729
+ case "reset-message":
730
+ return createRevealState(action);
731
+ case "commit-batched-target": {
732
+ const nextDisplayedUnitCount = action.role === "assistant" ? getNextDisplayedUnitCount({
733
+ currentUnits: state.displayedUnitCount,
734
+ targetUnits: action.nextTargetUnitCount,
735
+ isStreaming: action.isAssistantStreaming,
736
+ minimumStep: state.displayedUnitCount > 0 && action.isAssistantStreaming ? 2 : 1
737
+ }) : action.nextTargetUnitCount;
738
+ return {
739
+ ...state,
740
+ batchedTargetUnitCount: action.nextTargetUnitCount,
741
+ displayedUnitCount: nextDisplayedUnitCount
742
+ };
743
+ }
744
+ case "set-fresh-block-active":
745
+ return state.isFreshBlockActive === action.isActive ? state : {
746
+ ...state,
747
+ isFreshBlockActive: action.isActive
748
+ };
749
+ case "sync-displayed-target":
750
+ return state.displayedUnitCount === state.batchedTargetUnitCount ? state : {
751
+ ...state,
752
+ displayedUnitCount: state.batchedTargetUnitCount
753
+ };
754
+ case "advance-reveal": {
755
+ if (state.displayedUnitCount >= state.batchedTargetUnitCount) {
756
+ return state;
757
+ }
758
+ return {
759
+ ...state,
760
+ displayedUnitCount: Math.min(
761
+ state.batchedTargetUnitCount,
762
+ getNextDisplayedUnitCount({
763
+ currentUnits: state.displayedUnitCount,
764
+ targetUnits: state.batchedTargetUnitCount,
765
+ isStreaming: action.isAssistantStreaming
766
+ })
767
+ )
768
+ };
769
+ }
770
+ default:
771
+ return state;
772
+ }
773
+ };
732
774
  var useChatMessageReveal = (message) => {
733
775
  const isAssistantStreaming = message.role === "assistant" && message.status === "streaming";
734
776
  const targetContent = message.content || "";
@@ -736,41 +778,47 @@ var useChatMessageReveal = (message) => {
736
778
  const pendingTargetUnitCountRef = (0, import_react5.useRef)(targetUnits.length);
737
779
  const batchedTargetUnitCountRef = (0, import_react5.useRef)(isAssistantStreaming ? 0 : targetUnits.length);
738
780
  const inputBatchTimeoutRef = (0, import_react5.useRef)(null);
739
- const [batchedTargetUnitCount, setBatchedTargetUnitCount] = (0, import_react5.useState)(
740
- () => isAssistantStreaming ? 0 : targetUnits.length
741
- );
742
781
  const lastDisplayedBlockCountRef = (0, import_react5.useRef)(0);
743
- const [displayedUnitCount, setDisplayedUnitCount] = (0, import_react5.useState)(
744
- () => isAssistantStreaming ? 0 : targetUnits.length
782
+ const previousMessageIdRef = (0, import_react5.useRef)(message.id);
783
+ const [state, dispatch] = (0, import_react5.useReducer)(
784
+ revealReducer,
785
+ {
786
+ isAssistantStreaming,
787
+ targetUnitCount: targetUnits.length
788
+ },
789
+ createRevealState
745
790
  );
746
- const [isFreshBlockActive, setIsFreshBlockActive] = (0, import_react5.useState)(false);
791
+ const { batchedTargetUnitCount, displayedUnitCount, isFreshBlockActive } = state;
747
792
  const commitBatchedTargetUnitCount = (0, import_react5.useCallback)(
748
793
  (nextTargetUnitCount) => {
749
794
  batchedTargetUnitCountRef.current = nextTargetUnitCount;
750
- setBatchedTargetUnitCount(nextTargetUnitCount);
751
- setDisplayedUnitCount(
752
- (current) => message.role === "assistant" ? getNextDisplayedUnitCount({
753
- currentUnits: current,
754
- targetUnits: nextTargetUnitCount,
755
- isStreaming: isAssistantStreaming,
756
- minimumStep: current > 0 && isAssistantStreaming ? 2 : 1
757
- }) : nextTargetUnitCount
758
- );
795
+ dispatch({
796
+ type: "commit-batched-target",
797
+ isAssistantStreaming,
798
+ nextTargetUnitCount,
799
+ role: message.role
800
+ });
759
801
  },
760
802
  [isAssistantStreaming, message.role]
761
803
  );
762
804
  (0, import_react5.useEffect)(() => {
805
+ if (previousMessageIdRef.current === message.id) {
806
+ return;
807
+ }
808
+ previousMessageIdRef.current = message.id;
763
809
  pendingTargetUnitCountRef.current = targetUnits.length;
764
810
  batchedTargetUnitCountRef.current = isAssistantStreaming ? 0 : targetUnits.length;
765
- setBatchedTargetUnitCount(batchedTargetUnitCountRef.current);
766
- setDisplayedUnitCount(isAssistantStreaming ? 0 : targetUnits.length);
767
811
  lastDisplayedBlockCountRef.current = 0;
768
812
  if (inputBatchTimeoutRef.current !== null) {
769
813
  window.clearTimeout(inputBatchTimeoutRef.current);
770
814
  inputBatchTimeoutRef.current = null;
771
815
  }
772
- setIsFreshBlockActive(false);
773
- }, [message.id, isAssistantStreaming, targetUnits.length]);
816
+ dispatch({
817
+ type: "reset-message",
818
+ isAssistantStreaming,
819
+ targetUnitCount: targetUnits.length
820
+ });
821
+ }, [isAssistantStreaming, message.id, targetUnits.length]);
774
822
  (0, import_react5.useEffect)(() => {
775
823
  pendingTargetUnitCountRef.current = targetUnits.length;
776
824
  if (message.role !== "assistant" || !isAssistantStreaming) {
@@ -813,9 +861,9 @@ var useChatMessageReveal = (message) => {
813
861
  if (!hasNewDisplayedBlock) {
814
862
  return;
815
863
  }
816
- setIsFreshBlockActive(true);
864
+ dispatch({ type: "set-fresh-block-active", isActive: true });
817
865
  const timer = window.setTimeout(() => {
818
- setIsFreshBlockActive(false);
866
+ dispatch({ type: "set-fresh-block-active", isActive: false });
819
867
  }, STREAM_FRESH_BLOCK_SETTLE_MS);
820
868
  return () => {
821
869
  window.clearTimeout(timer);
@@ -825,37 +873,34 @@ var useChatMessageReveal = (message) => {
825
873
  const shouldAnimateReveal = message.role === "assistant" && displayedUnitCount < batchedTargetUnitCount && (isAssistantStreaming || displayedUnitCount > 0);
826
874
  if (!shouldAnimateReveal) {
827
875
  if (displayedUnitCount !== batchedTargetUnitCount) {
828
- setDisplayedUnitCount(batchedTargetUnitCount);
876
+ dispatch({ type: "sync-displayed-target" });
829
877
  }
830
878
  return;
831
879
  }
832
880
  const timer = window.setInterval(() => {
833
- setDisplayedUnitCount((current) => {
834
- if (current >= batchedTargetUnitCount) {
835
- window.clearInterval(timer);
836
- return current;
837
- }
838
- return Math.min(
839
- batchedTargetUnitCount,
840
- getNextDisplayedUnitCount({
841
- currentUnits: current,
842
- targetUnits: batchedTargetUnitCount,
843
- isStreaming: isAssistantStreaming
844
- })
845
- );
846
- });
881
+ dispatch({ type: "advance-reveal", isAssistantStreaming });
847
882
  }, STREAM_REVEAL_TICK_MS);
848
883
  return () => {
849
884
  window.clearInterval(timer);
850
885
  };
851
886
  }, [batchedTargetUnitCount, displayedUnitCount, isAssistantStreaming, message.role]);
852
887
  const settledContent = isFreshBlockActive ? contentBlocks.slice(0, -1).join("\n\n") : displayedContent;
853
- const freshContent = isFreshBlockActive ? contentBlocks.at(-1) ?? "" : "";
888
+ const freshContent = isFreshBlockActive ? contentBlocks[contentBlocks.length - 1] ?? "" : "";
889
+ const displayedBlocks = isAssistantStreaming && contentBlocks.length > 1 ? contentBlocks.map((content, index) => ({
890
+ content,
891
+ tone: isFreshBlockActive && index === contentBlocks.length - 1 ? "fresh" : "settled"
892
+ })) : [
893
+ {
894
+ content: displayedContent,
895
+ tone: "settled"
896
+ }
897
+ ];
854
898
  return {
855
899
  isAssistantStreaming,
856
900
  displayedContent,
857
901
  settledContent,
858
- freshContent
902
+ freshContent,
903
+ displayedBlocks
859
904
  };
860
905
  };
861
906
 
@@ -1803,16 +1848,18 @@ var ChatMessageItemView = ({
1803
1848
  }) => {
1804
1849
  const { labels } = useChatContext();
1805
1850
  const [activeImage, setActiveImage] = (0, import_react8.useState)(void 0);
1806
- const { displayedContent, freshContent, isAssistantStreaming, settledContent } = useChatMessageReveal(message);
1851
+ const { displayedBlocks, displayedContent, freshContent, isAssistantStreaming, settledContent } = useChatMessageReveal(message);
1807
1852
  const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
1808
1853
  const attachments = message.attachments ?? [];
1809
1854
  const blocks = message.blocks ?? [];
1810
1855
  const hasStructuredBlocks = blocks.length > 0;
1856
+ const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
1811
1857
  const hasTextContent = Boolean(settledContent || freshContent || displayedContent);
1858
+ const shouldRenderStructuredBlocks = hasStructuredBlocks && !(isAssistantStreaming && hasMarkdownOnlyBlocks && hasTextContent);
1812
1859
  const isPlanMode = mode === "plan";
1813
1860
  const canSubmitConfirmation = isPlanMode && typeof onConfirmationSubmit === "function";
1814
1861
  const canSubmitQuestionnaire = isPlanMode && typeof onQuestionnaireSubmit === "function";
1815
- const shouldShowStreamingCaret = isAssistantStreaming && (!hasStructuredBlocks || hasTextContent);
1862
+ const shouldShowStreamingCaret = isAssistantStreaming && (!shouldRenderStructuredBlocks || hasTextContent);
1816
1863
  const renderChatMessageBlock = (block, index) => {
1817
1864
  switch (block.type) {
1818
1865
  case "markdown":
@@ -1870,9 +1917,17 @@ var ChatMessageItemView = ({
1870
1917
  }
1871
1918
  };
1872
1919
  const renderTextContent = () => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1873
- settledContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(settledContent) }) : null,
1874
- freshContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-fresh-block", "data-block-tone": "fresh", children: renderMarkdownContent(freshContent) }) : null,
1875
- !settledContent && !freshContent && hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(displayedContent) }) : null
1920
+ displayedBlocks.filter((block) => block.content).map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1921
+ ContentBlock,
1922
+ {
1923
+ "data-testid": block.tone === "fresh" ? "chat-message-fresh-block" : "chat-message-settled-block",
1924
+ "data-block-tone": block.tone,
1925
+ "data-block-index": index,
1926
+ children: renderMarkdownContent(block.content)
1927
+ },
1928
+ `${block.tone}-${index}`
1929
+ )),
1930
+ !displayedBlocks.some((block) => block.content) && !settledContent && !freshContent && hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentBlock, { "data-testid": "chat-message-settled-block", "data-block-tone": "settled", children: renderMarkdownContent(displayedContent) }) : null
1876
1931
  ] });
1877
1932
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1878
1933
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
@@ -1892,8 +1947,8 @@ var ChatMessageItemView = ({
1892
1947
  isStoppedAssistant ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
1893
1948
  ] }),
1894
1949
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Content, { "data-testid": "chat-message-content", children: [
1895
- hasStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1896
- hasStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1950
+ shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ContentStack, { "data-testid": "chat-message-body-stack", children: [
1951
+ shouldRenderStructuredBlocks ? blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1897
1952
  ContentSegment,
1898
1953
  {
1899
1954
  "data-testid": "chat-message-content-segment",
@@ -2136,65 +2191,21 @@ var StreamingCaret = import_styled7.default.span`
2136
2191
  animation: ${caretBlink} 0.9s steps(1) infinite;
2137
2192
  `;
2138
2193
 
2139
- // src/components/chat-thread/components/chat-thread-history-list.tsx
2140
- var import_react10 = require("react");
2194
+ // src/components/chat-thread/components/chat-thread-empty-state.tsx
2141
2195
  var import_styled8 = __toESM(require("@emotion/styled"));
2142
2196
  var import_jsx_runtime9 = require("@emotion/react/jsx-runtime");
2143
- var ChatThreadHistoryList = (0, import_react10.memo)(
2144
- ({
2145
- mode,
2146
- historyMessages,
2147
- latestUserMessageId,
2148
- latestUserMessageRef,
2149
- onConfirmationSubmit,
2150
- onQuestionnaireSubmit,
2151
- renderMessageBlock
2152
- }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HistoryGroup, { "data-testid": "chat-thread-history", children: historyMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2153
- MessageSlot,
2154
- {
2155
- ref: message.id === latestUserMessageId ? latestUserMessageRef : null,
2156
- "data-testid": message.id === latestUserMessageId ? "chat-latest-user-anchor" : void 0,
2157
- style: message.id === latestUserMessageId ? {
2158
- scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px`
2159
- } : void 0,
2160
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2161
- ChatMessageItem,
2162
- {
2163
- mode,
2164
- message,
2165
- onConfirmationSubmit,
2166
- onQuestionnaireSubmit,
2167
- renderMessageBlock
2168
- }
2169
- )
2170
- },
2171
- message.id
2172
- )) })
2173
- );
2174
- ChatThreadHistoryList.displayName = "ChatThreadHistoryList";
2175
- var HistoryGroup = import_styled8.default.div`
2176
- display: contents;
2177
- `;
2178
- var MessageSlot = import_styled8.default.div`
2179
- display: flex;
2180
- align-items: flex-start;
2181
- `;
2182
-
2183
- // src/components/chat-thread/components/chat-thread-empty-state.tsx
2184
- var import_styled9 = __toESM(require("@emotion/styled"));
2185
- var import_jsx_runtime10 = require("@emotion/react/jsx-runtime");
2186
2197
  var ChatThreadEmptyState = () => {
2187
2198
  const { labels } = useChatContext();
2188
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(EmptyShell, { "data-testid": "chat-empty-hero", children: [
2189
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(HeroMark, { children: [
2190
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroOrbit, {}),
2191
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroCore, { children: "AI" })
2199
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(EmptyShell, { "data-testid": "chat-empty-hero", children: [
2200
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(HeroMark, { children: [
2201
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroOrbit, {}),
2202
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroCore, { children: "AI" })
2192
2203
  ] }),
2193
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroTitle, { children: labels.emptyStateTitle }),
2194
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HeroSubtitle, { children: labels.emptyStateSubtitle })
2204
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroTitle, { children: labels.emptyStateTitle }),
2205
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(HeroSubtitle, { children: labels.emptyStateSubtitle })
2195
2206
  ] });
2196
2207
  };
2197
- var EmptyShell = import_styled9.default.div`
2208
+ var EmptyShell = import_styled8.default.div`
2198
2209
  flex: 1;
2199
2210
  min-height: 0;
2200
2211
  display: flex;
@@ -2204,7 +2215,7 @@ var EmptyShell = import_styled9.default.div`
2204
2215
  gap: 16px;
2205
2216
  padding: 48px 24px 24px;
2206
2217
  `;
2207
- var HeroMark = import_styled9.default.div`
2218
+ var HeroMark = import_styled8.default.div`
2208
2219
  position: relative;
2209
2220
  width: 108px;
2210
2221
  height: 108px;
@@ -2212,7 +2223,7 @@ var HeroMark = import_styled9.default.div`
2212
2223
  display: grid;
2213
2224
  place-items: center;
2214
2225
  `;
2215
- var HeroOrbit = import_styled9.default.div`
2226
+ var HeroOrbit = import_styled8.default.div`
2216
2227
  position: absolute;
2217
2228
  inset: 20px;
2218
2229
  border-radius: 50%;
@@ -2235,7 +2246,7 @@ var HeroOrbit = import_styled9.default.div`
2235
2246
  transform: rotate(-22deg);
2236
2247
  }
2237
2248
  `;
2238
- var HeroCore = import_styled9.default.div`
2249
+ var HeroCore = import_styled8.default.div`
2239
2250
  position: relative;
2240
2251
  z-index: 1;
2241
2252
  font-size: 28px;
@@ -2245,13 +2256,13 @@ var HeroCore = import_styled9.default.div`
2245
2256
  color: rgba(242, 244, 255, 0.96);
2246
2257
  text-shadow: 0 0 16px rgba(98, 116, 255, 0.65);
2247
2258
  `;
2248
- var HeroTitle = import_styled9.default.p`
2259
+ var HeroTitle = import_styled8.default.p`
2249
2260
  margin: 0;
2250
2261
  color: rgba(255, 255, 255, 0.88);
2251
2262
  font-size: 16px;
2252
2263
  line-height: 24px;
2253
2264
  `;
2254
- var HeroSubtitle = import_styled9.default.p`
2265
+ var HeroSubtitle = import_styled8.default.p`
2255
2266
  margin: 0;
2256
2267
  color: rgba(255, 255, 255, 0.72);
2257
2268
  font-size: 14px;
@@ -2259,7 +2270,74 @@ var HeroSubtitle = import_styled9.default.p`
2259
2270
  `;
2260
2271
 
2261
2272
  // src/components/chat-thread/index.tsx
2262
- var import_jsx_runtime11 = require("@emotion/react/jsx-runtime");
2273
+ var import_jsx_runtime10 = require("@emotion/react/jsx-runtime");
2274
+ var renderChatMessage = ({
2275
+ message,
2276
+ mode,
2277
+ onConfirmationSubmit,
2278
+ onQuestionnaireSubmit,
2279
+ renderMessageBlock
2280
+ }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2281
+ ChatMessageItem,
2282
+ {
2283
+ mode,
2284
+ message,
2285
+ onConfirmationSubmit,
2286
+ onQuestionnaireSubmit,
2287
+ renderMessageBlock
2288
+ }
2289
+ );
2290
+ var renderErrorState = ({
2291
+ error,
2292
+ onRetry,
2293
+ retryButtonLabel
2294
+ }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ErrorState, { "data-testid": "chat-thread-error-state", children: [
2295
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ErrorText, { children: error }),
2296
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ErrorActions, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RetryButton, { type: "button", "data-testid": "chat-thread-retry", onClick: onRetry, children: retryButtonLabel }) }) : null
2297
+ ] });
2298
+ var groupConversationTurns = (historyMessages, streamingMessage) => {
2299
+ const turns = [];
2300
+ let currentTurn = null;
2301
+ historyMessages.forEach((message) => {
2302
+ if (message.role === "user") {
2303
+ currentTurn = {
2304
+ id: message.id,
2305
+ userMessage: message,
2306
+ responseMessages: []
2307
+ };
2308
+ turns.push(currentTurn);
2309
+ return;
2310
+ }
2311
+ if (!currentTurn) {
2312
+ currentTurn = {
2313
+ id: `assistant-turn-${message.id}`,
2314
+ responseMessages: [message]
2315
+ };
2316
+ turns.push(currentTurn);
2317
+ return;
2318
+ }
2319
+ currentTurn.responseMessages.push(message);
2320
+ });
2321
+ if (!streamingMessage) {
2322
+ return turns;
2323
+ }
2324
+ const lastTurn = turns[turns.length - 1];
2325
+ if (lastTurn) {
2326
+ return [
2327
+ ...turns.slice(0, -1),
2328
+ {
2329
+ ...lastTurn,
2330
+ responseMessages: [...lastTurn.responseMessages, streamingMessage]
2331
+ }
2332
+ ];
2333
+ }
2334
+ return [
2335
+ {
2336
+ id: `assistant-turn-${streamingMessage.id}`,
2337
+ responseMessages: [streamingMessage]
2338
+ }
2339
+ ];
2340
+ };
2263
2341
  var ChatThreadView = ({
2264
2342
  activeSessionMode = DEFAULT_CHAT_AGENT_MODE,
2265
2343
  historyMessages,
@@ -2271,45 +2349,56 @@ var ChatThreadView = ({
2271
2349
  onQuestionnaireSubmit,
2272
2350
  renderMessageBlock
2273
2351
  }) => {
2274
- const containerRef = (0, import_react11.useRef)(null);
2275
- const latestUserMessageId = (0, import_react11.useMemo)(
2276
- () => findLatestUserMessageId(historyMessages),
2277
- [historyMessages]
2352
+ const containerRef = (0, import_react10.useRef)(null);
2353
+ const conversationTurns = (0, import_react10.useMemo)(
2354
+ () => groupConversationTurns(historyMessages, streamingMessage),
2355
+ [historyMessages, streamingMessage]
2278
2356
  );
2279
- const latestUserMessageRef = (0, import_react11.useRef)(null);
2280
- const pendingScrollUserMessageIdRef = (0, import_react11.useRef)(void 0);
2281
- const reservedSpaceFrameRef = (0, import_react11.useRef)(null);
2282
- const [latestUserMessageReservedSpace, setLatestUserMessageReservedSpace] = (0, import_react11.useState)({ messageId: void 0, value: 0 });
2283
- const reservedPaddingBottom = 24 + (latestUserMessageReservedSpace.messageId === latestUserMessageId ? latestUserMessageReservedSpace.value : 0);
2284
- const measureLatestUserMessageReservedSpace = (0, import_react11.useCallback)((messageId) => {
2357
+ const latestTurn = conversationTurns[conversationTurns.length - 1];
2358
+ const previousTurns = conversationTurns.slice(0, -1);
2359
+ const latestUserMessageId = latestTurn?.userMessage?.id;
2360
+ const latestUserMessageRef = (0, import_react10.useRef)(null);
2361
+ const reservedSpaceFrameRef = (0, import_react10.useRef)(null);
2362
+ const [latestTurnMinHeight, setLatestTurnMinHeight] = (0, import_react10.useState)(0);
2363
+ const measureLatestTurnMinHeight = (0, import_react10.useCallback)(() => {
2364
+ const container = containerRef.current;
2365
+ if (!container)
2366
+ return;
2367
+ const computedStyle = window.getComputedStyle(container);
2368
+ const paddingTop = Number.parseFloat(computedStyle.paddingTop || "0") || 0;
2369
+ const paddingBottom = Number.parseFloat(computedStyle.paddingBottom || "0") || 0;
2370
+ const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
2371
+ setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
2372
+ }, []);
2373
+ const scrollLatestUserMessageToTop = (0, import_react10.useCallback)(() => {
2285
2374
  const container = containerRef.current;
2286
2375
  const target = latestUserMessageRef.current;
2287
2376
  if (!container || !target)
2288
2377
  return;
2289
- const reservedHeight = calculateChatThreadScrollSpacerHeight({
2290
- containerClientHeight: container.clientHeight,
2291
- containerScrollHeight: container.scrollHeight,
2292
- targetOffsetTop: target.offsetTop
2293
- });
2294
- setLatestUserMessageReservedSpace((current) => {
2295
- const next = reservedHeight > 0 ? reservedHeight : 0;
2296
- if (current.messageId === messageId && current.value === next)
2297
- return current;
2298
- return { messageId, value: next };
2299
- });
2378
+ const containerRect = container.getBoundingClientRect();
2379
+ const targetRect = target.getBoundingClientRect();
2380
+ const nextScrollTop = Math.max(
2381
+ 0,
2382
+ container.scrollTop + (targetRect.top - containerRect.top) - CHAT_THREAD_SCROLL_TOP_GAP
2383
+ );
2384
+ if (typeof container.scrollTo === "function") {
2385
+ container.scrollTo({
2386
+ top: nextScrollTop,
2387
+ behavior: "auto"
2388
+ });
2389
+ return;
2390
+ }
2391
+ container.scrollTop = nextScrollTop;
2300
2392
  }, []);
2301
- (0, import_react11.useLayoutEffect)(() => {
2393
+ (0, import_react10.useLayoutEffect)(() => {
2302
2394
  if (reservedSpaceFrameRef.current !== null) {
2303
2395
  window.cancelAnimationFrame(reservedSpaceFrameRef.current);
2304
2396
  reservedSpaceFrameRef.current = null;
2305
2397
  }
2306
2398
  if (!latestUserMessageId) {
2307
- pendingScrollUserMessageIdRef.current = void 0;
2308
2399
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
2309
2400
  reservedSpaceFrameRef.current = null;
2310
- setLatestUserMessageReservedSpace(
2311
- (current) => current.messageId === void 0 && current.value === 0 ? current : { messageId: void 0, value: 0 }
2312
- );
2401
+ setLatestTurnMinHeight((current) => current === 0 ? current : 0);
2313
2402
  });
2314
2403
  return () => {
2315
2404
  if (reservedSpaceFrameRef.current !== null) {
@@ -2318,10 +2407,10 @@ var ChatThreadView = ({
2318
2407
  }
2319
2408
  };
2320
2409
  }
2321
- pendingScrollUserMessageIdRef.current = latestUserMessageId;
2322
2410
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
2323
2411
  reservedSpaceFrameRef.current = null;
2324
- measureLatestUserMessageReservedSpace(latestUserMessageId);
2412
+ measureLatestTurnMinHeight();
2413
+ scrollLatestUserMessageToTop();
2325
2414
  });
2326
2415
  return () => {
2327
2416
  if (reservedSpaceFrameRef.current !== null) {
@@ -2329,51 +2418,79 @@ var ChatThreadView = ({
2329
2418
  reservedSpaceFrameRef.current = null;
2330
2419
  }
2331
2420
  };
2332
- }, [latestUserMessageId, measureLatestUserMessageReservedSpace]);
2333
- (0, import_react11.useLayoutEffect)(() => {
2334
- if (!latestUserMessageId || pendingScrollUserMessageIdRef.current !== latestUserMessageId)
2335
- return;
2336
- if (latestUserMessageReservedSpace.messageId !== latestUserMessageId)
2421
+ }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
2422
+ (0, import_react10.useLayoutEffect)(() => {
2423
+ if (!latestUserMessageId)
2337
2424
  return;
2338
- latestUserMessageRef.current?.scrollIntoView({ block: "start", behavior: "smooth" });
2339
- pendingScrollUserMessageIdRef.current = void 0;
2340
- }, [latestUserMessageId, latestUserMessageReservedSpace]);
2341
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2342
- Container,
2343
- {
2344
- ref: containerRef,
2345
- "data-testid": "chat-thread",
2346
- style: { paddingBottom: `${reservedPaddingBottom}px` },
2347
- children: [
2348
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2349
- ChatThreadHistoryList,
2350
- {
2351
- mode: activeSessionMode,
2352
- historyMessages,
2353
- latestUserMessageId,
2354
- latestUserMessageRef,
2355
- onConfirmationSubmit,
2356
- onQuestionnaireSubmit,
2357
- renderMessageBlock
2358
- }
2359
- ),
2360
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StreamingGroup, { "data-testid": "chat-thread-streaming", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageSlot2, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2361
- ChatMessageItem,
2362
- {
2425
+ const handleResize = () => {
2426
+ measureLatestTurnMinHeight();
2427
+ scrollLatestUserMessageToTop();
2428
+ };
2429
+ const container = containerRef.current;
2430
+ let resizeObserver = null;
2431
+ if (container && typeof ResizeObserver !== "undefined") {
2432
+ resizeObserver = new ResizeObserver(() => {
2433
+ handleResize();
2434
+ });
2435
+ resizeObserver.observe(container);
2436
+ }
2437
+ window.addEventListener("resize", handleResize);
2438
+ return () => {
2439
+ resizeObserver?.disconnect();
2440
+ window.removeEventListener("resize", handleResize);
2441
+ };
2442
+ }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
2443
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Container, { ref: containerRef, "data-testid": "chat-thread", children: [
2444
+ previousTurns.map((turn) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
2445
+ turn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2446
+ message: turn.userMessage,
2447
+ mode: activeSessionMode,
2448
+ onConfirmationSubmit,
2449
+ onQuestionnaireSubmit,
2450
+ renderMessageBlock
2451
+ }) }) : null,
2452
+ turn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2453
+ message,
2454
+ mode: activeSessionMode,
2455
+ onConfirmationSubmit,
2456
+ onQuestionnaireSubmit,
2457
+ renderMessageBlock
2458
+ }) }, message.id))
2459
+ ] }, turn.id)),
2460
+ latestTurn ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2461
+ ConversationTurn,
2462
+ {
2463
+ "data-testid": "chat-thread-latest-turn",
2464
+ style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
2465
+ children: [
2466
+ latestTurn.userMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2467
+ MessageSlot,
2468
+ {
2469
+ ref: latestUserMessageRef,
2470
+ "data-testid": "chat-latest-user-anchor",
2471
+ style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
2472
+ children: renderChatMessage({
2473
+ message: latestTurn.userMessage,
2474
+ mode: activeSessionMode,
2475
+ onConfirmationSubmit,
2476
+ onQuestionnaireSubmit,
2477
+ renderMessageBlock
2478
+ })
2479
+ }
2480
+ ) : null,
2481
+ latestTurn.responseMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageSlot, { children: renderChatMessage({
2482
+ message,
2363
2483
  mode: activeSessionMode,
2364
- message: streamingMessage,
2365
2484
  onConfirmationSubmit,
2366
2485
  onQuestionnaireSubmit,
2367
2486
  renderMessageBlock
2368
- }
2369
- ) }) }) : null,
2370
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ErrorState, { "data-testid": "chat-thread-error-state", children: [
2371
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ErrorText, { children: error }),
2372
- onRetry ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ErrorActions, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(RetryButton, { type: "button", "data-testid": "chat-thread-retry", onClick: onRetry, children: retryButtonLabel }) }) : null
2373
- ] }) : null
2374
- ]
2375
- }
2376
- );
2487
+ }) }, message.id)),
2488
+ error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
2489
+ ]
2490
+ }
2491
+ ) : null,
2492
+ !latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
2493
+ ] });
2377
2494
  };
2378
2495
  var EMPTY_MESSAGES = [];
2379
2496
  var ChatThread = () => {
@@ -2390,13 +2507,13 @@ var ChatThread = () => {
2390
2507
  const updateQA = useChatStore((s) => s.updateQuestionnaireAnswers);
2391
2508
  const clearSessionError = useChatStore((s) => s.clearSessionError);
2392
2509
  const { sendRef, retryRef, renderMessageBlock, labels } = useChatContext();
2393
- const handleRetry = (0, import_react11.useCallback)(() => {
2510
+ const handleRetry = (0, import_react10.useCallback)(() => {
2394
2511
  if (!activeSessionId)
2395
2512
  return;
2396
2513
  clearSessionError(activeSessionId);
2397
2514
  void retryRef.current();
2398
2515
  }, [activeSessionId, clearSessionError, retryRef]);
2399
- const handleQuestionnaireSubmit = (0, import_react11.useCallback)(
2516
+ const handleQuestionnaireSubmit = (0, import_react10.useCallback)(
2400
2517
  (submission) => {
2401
2518
  if (activeSessionId && submission.sourceMessageId) {
2402
2519
  updateQA(
@@ -2410,16 +2527,16 @@ var ChatThread = () => {
2410
2527
  },
2411
2528
  [activeSessionId, updateQA, sendRef]
2412
2529
  );
2413
- const handleConfirmationSubmit = (0, import_react11.useCallback)(
2530
+ const handleConfirmationSubmit = (0, import_react10.useCallback)(
2414
2531
  (submission) => {
2415
2532
  void sendRef.current(submission.content);
2416
2533
  },
2417
2534
  [sendRef]
2418
2535
  );
2419
2536
  if (!hasSessions || messages.length === 0 && !streamingMessage) {
2420
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatThreadEmptyState, {});
2537
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatThreadEmptyState, {});
2421
2538
  }
2422
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2539
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2423
2540
  ChatThreadView,
2424
2541
  {
2425
2542
  activeSessionMode,
@@ -2434,7 +2551,7 @@ var ChatThread = () => {
2434
2551
  }
2435
2552
  );
2436
2553
  };
2437
- var Container = import_styled10.default.div`
2554
+ var Container = import_styled9.default.div`
2438
2555
  display: flex;
2439
2556
  flex: 1;
2440
2557
  flex-direction: column;
@@ -2457,27 +2574,29 @@ var Container = import_styled10.default.div`
2457
2574
  background: transparent;
2458
2575
  }
2459
2576
  `;
2460
- var MessageSlot2 = import_styled10.default.div`
2577
+ var MessageSlot = import_styled9.default.div`
2461
2578
  display: flex;
2462
2579
  `;
2463
- var StreamingGroup = import_styled10.default.div`
2464
- display: contents;
2580
+ var ConversationTurn = import_styled9.default.div`
2581
+ display: flex;
2582
+ flex-direction: column;
2583
+ gap: 18px;
2465
2584
  `;
2466
- var ErrorText = import_styled10.default.div`
2585
+ var ErrorText = import_styled9.default.div`
2467
2586
  color: #ff7b72;
2468
2587
  font-size: 14px;
2469
2588
  `;
2470
- var ErrorState = import_styled10.default.div`
2589
+ var ErrorState = import_styled9.default.div`
2471
2590
  display: flex;
2472
2591
  flex-direction: column;
2473
2592
  align-items: flex-start;
2474
2593
  gap: 10px;
2475
2594
  `;
2476
- var ErrorActions = import_styled10.default.div`
2595
+ var ErrorActions = import_styled9.default.div`
2477
2596
  display: flex;
2478
2597
  align-items: center;
2479
2598
  `;
2480
- var RetryButton = import_styled10.default.button`
2599
+ var RetryButton = import_styled9.default.button`
2481
2600
  border: 1px solid rgba(255, 255, 255, 0.14);
2482
2601
  border-radius: 999px;
2483
2602
  background: rgba(255, 255, 255, 0.04);
@@ -2493,8 +2612,8 @@ var RetryButton = import_styled10.default.button`
2493
2612
  `;
2494
2613
 
2495
2614
  // src/components/chat-composer/index.tsx
2496
- var import_react15 = require("react");
2497
- var import_styled15 = __toESM(require("@emotion/styled"));
2615
+ var import_react14 = require("react");
2616
+ var import_styled14 = __toESM(require("@emotion/styled"));
2498
2617
 
2499
2618
  // src/components/chat-composer/lib/chat-composer.ts
2500
2619
  var DRAFT_CHAT_SESSION_ID_PREFIX = "draft-session-";
@@ -2605,10 +2724,10 @@ var resolveSendSession = ({
2605
2724
  };
2606
2725
 
2607
2726
  // src/components/chat-composer/hooks/use-chat-composer.ts
2608
- var import_react13 = require("react");
2727
+ var import_react12 = require("react");
2609
2728
 
2610
2729
  // src/components/chat-composer/hooks/use-composer-attachments.ts
2611
- var import_react12 = require("react");
2730
+ var import_react11 = require("react");
2612
2731
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
2613
2732
  var MAX_COMPOSER_ATTACHMENTS = 10;
2614
2733
  var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
@@ -2622,12 +2741,12 @@ var releaseComposerAttachments = (attachments) => {
2622
2741
  attachments.forEach((attachment) => revokeObjectUrl(attachment.previewUrl));
2623
2742
  };
2624
2743
  var useComposerAttachments = () => {
2625
- const [attachments, setAttachments] = (0, import_react12.useState)([]);
2626
- const attachmentsRef = (0, import_react12.useRef)([]);
2627
- (0, import_react12.useEffect)(() => {
2744
+ const [attachments, setAttachments] = (0, import_react11.useState)([]);
2745
+ const attachmentsRef = (0, import_react11.useRef)([]);
2746
+ (0, import_react11.useEffect)(() => {
2628
2747
  attachmentsRef.current = attachments;
2629
2748
  }, [attachments]);
2630
- (0, import_react12.useEffect)(
2749
+ (0, import_react11.useEffect)(
2631
2750
  () => () => {
2632
2751
  releaseComposerAttachments(attachmentsRef.current);
2633
2752
  },
@@ -2740,10 +2859,10 @@ var useChatComposer = () => {
2740
2859
  const clearSessionError = useChatStore((s) => s.clearSessionError);
2741
2860
  const setPreferredMode = useChatStore((s) => s.setPreferredMode);
2742
2861
  const setSessionMode = useChatStore((s) => s.setSessionMode);
2743
- const [availableModels, setAvailableModels] = (0, import_react13.useState)([]);
2744
- const [isModelsLoading, setIsModelsLoading] = (0, import_react13.useState)(true);
2745
- const [isModelsError, setIsModelsError] = (0, import_react13.useState)(false);
2746
- const fetchModels = (0, import_react13.useCallback)(async () => {
2862
+ const [availableModels, setAvailableModels] = (0, import_react12.useState)([]);
2863
+ const [isModelsLoading, setIsModelsLoading] = (0, import_react12.useState)(true);
2864
+ const [isModelsError, setIsModelsError] = (0, import_react12.useState)(false);
2865
+ const fetchModels = (0, import_react12.useCallback)(async () => {
2747
2866
  setIsModelsLoading(true);
2748
2867
  setIsModelsError(false);
2749
2868
  try {
@@ -2755,31 +2874,31 @@ var useChatComposer = () => {
2755
2874
  setIsModelsLoading(false);
2756
2875
  }
2757
2876
  }, [transport]);
2758
- (0, import_react13.useEffect)(() => {
2877
+ (0, import_react12.useEffect)(() => {
2759
2878
  void fetchModels();
2760
2879
  }, [fetchModels]);
2761
2880
  const hasModels = availableModels.length > 0;
2762
- const [value, setValue] = (0, import_react13.useState)("");
2763
- const [selectedModel, setSelectedModel] = (0, import_react13.useState)("");
2764
- const [selectedMode, setSelectedModeLocal] = (0, import_react13.useState)(DEFAULT_CHAT_AGENT_MODE);
2765
- const [attachmentNotice, setAttachmentNotice] = (0, import_react13.useState)(null);
2881
+ const [value, setValue] = (0, import_react12.useState)("");
2882
+ const [selectedModel, setSelectedModel] = (0, import_react12.useState)("");
2883
+ const [selectedMode, setSelectedModeLocal] = (0, import_react12.useState)(DEFAULT_CHAT_AGENT_MODE);
2884
+ const [attachmentNotice, setAttachmentNotice] = (0, import_react12.useState)(null);
2766
2885
  const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
2767
- const abortControllerRef = (0, import_react13.useRef)(null);
2768
- const stopRequestRef = (0, import_react13.useRef)(null);
2769
- const lastRequestRef = (0, import_react13.useRef)(null);
2770
- (0, import_react13.useEffect)(() => {
2886
+ const abortControllerRef = (0, import_react12.useRef)(null);
2887
+ const stopRequestRef = (0, import_react12.useRef)(null);
2888
+ const lastRequestRef = (0, import_react12.useRef)(null);
2889
+ (0, import_react12.useEffect)(() => {
2771
2890
  setSelectedModel(
2772
2891
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
2773
2892
  );
2774
2893
  }, [availableModels, isModelsLoading]);
2775
- (0, import_react13.useEffect)(() => {
2894
+ (0, import_react12.useEffect)(() => {
2776
2895
  if (activeSession) {
2777
2896
  setSelectedModeLocal(activeSession.mode ?? DEFAULT_CHAT_AGENT_MODE);
2778
2897
  return;
2779
2898
  }
2780
2899
  setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
2781
2900
  }, [activeSession, preferredMode]);
2782
- (0, import_react13.useEffect)(() => {
2901
+ (0, import_react12.useEffect)(() => {
2783
2902
  if (!attachmentNotice)
2784
2903
  return;
2785
2904
  const timeoutId = window.setTimeout(
@@ -2798,7 +2917,7 @@ var useChatComposer = () => {
2798
2917
  stopRequestRef.current.timeoutId = null;
2799
2918
  }
2800
2919
  };
2801
- const clearStopRequest = (0, import_react13.useCallback)((sessionId) => {
2920
+ const clearStopRequest = (0, import_react12.useCallback)((sessionId) => {
2802
2921
  if (!stopRequestRef.current)
2803
2922
  return;
2804
2923
  if (sessionId && stopRequestRef.current.sessionId !== sessionId)
@@ -2806,7 +2925,7 @@ var useChatComposer = () => {
2806
2925
  clearStopTimeout(sessionId);
2807
2926
  stopRequestRef.current = null;
2808
2927
  }, []);
2809
- const finalizeStop = (0, import_react13.useCallback)(
2928
+ const finalizeStop = (0, import_react12.useCallback)(
2810
2929
  (sessionId) => {
2811
2930
  if (stopRequestRef.current?.sessionId === sessionId) {
2812
2931
  if (stopRequestRef.current.finalized)
@@ -2821,7 +2940,7 @@ var useChatComposer = () => {
2821
2940
  },
2822
2941
  [clearStopRequest, finalizeStoppedStreamingMessage]
2823
2942
  );
2824
- const runStream = (0, import_react13.useCallback)(
2943
+ const runStream = (0, import_react12.useCallback)(
2825
2944
  async ({
2826
2945
  localSessionId,
2827
2946
  sessionId,
@@ -2910,7 +3029,7 @@ var useChatComposer = () => {
2910
3029
  setSessionError
2911
3030
  ]
2912
3031
  );
2913
- const send = (0, import_react13.useCallback)(
3032
+ const send = (0, import_react12.useCallback)(
2914
3033
  async (contentOverride) => {
2915
3034
  const content = (contentOverride ?? value).trim();
2916
3035
  const hasText = Boolean(content);
@@ -3058,29 +3177,29 @@ var useChatComposer = () => {
3058
3177
  };
3059
3178
 
3060
3179
  // src/components/chat-composer/components/chat-composer-attachment-list.tsx
3061
- var import_react14 = require("react");
3062
- var import_styled11 = __toESM(require("@emotion/styled"));
3063
- var import_jsx_runtime12 = require("@emotion/react/jsx-runtime");
3180
+ var import_react13 = require("react");
3181
+ var import_styled10 = __toESM(require("@emotion/styled"));
3182
+ var import_jsx_runtime11 = require("@emotion/react/jsx-runtime");
3064
3183
  var ChatComposerAttachmentList = ({
3065
3184
  attachments,
3066
3185
  onRemoveAttachment
3067
3186
  }) => {
3068
- const [activeImage, setActiveImage] = (0, import_react14.useState)(null);
3187
+ const [activeImage, setActiveImage] = (0, import_react13.useState)(null);
3069
3188
  if (!attachments.length) {
3070
3189
  return null;
3071
3190
  }
3072
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3073
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttachmentList, { "data-testid": "chat-composer-attachment-list", children: attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(AttachmentCard, { children: [
3074
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3191
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
3192
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttachmentList, { "data-testid": "chat-composer-attachment-list", children: attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(AttachmentCard, { children: [
3193
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3075
3194
  AttachmentPreviewButton,
3076
3195
  {
3077
3196
  type: "button",
3078
3197
  "aria-label": `${attachment.name} preview`,
3079
3198
  onClick: () => setActiveImage(attachment),
3080
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(AttachmentThumb, { src: attachment.previewUrl, alt: attachment.name })
3199
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AttachmentThumb, { src: attachment.previewUrl, alt: attachment.name })
3081
3200
  }
3082
3201
  ),
3083
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3202
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3084
3203
  AttachmentRemoveButton,
3085
3204
  {
3086
3205
  type: "button",
@@ -3089,11 +3208,11 @@ var ChatComposerAttachmentList = ({
3089
3208
  event.stopPropagation();
3090
3209
  onRemoveAttachment(attachment.id);
3091
3210
  },
3092
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CloseGlyph, { "aria-hidden": "true" })
3211
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CloseGlyph, { "aria-hidden": "true" })
3093
3212
  }
3094
3213
  )
3095
3214
  ] }, attachment.id)) }),
3096
- activeImage ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3215
+ activeImage ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3097
3216
  ImageViewer,
3098
3217
  {
3099
3218
  src: activeImage.previewUrl,
@@ -3103,7 +3222,7 @@ var ChatComposerAttachmentList = ({
3103
3222
  ) : null
3104
3223
  ] });
3105
3224
  };
3106
- var AttachmentList = import_styled11.default.div`
3225
+ var AttachmentList = import_styled10.default.div`
3107
3226
  display: flex;
3108
3227
  flex-wrap: wrap;
3109
3228
  gap: 10px;
@@ -3125,7 +3244,7 @@ var AttachmentList = import_styled11.default.div`
3125
3244
  background: transparent;
3126
3245
  }
3127
3246
  `;
3128
- var AttachmentCard = import_styled11.default.div`
3247
+ var AttachmentCard = import_styled10.default.div`
3129
3248
  position: relative;
3130
3249
  width: 108px;
3131
3250
  height: 72px;
@@ -3134,7 +3253,7 @@ var AttachmentCard = import_styled11.default.div`
3134
3253
  border: 1px solid rgba(255, 255, 255, 0.12);
3135
3254
  background: rgba(255, 255, 255, 0.04);
3136
3255
  `;
3137
- var AttachmentPreviewButton = import_styled11.default.button`
3256
+ var AttachmentPreviewButton = import_styled10.default.button`
3138
3257
  width: 100%;
3139
3258
  height: 100%;
3140
3259
  padding: 0;
@@ -3142,13 +3261,13 @@ var AttachmentPreviewButton = import_styled11.default.button`
3142
3261
  background: transparent;
3143
3262
  cursor: zoom-in;
3144
3263
  `;
3145
- var AttachmentThumb = import_styled11.default.img`
3264
+ var AttachmentThumb = import_styled10.default.img`
3146
3265
  width: 100%;
3147
3266
  height: 100%;
3148
3267
  object-fit: cover;
3149
3268
  display: block;
3150
3269
  `;
3151
- var AttachmentRemoveButton = import_styled11.default.button`
3270
+ var AttachmentRemoveButton = import_styled10.default.button`
3152
3271
  position: absolute;
3153
3272
  top: 6px;
3154
3273
  right: 6px;
@@ -3186,7 +3305,7 @@ var AttachmentRemoveButton = import_styled11.default.button`
3186
3305
  background: rgba(30, 30, 35, 0.98);
3187
3306
  }
3188
3307
  `;
3189
- var CloseGlyph = import_styled11.default.span`
3308
+ var CloseGlyph = import_styled10.default.span`
3190
3309
  position: relative;
3191
3310
  width: 11px;
3192
3311
  height: 11px;
@@ -3215,9 +3334,9 @@ var CloseGlyph = import_styled11.default.span`
3215
3334
  `;
3216
3335
 
3217
3336
  // src/components/chat-composer/components/chat-model-control.tsx
3218
- var import_styled12 = __toESM(require("@emotion/styled"));
3337
+ var import_styled11 = __toESM(require("@emotion/styled"));
3219
3338
  var import_compass_ui = require("@xinghunm/compass-ui");
3220
- var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
3339
+ var import_jsx_runtime12 = require("@emotion/react/jsx-runtime");
3221
3340
  var ChatModelControl = ({
3222
3341
  selectedModel,
3223
3342
  availableModels,
@@ -3228,7 +3347,7 @@ var ChatModelControl = ({
3228
3347
  onReloadModels
3229
3348
  }) => {
3230
3349
  if (isModelsError) {
3231
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3350
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3232
3351
  ModelReloadButton,
3233
3352
  {
3234
3353
  type: "button",
@@ -3236,8 +3355,8 @@ var ChatModelControl = ({
3236
3355
  "aria-label": "Reload",
3237
3356
  onClick: onReloadModels,
3238
3357
  children: [
3239
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Failed to load models" }),
3240
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3358
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Failed to load models" }),
3359
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3241
3360
  ReloadIcon,
3242
3361
  {
3243
3362
  "data-testid": "chat-model-reload-icon",
@@ -3248,8 +3367,8 @@ var ChatModelControl = ({
3248
3367
  fill: "currentColor",
3249
3368
  xmlns: "http://www.w3.org/2000/svg",
3250
3369
  children: [
3251
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M895.469672 511.745197c0-146.498562-82.099856-273.805016-202.788589-338.470805l22.072715-46.630017c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-92.436272 33.040511c-12.609179 4.50664-19.176758 18.382673-14.670118 30.991852l33.040511 92.436272c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l24.581861-51.92972c99.069343 54.335513 166.240185 159.596881 166.240185 280.561907 0 165.56685-125.817544 301.747415-287.057855 318.14692l0 0.022513c-17.730826 0-32.105209 14.374382-32.105209 32.105209 0 17.730826 14.374382 32.105209 32.105209 32.105209 2.098801 0 4.149507-0.207731 6.135744-0.592494C744.270041 874.039593 895.469672 710.564381 895.469672 511.745197z" }),
3252
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M480.616222 129.23948c-0.041956 0-0.082888 0.00307-0.124843 0.00307l0-0.00307c-0.01535 0.001023-0.031722 0.00307-0.047072 0.004093-1.892093 0.010233-3.744277 0.189312-5.545296 0.5137-194.674794 18.529005-346.957083 182.459588-346.957083 381.987924 0 147.431817 83.146699 275.42798 205.097168 339.700819l-24.814152 52.419883c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l92.436272-33.040511c12.609179-4.50664 19.176758-18.382673 14.670118-30.991852l-33.040511-92.436272c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-21.853727 46.167482c-100.326986-53.964052-168.535461-159.920246-168.535461-281.81955 0-166.089759 126.616746-302.591643 288.588721-318.284043l0-0.014326c0.041956 0 0.082888 0.00307 0.124843 0.00307 17.730826 0 32.105209-14.374382 32.105209-32.105209C512.721431 143.613862 498.347049 129.23948 480.616222 129.23948z" })
3370
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M895.469672 511.745197c0-146.498562-82.099856-273.805016-202.788589-338.470805l22.072715-46.630017c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-92.436272 33.040511c-12.609179 4.50664-19.176758 18.382673-14.670118 30.991852l33.040511 92.436272c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l24.581861-51.92972c99.069343 54.335513 166.240185 159.596881 166.240185 280.561907 0 165.56685-125.817544 301.747415-287.057855 318.14692l0 0.022513c-17.730826 0-32.105209 14.374382-32.105209 32.105209 0 17.730826 14.374382 32.105209 32.105209 32.105209 2.098801 0 4.149507-0.207731 6.135744-0.592494C744.270041 874.039593 895.469672 710.564381 895.469672 511.745197z" }),
3371
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: "M480.616222 129.23948c-0.041956 0-0.082888 0.00307-0.124843 0.00307l0-0.00307c-0.01535 0.001023-0.031722 0.00307-0.047072 0.004093-1.892093 0.010233-3.744277 0.189312-5.545296 0.5137-194.674794 18.529005-346.957083 182.459588-346.957083 381.987924 0 147.431817 83.146699 275.42798 205.097168 339.700819l-24.814152 52.419883c4.50664 12.609179 18.382673 19.176758 30.991852 14.670118l92.436272-33.040511c12.609179-4.50664 19.176758-18.382673 14.670118-30.991852l-33.040511-92.436272c-4.50664-12.609179-18.382673-19.176758-30.991852-14.670118l-21.853727 46.167482c-100.326986-53.964052-168.535461-159.920246-168.535461-281.81955 0-166.089759 126.616746-302.591643 288.588721-318.284043l0-0.014326c0.041956 0 0.082888 0.00307 0.124843 0.00307 17.730826 0 32.105209-14.374382 32.105209-32.105209C512.721431 143.613862 498.347049 129.23948 480.616222 129.23948z" })
3253
3372
  ]
3254
3373
  }
3255
3374
  )
@@ -3258,11 +3377,11 @@ var ChatModelControl = ({
3258
3377
  );
3259
3378
  }
3260
3379
  if (isModelsLoading) {
3261
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "Loading models..." });
3380
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: "Loading models..." });
3262
3381
  }
3263
3382
  if (hasModels && selectedModel) {
3264
3383
  if (availableModels.length > 1) {
3265
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3384
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3266
3385
  ModelSelect,
3267
3386
  {
3268
3387
  "data-testid": "chat-model-select",
@@ -3276,11 +3395,11 @@ var ChatModelControl = ({
3276
3395
  }
3277
3396
  );
3278
3397
  }
3279
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: selectedModel });
3398
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: selectedModel });
3280
3399
  }
3281
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "No model available" });
3400
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ModelBadge, { children: "No model available" });
3282
3401
  };
3283
- var ModelBadge = import_styled12.default.span`
3402
+ var ModelBadge = import_styled11.default.span`
3284
3403
  border-radius: 999px;
3285
3404
  border: 1px solid var(--border-hover);
3286
3405
  padding: 5px 12px;
@@ -3289,7 +3408,7 @@ var ModelBadge = import_styled12.default.span`
3289
3408
  color: var(--text-secondary);
3290
3409
  line-height: 12px;
3291
3410
  `;
3292
- var ModelReloadButton = import_styled12.default.button`
3411
+ var ModelReloadButton = import_styled11.default.button`
3293
3412
  display: inline-flex;
3294
3413
  align-items: center;
3295
3414
  gap: 8px;
@@ -3314,10 +3433,10 @@ var ModelReloadButton = import_styled12.default.button`
3314
3433
  color: rgba(255, 255, 255, 0.88);
3315
3434
  }
3316
3435
  `;
3317
- var ReloadIcon = import_styled12.default.svg`
3436
+ var ReloadIcon = import_styled11.default.svg`
3318
3437
  flex-shrink: 0;
3319
3438
  `;
3320
- var ModelSelect = (0, import_styled12.default)(import_compass_ui.Select)`
3439
+ var ModelSelect = (0, import_styled11.default)(import_compass_ui.Select)`
3321
3440
  && {
3322
3441
  width: auto;
3323
3442
  min-width: 0;
@@ -3337,16 +3456,16 @@ var ModelSelect = (0, import_styled12.default)(import_compass_ui.Select)`
3337
3456
  `;
3338
3457
 
3339
3458
  // src/components/chat-composer/components/chat-mode-control.tsx
3340
- var import_styled13 = __toESM(require("@emotion/styled"));
3459
+ var import_styled12 = __toESM(require("@emotion/styled"));
3341
3460
  var import_compass_ui2 = require("@xinghunm/compass-ui");
3342
- var import_jsx_runtime14 = require("@emotion/react/jsx-runtime");
3461
+ var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
3343
3462
  var ChatModeControl = ({
3344
3463
  value,
3345
3464
  disabled = false,
3346
3465
  labels,
3347
3466
  onChange
3348
3467
  }) => {
3349
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3468
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3350
3469
  ModeSelect,
3351
3470
  {
3352
3471
  "data-testid": "chat-mode-select",
@@ -3361,7 +3480,7 @@ var ChatModeControl = ({
3361
3480
  }
3362
3481
  );
3363
3482
  };
3364
- var ModeSelect = (0, import_styled13.default)(import_compass_ui2.Select)`
3483
+ var ModeSelect = (0, import_styled12.default)(import_compass_ui2.Select)`
3365
3484
  && {
3366
3485
  flex: 0 1 auto;
3367
3486
  width: auto;
@@ -3382,10 +3501,10 @@ var ModeSelect = (0, import_styled13.default)(import_compass_ui2.Select)`
3382
3501
  `;
3383
3502
 
3384
3503
  // src/components/chat-composer/components/chat-send-actions.tsx
3385
- var import_styled14 = __toESM(require("@emotion/styled"));
3504
+ var import_styled13 = __toESM(require("@emotion/styled"));
3386
3505
  var import_compass_ui3 = require("@xinghunm/compass-ui");
3387
- var import_jsx_runtime15 = require("@emotion/react/jsx-runtime");
3388
- var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3506
+ var import_jsx_runtime14 = require("@emotion/react/jsx-runtime");
3507
+ var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3389
3508
  "svg",
3390
3509
  {
3391
3510
  "aria-hidden": "true",
@@ -3394,7 +3513,7 @@ var ArrowUpIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3394
3513
  viewBox: "0 0 12 12",
3395
3514
  fill: "none",
3396
3515
  xmlns: "http://www.w3.org/2000/svg",
3397
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3516
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3398
3517
  "path",
3399
3518
  {
3400
3519
  d: "M6 10V2M6 2L2 6M6 2L10 6",
@@ -3412,7 +3531,7 @@ var ChatSendActions = ({
3412
3531
  isStopping,
3413
3532
  onStop,
3414
3533
  onSend
3415
- }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3534
+ }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3416
3535
  StopButton,
3417
3536
  {
3418
3537
  type: "button",
@@ -3422,14 +3541,14 @@ var ChatSendActions = ({
3422
3541
  disabled: isStopping,
3423
3542
  shape: "circle",
3424
3543
  onClick: () => void onStop(),
3425
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StopGlyph, { "aria-hidden": "true" })
3544
+ children: isStopping ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StopSpinner, { "aria-hidden": "true", "data-testid": "chat-composer-stop-spinner" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(StopGlyph, { "aria-hidden": "true", "data-testid": "chat-composer-stop-glyph" })
3426
3545
  }
3427
- ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3546
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3428
3547
  PrimaryButton,
3429
3548
  {
3430
3549
  $canSend: canSend,
3431
3550
  type: "button",
3432
- icon: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ArrowUpIcon, {}),
3551
+ icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArrowUpIcon, {}),
3433
3552
  "aria-label": "Send",
3434
3553
  "data-testid": "chat-composer-send",
3435
3554
  disabled: !canSend,
@@ -3437,7 +3556,7 @@ var ChatSendActions = ({
3437
3556
  onClick: () => void onSend()
3438
3557
  }
3439
3558
  ) });
3440
- var PrimaryButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3559
+ var PrimaryButton = (0, import_styled13.default)(import_compass_ui3.Button)`
3441
3560
  && {
3442
3561
  min-width: 24px;
3443
3562
  width: 24px;
@@ -3463,7 +3582,7 @@ var PrimaryButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3463
3582
  }
3464
3583
  }
3465
3584
  `;
3466
- var StopButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3585
+ var StopButton = (0, import_styled13.default)(import_compass_ui3.Button)`
3467
3586
  && {
3468
3587
  min-width: 24px;
3469
3588
  width: 24px;
@@ -3491,17 +3610,35 @@ var StopButton = (0, import_styled14.default)(import_compass_ui3.Button)`
3491
3610
  }
3492
3611
  }
3493
3612
  `;
3494
- var StopGlyph = import_styled14.default.span`
3613
+ var StopGlyph = import_styled13.default.span`
3495
3614
  width: 8px;
3496
3615
  height: 8px;
3497
3616
  border-radius: 2px;
3498
3617
  background: #1b1b1b;
3499
3618
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
3500
3619
  `;
3620
+ var StopSpinner = import_styled13.default.span`
3621
+ width: 10px;
3622
+ height: 10px;
3623
+ border-radius: 999px;
3624
+ border: 1.5px solid rgba(27, 27, 27, 0.2);
3625
+ border-top-color: #1b1b1b;
3626
+ animation: chat-composer-stop-spin 0.7s linear infinite;
3627
+
3628
+ @keyframes chat-composer-stop-spin {
3629
+ from {
3630
+ transform: rotate(0deg);
3631
+ }
3632
+
3633
+ to {
3634
+ transform: rotate(360deg);
3635
+ }
3636
+ }
3637
+ `;
3501
3638
 
3502
3639
  // src/components/chat-composer/index.tsx
3503
- var import_jsx_runtime16 = require("@emotion/react/jsx-runtime");
3504
- var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3640
+ var import_jsx_runtime15 = require("@emotion/react/jsx-runtime");
3641
+ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3505
3642
  "svg",
3506
3643
  {
3507
3644
  "aria-hidden": "true",
@@ -3510,7 +3647,7 @@ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3510
3647
  viewBox: "0 0 16 16",
3511
3648
  fill: "none",
3512
3649
  xmlns: "http://www.w3.org/2000/svg",
3513
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
3650
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
3514
3651
  }
3515
3652
  );
3516
3653
  var ChatComposerView = ({
@@ -3539,7 +3676,7 @@ var ChatComposerView = ({
3539
3676
  onStop,
3540
3677
  onSend
3541
3678
  }) => {
3542
- const imageInputRef = (0, import_react15.useRef)(null);
3679
+ const imageInputRef = (0, import_react14.useRef)(null);
3543
3680
  const canSend = canSendChatMessage({
3544
3681
  value,
3545
3682
  attachmentCount: attachments.length,
@@ -3570,8 +3707,8 @@ var ChatComposerView = ({
3570
3707
  event.preventDefault();
3571
3708
  onPasteImages(imageFiles);
3572
3709
  };
3573
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Container2, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Surface, { "data-testid": "chat-composer-surface", children: [
3574
- enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3710
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Container2, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Surface, { "data-testid": "chat-composer-surface", children: [
3711
+ enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3575
3712
  "input",
3576
3713
  {
3577
3714
  ref: imageInputRef,
@@ -3583,15 +3720,15 @@ var ChatComposerView = ({
3583
3720
  onChange: handlePickImages
3584
3721
  }
3585
3722
  ) : null,
3586
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3723
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3587
3724
  ChatComposerAttachmentList,
3588
3725
  {
3589
3726
  attachments,
3590
3727
  onRemoveAttachment
3591
3728
  }
3592
3729
  ),
3593
- attachmentNotice === "limit_reached" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
3594
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3730
+ attachmentNotice === "limit_reached" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
3731
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3595
3732
  Input,
3596
3733
  {
3597
3734
  "data-testid": "chat-composer-input",
@@ -3602,18 +3739,18 @@ var ChatComposerView = ({
3602
3739
  placeholder
3603
3740
  }
3604
3741
  ),
3605
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Footer, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Actions2, { "data-testid": "chat-composer-actions", children: [
3606
- enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3742
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Footer, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Actions2, { "data-testid": "chat-composer-actions", children: [
3743
+ enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3607
3744
  AttachButton,
3608
3745
  {
3609
3746
  type: "button",
3610
3747
  "data-testid": "chat-composer-attach-image",
3611
3748
  "aria-label": "Attach image",
3612
3749
  onClick: () => imageInputRef.current?.click(),
3613
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlusIcon, {})
3750
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PlusIcon, {})
3614
3751
  }
3615
3752
  ) : null,
3616
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3753
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3617
3754
  ChatModeControl,
3618
3755
  {
3619
3756
  value: selectedMode,
@@ -3622,7 +3759,7 @@ var ChatComposerView = ({
3622
3759
  onChange: onSelectedModeChange
3623
3760
  }
3624
3761
  ),
3625
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3762
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3626
3763
  ChatModelControl,
3627
3764
  {
3628
3765
  selectedModel,
@@ -3634,7 +3771,7 @@ var ChatComposerView = ({
3634
3771
  onReloadModels
3635
3772
  }
3636
3773
  ),
3637
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3774
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3638
3775
  ChatSendActions,
3639
3776
  {
3640
3777
  canSend,
@@ -3651,7 +3788,7 @@ var ChatComposer = () => {
3651
3788
  const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
3652
3789
  const { state, actions } = useChatComposer();
3653
3790
  const { send, retry } = actions;
3654
- (0, import_react15.useEffect)(() => {
3791
+ (0, import_react14.useEffect)(() => {
3655
3792
  sendRef.current = send;
3656
3793
  retryRef.current = async () => {
3657
3794
  retry();
@@ -3662,7 +3799,7 @@ var ChatComposer = () => {
3662
3799
  plan: labels.modeLabelPlan,
3663
3800
  agent: labels.modeLabelAgent
3664
3801
  };
3665
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3802
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3666
3803
  ChatComposerView,
3667
3804
  {
3668
3805
  value: state.value,
@@ -3692,10 +3829,10 @@ var ChatComposer = () => {
3692
3829
  }
3693
3830
  );
3694
3831
  };
3695
- var Container2 = import_styled15.default.div`
3832
+ var Container2 = import_styled14.default.div`
3696
3833
  padding: 0 16px 16px;
3697
3834
  `;
3698
- var Surface = import_styled15.default.div`
3835
+ var Surface = import_styled14.default.div`
3699
3836
  background: var(--border-color);
3700
3837
  border-radius: 20px;
3701
3838
  border: 1px solid var(--border-hover);
@@ -3704,7 +3841,7 @@ var Surface = import_styled15.default.div`
3704
3841
  0 12px 36px rgba(0, 0, 0, 0.3);
3705
3842
  backdrop-filter: blur(10px);
3706
3843
  `;
3707
- var AttachmentNotice = import_styled15.default.div`
3844
+ var AttachmentNotice = import_styled14.default.div`
3708
3845
  margin: 10px 12px 0;
3709
3846
  padding: 8px 10px;
3710
3847
  border-radius: 10px;
@@ -3714,7 +3851,7 @@ var AttachmentNotice = import_styled15.default.div`
3714
3851
  font-size: 12px;
3715
3852
  line-height: 1.4;
3716
3853
  `;
3717
- var Input = import_styled15.default.textarea`
3854
+ var Input = import_styled14.default.textarea`
3718
3855
  width: 100%;
3719
3856
  min-height: 96px;
3720
3857
  resize: none;
@@ -3736,14 +3873,14 @@ var Input = import_styled15.default.textarea`
3736
3873
  display: none;
3737
3874
  }
3738
3875
  `;
3739
- var Footer = import_styled15.default.div`
3876
+ var Footer = import_styled14.default.div`
3740
3877
  display: flex;
3741
3878
  align-items: flex-end;
3742
3879
  justify-content: stretch;
3743
3880
  gap: 16px;
3744
3881
  padding: 0 14px 14px;
3745
3882
  `;
3746
- var Actions2 = import_styled15.default.div`
3883
+ var Actions2 = import_styled14.default.div`
3747
3884
  display: flex;
3748
3885
  align-items: center;
3749
3886
  flex-wrap: wrap;
@@ -3752,7 +3889,7 @@ var Actions2 = import_styled15.default.div`
3752
3889
  justify-content: flex-end;
3753
3890
  gap: 8px;
3754
3891
  `;
3755
- var AttachButton = import_styled15.default.button`
3892
+ var AttachButton = import_styled14.default.button`
3756
3893
  width: 28px;
3757
3894
  height: 28px;
3758
3895
  display: grid;
@@ -3769,42 +3906,42 @@ var AttachButton = import_styled15.default.button`
3769
3906
  `;
3770
3907
 
3771
3908
  // src/components/chat-conversation-list/index.tsx
3772
- var import_styled17 = __toESM(require("@emotion/styled"));
3909
+ var import_styled16 = __toESM(require("@emotion/styled"));
3773
3910
 
3774
3911
  // src/components/chat-conversation-list/components/chat-session-item.tsx
3775
- var import_react16 = require("react");
3776
- var import_styled16 = __toESM(require("@emotion/styled"));
3777
- var import_jsx_runtime17 = require("@emotion/react/jsx-runtime");
3778
- var ChatSessionItem = (0, import_react16.memo)(
3912
+ var import_react15 = require("react");
3913
+ var import_styled15 = __toESM(require("@emotion/styled"));
3914
+ var import_jsx_runtime16 = require("@emotion/react/jsx-runtime");
3915
+ var ChatSessionItem = (0, import_react15.memo)(
3779
3916
  ({ session, isActive, modeLabel, onClick }) => {
3780
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3917
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3781
3918
  SessionButton,
3782
3919
  {
3783
3920
  type: "button",
3784
3921
  "data-active": isActive,
3785
3922
  onClick: () => onClick(session.sessionId),
3786
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(SessionMeta, { children: [
3787
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SessionTitle, { children: session.title }),
3788
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ModeBadge, { children: modeLabel })
3923
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SessionMeta, { children: [
3924
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SessionTitle, { children: session.title }),
3925
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ModeBadge, { children: modeLabel })
3789
3926
  ] })
3790
3927
  }
3791
3928
  );
3792
3929
  }
3793
3930
  );
3794
3931
  ChatSessionItem.displayName = "ChatSessionItem";
3795
- var SessionMeta = import_styled16.default.div`
3932
+ var SessionMeta = import_styled15.default.div`
3796
3933
  display: flex;
3797
3934
  align-items: center;
3798
3935
  justify-content: space-between;
3799
3936
  gap: 8px;
3800
3937
  `;
3801
- var SessionTitle = import_styled16.default.span`
3938
+ var SessionTitle = import_styled15.default.span`
3802
3939
  min-width: 0;
3803
3940
  overflow: hidden;
3804
3941
  text-overflow: ellipsis;
3805
3942
  white-space: nowrap;
3806
3943
  `;
3807
- var SessionButton = import_styled16.default.button`
3944
+ var SessionButton = import_styled15.default.button`
3808
3945
  border: 1px solid transparent;
3809
3946
  border-radius: 12px;
3810
3947
  padding: 12px;
@@ -3818,7 +3955,7 @@ var SessionButton = import_styled16.default.button`
3818
3955
  background: rgba(255, 255, 255, 0.08);
3819
3956
  }
3820
3957
  `;
3821
- var ModeBadge = import_styled16.default.span`
3958
+ var ModeBadge = import_styled15.default.span`
3822
3959
  flex-shrink: 0;
3823
3960
  border-radius: 999px;
3824
3961
  border: 1px solid rgba(255, 255, 255, 0.1);
@@ -3830,7 +3967,7 @@ var ModeBadge = import_styled16.default.span`
3830
3967
  `;
3831
3968
 
3832
3969
  // src/components/chat-conversation-list/index.tsx
3833
- var import_jsx_runtime18 = require("@emotion/react/jsx-runtime");
3970
+ var import_jsx_runtime17 = require("@emotion/react/jsx-runtime");
3834
3971
  var ChatConversationList = () => {
3835
3972
  const { labels } = useChatContext();
3836
3973
  const sessions = useChatStore((s) => s.sessions);
@@ -3853,12 +3990,12 @@ var ChatConversationList = () => {
3853
3990
  });
3854
3991
  createSession(session);
3855
3992
  };
3856
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Container3, { children: [
3857
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Toolbar, { children: [
3858
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Title4, { children: "Sessions" }),
3859
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: handleCreateSession, children: labels.newChat })
3993
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Container3, { children: [
3994
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Toolbar, { children: [
3995
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Title4, { children: "Sessions" }),
3996
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: handleCreateSession, children: labels.newChat })
3860
3997
  ] }),
3861
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3998
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3862
3999
  ChatSessionItem,
3863
4000
  {
3864
4001
  session,
@@ -3870,7 +4007,7 @@ var ChatConversationList = () => {
3870
4007
  )) })
3871
4008
  ] });
3872
4009
  };
3873
- var Container3 = import_styled17.default.aside`
4010
+ var Container3 = import_styled16.default.aside`
3874
4011
  width: 280px;
3875
4012
  min-width: 280px;
3876
4013
  border-right: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
@@ -3878,18 +4015,18 @@ var Container3 = import_styled17.default.aside`
3878
4015
  flex-direction: column;
3879
4016
  background: rgba(255, 255, 255, 0.02);
3880
4017
  `;
3881
- var Toolbar = import_styled17.default.div`
4018
+ var Toolbar = import_styled16.default.div`
3882
4019
  padding: 20px 16px 12px;
3883
4020
  display: flex;
3884
4021
  flex-direction: column;
3885
4022
  gap: 12px;
3886
4023
  `;
3887
- var Title4 = import_styled17.default.h2`
4024
+ var Title4 = import_styled16.default.h2`
3888
4025
  margin: 0;
3889
4026
  font-size: 14px;
3890
4027
  color: var(--text-secondary);
3891
4028
  `;
3892
- var CreateButton = import_styled17.default.button`
4029
+ var CreateButton = import_styled16.default.button`
3893
4030
  border: none;
3894
4031
  border-radius: 12px;
3895
4032
  padding: 12px 14px;
@@ -3898,7 +4035,7 @@ var CreateButton = import_styled17.default.button`
3898
4035
  text-align: left;
3899
4036
  cursor: pointer;
3900
4037
  `;
3901
- var List2 = import_styled17.default.div`
4038
+ var List2 = import_styled16.default.div`
3902
4039
  padding: 0 12px 16px;
3903
4040
  display: flex;
3904
4041
  flex-direction: column;
@@ -3907,8 +4044,8 @@ var List2 = import_styled17.default.div`
3907
4044
  `;
3908
4045
 
3909
4046
  // src/components/ai-chat/index.tsx
3910
- var import_jsx_runtime19 = require("@emotion/react/jsx-runtime");
3911
- var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4047
+ var import_jsx_runtime18 = require("@emotion/react/jsx-runtime");
4048
+ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3912
4049
  import_compass_ui4.ConfigProvider,
3913
4050
  {
3914
4051
  theme: {
@@ -3943,23 +4080,23 @@ var AiChat = ({ showConversationList = false, ...providerProps }) => /* @__PURE_
3943
4080
  }
3944
4081
  }
3945
4082
  },
3946
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Root, { "data-testid": "ai-chat", children: [
3947
- showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatConversationList, {}) : null,
3948
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Workspace, { children: [
3949
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatThread, {}),
3950
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, {})
4083
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Root, { "data-testid": "ai-chat", children: [
4084
+ showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatConversationList, {}) : null,
4085
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(Workspace, { children: [
4086
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatThread, {}),
4087
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatComposer, {})
3951
4088
  ] })
3952
4089
  ] }) })
3953
4090
  }
3954
4091
  );
3955
- var Root = import_styled18.default.div`
4092
+ var Root = import_styled17.default.div`
3956
4093
  display: flex;
3957
4094
  width: 100%;
3958
4095
  height: 100%;
3959
4096
  min-height: 0;
3960
4097
  overflow: hidden;
3961
4098
  `;
3962
- var Workspace = import_styled18.default.section`
4099
+ var Workspace = import_styled17.default.section`
3963
4100
  flex: 1;
3964
4101
  display: flex;
3965
4102
  flex-direction: column;