@xinghunm/ai-chat 1.5.0 → 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.d.mts CHANGED
@@ -69,6 +69,21 @@ interface ChatHistorySessionListHandlers {
69
69
  onSelectHistorySession?: (session: ChatSession) => Promise<ChatHistorySessionMessagesPage | ChatMessage[] | void> | ChatHistorySessionMessagesPage | ChatMessage[] | void;
70
70
  onLoadMoreHistoryMessages?: (args: ChatHistorySessionMessageLoadArgs) => Promise<ChatHistorySessionMessagesPage | void> | ChatHistorySessionMessagesPage | void;
71
71
  }
72
+ /**
73
+ * Arguments passed to custom history session action renderers.
74
+ */
75
+ interface ChatSessionItemActionRenderProps {
76
+ session: ChatSession;
77
+ isActive: boolean;
78
+ isStreaming: boolean;
79
+ selectSession: () => Promise<void>;
80
+ startNewChat: () => void;
81
+ stopSession: () => Promise<void>;
82
+ }
83
+ /**
84
+ * Optional renderer used to append custom actions to a history session item.
85
+ */
86
+ type ChatSessionItemActionsRenderer = (args: ChatSessionItemActionRenderProps) => ReactNode;
72
87
  /**
73
88
  * Client-side image attachment metadata kept in chat messages.
74
89
  */
@@ -506,6 +521,13 @@ interface NewChatTriggerRenderProps {
506
521
  stopActiveSession: () => Promise<void>;
507
522
  startNewChat: () => Promise<void>;
508
523
  }
524
+ /**
525
+ * State exposed to optional composer mode accessory renderers.
526
+ */
527
+ interface ChatComposerModeAccessoryRenderProps {
528
+ selectedMode: ChatAgentMode;
529
+ disabled: boolean;
530
+ }
509
531
  /**
510
532
  * Default English label values used when no labels are provided.
511
533
  */
@@ -532,6 +554,8 @@ interface AiChatProviderBaseProps {
532
554
  onSelectHistorySession?: ChatHistorySessionListHandlers['onSelectHistorySession'];
533
555
  /** Optional callback used when the active historical session needs older messages. */
534
556
  onLoadMoreHistoryMessages?: ChatHistorySessionListHandlers['onLoadMoreHistoryMessages'];
557
+ /** Optional renderer used to append custom actions to a history session item. */
558
+ renderSessionItemActions?: ChatSessionItemActionsRenderer;
535
559
  /**
536
560
  * Whether to enable image attachment uploads. Defaults to `true`.
537
561
  * Set to `false` to hide the upload button, disable paste-image handling,
@@ -591,12 +615,14 @@ type AiChatProps = Omit<AiChatProviderProps, 'children'> & {
591
615
  * a streaming message, or an error to render.
592
616
  */
593
617
  onConversationStartedChange?: (started: boolean) => void;
618
+ /** Optional renderer used to append extra controls next to the mode selector. */
619
+ renderComposerModeAccessory?: (props: ChatComposerModeAccessoryRenderProps) => ReactNode;
594
620
  };
595
621
  /**
596
622
  * Top-level AI chat component. Wraps AiChatProvider and composes the full
597
623
  * chat UI: optional conversation sidebar + thread + composer.
598
624
  */
599
- declare const AiChat: ({ showConversationList, showNewChatButton, renderNewChatTrigger, showComposerOnlyBeforeFirstMessage, onConversationStartedChange, ...providerProps }: AiChatProps) => _emotion_react_jsx_runtime.JSX.Element;
625
+ declare const AiChat: ({ showConversationList, showNewChatButton, renderNewChatTrigger, showComposerOnlyBeforeFirstMessage, onConversationStartedChange, renderComposerModeAccessory, ...providerProps }: AiChatProps) => _emotion_react_jsx_runtime.JSX.Element;
600
626
 
601
627
  /**
602
628
  * Endpoint overrides for the built-in HTTP transport adapter.
@@ -714,6 +740,7 @@ interface ChatComposerViewProps {
714
740
  modelLoadingLabel: string;
715
741
  modelLoadFailedLabel: string;
716
742
  modelUnavailableLabel: string;
743
+ modeAccessory?: ReactNode;
717
744
  onValueChange: (value: string) => void;
718
745
  onPickImages: (files: FileList | File[]) => void;
719
746
  onPasteImages: (files: File[]) => void;
@@ -729,7 +756,9 @@ interface ChatComposerViewProps {
729
756
  /** Called to send a new user message. */
730
757
  onSend: () => void | Promise<void>;
731
758
  }
732
- declare const ChatComposer: () => _emotion_react_jsx_runtime.JSX.Element;
759
+ declare const ChatComposer: ({ renderModeAccessory, }?: {
760
+ renderModeAccessory?: (props: ChatComposerModeAccessoryRenderProps) => ReactNode;
761
+ }) => _emotion_react_jsx_runtime.JSX.Element;
733
762
 
734
763
  declare const ChatConversationList: () => _emotion_react_jsx_runtime.JSX.Element;
735
764
 
@@ -811,6 +840,10 @@ interface ChatContextValue {
811
840
  retryRef: MutableRefObject<(sessionId: string) => Promise<void>>;
812
841
  /** Stable ref populated by ChatComposer on mount; allows external controls to stop streaming. */
813
842
  stopRef: MutableRefObject<(sessionId: string) => Promise<void>>;
843
+ /** Current stop handler exposed as a plain function for render-time consumers. */
844
+ stopSession?: (sessionId: string) => Promise<void>;
845
+ /** Internal registration hook used by ChatComposer to refresh the stop handler. */
846
+ registerStopSession?: (handler: (sessionId: string) => Promise<void>) => void;
814
847
  /** Optional block renderer used to extend message rendering with custom block types. */
815
848
  renderMessageBlock?: ChatMessageBlockRenderer;
816
849
  /** Optional handler used to intercept questionnaire submissions before the default send flow. */
@@ -831,6 +864,8 @@ interface ChatContextValue {
831
864
  onSelectHistorySession?: ChatHistorySessionListHandlers['onSelectHistorySession'];
832
865
  /** Host callback triggered when a historical session needs older messages. */
833
866
  onLoadMoreHistoryMessages?: ChatHistorySessionListHandlers['onLoadMoreHistoryMessages'];
867
+ /** Optional renderer used to append custom actions to a history session item. */
868
+ renderSessionItemActions?: ChatSessionItemActionsRenderer;
834
869
  }
835
870
 
836
871
  /**
@@ -847,4 +882,4 @@ declare const useChatContext: () => ChatContextValue;
847
882
  */
848
883
  declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
849
884
 
850
- export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatHistorySessionListHandlers, type ChatHistorySessionListState, type ChatHistorySessionMessageLoadArgs, type ChatHistorySessionMessagesPage, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatSessionMessageLoadStatus, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type NewChatTriggerRenderProps, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
885
+ export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerModeAccessoryRenderProps, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatHistorySessionListHandlers, type ChatHistorySessionListState, type ChatHistorySessionMessageLoadArgs, type ChatHistorySessionMessagesPage, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatSessionItemActionRenderProps, type ChatSessionItemActionsRenderer, type ChatSessionMessageLoadStatus, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type NewChatTriggerRenderProps, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
package/dist/index.d.ts CHANGED
@@ -69,6 +69,21 @@ interface ChatHistorySessionListHandlers {
69
69
  onSelectHistorySession?: (session: ChatSession) => Promise<ChatHistorySessionMessagesPage | ChatMessage[] | void> | ChatHistorySessionMessagesPage | ChatMessage[] | void;
70
70
  onLoadMoreHistoryMessages?: (args: ChatHistorySessionMessageLoadArgs) => Promise<ChatHistorySessionMessagesPage | void> | ChatHistorySessionMessagesPage | void;
71
71
  }
72
+ /**
73
+ * Arguments passed to custom history session action renderers.
74
+ */
75
+ interface ChatSessionItemActionRenderProps {
76
+ session: ChatSession;
77
+ isActive: boolean;
78
+ isStreaming: boolean;
79
+ selectSession: () => Promise<void>;
80
+ startNewChat: () => void;
81
+ stopSession: () => Promise<void>;
82
+ }
83
+ /**
84
+ * Optional renderer used to append custom actions to a history session item.
85
+ */
86
+ type ChatSessionItemActionsRenderer = (args: ChatSessionItemActionRenderProps) => ReactNode;
72
87
  /**
73
88
  * Client-side image attachment metadata kept in chat messages.
74
89
  */
@@ -506,6 +521,13 @@ interface NewChatTriggerRenderProps {
506
521
  stopActiveSession: () => Promise<void>;
507
522
  startNewChat: () => Promise<void>;
508
523
  }
524
+ /**
525
+ * State exposed to optional composer mode accessory renderers.
526
+ */
527
+ interface ChatComposerModeAccessoryRenderProps {
528
+ selectedMode: ChatAgentMode;
529
+ disabled: boolean;
530
+ }
509
531
  /**
510
532
  * Default English label values used when no labels are provided.
511
533
  */
@@ -532,6 +554,8 @@ interface AiChatProviderBaseProps {
532
554
  onSelectHistorySession?: ChatHistorySessionListHandlers['onSelectHistorySession'];
533
555
  /** Optional callback used when the active historical session needs older messages. */
534
556
  onLoadMoreHistoryMessages?: ChatHistorySessionListHandlers['onLoadMoreHistoryMessages'];
557
+ /** Optional renderer used to append custom actions to a history session item. */
558
+ renderSessionItemActions?: ChatSessionItemActionsRenderer;
535
559
  /**
536
560
  * Whether to enable image attachment uploads. Defaults to `true`.
537
561
  * Set to `false` to hide the upload button, disable paste-image handling,
@@ -591,12 +615,14 @@ type AiChatProps = Omit<AiChatProviderProps, 'children'> & {
591
615
  * a streaming message, or an error to render.
592
616
  */
593
617
  onConversationStartedChange?: (started: boolean) => void;
618
+ /** Optional renderer used to append extra controls next to the mode selector. */
619
+ renderComposerModeAccessory?: (props: ChatComposerModeAccessoryRenderProps) => ReactNode;
594
620
  };
595
621
  /**
596
622
  * Top-level AI chat component. Wraps AiChatProvider and composes the full
597
623
  * chat UI: optional conversation sidebar + thread + composer.
598
624
  */
599
- declare const AiChat: ({ showConversationList, showNewChatButton, renderNewChatTrigger, showComposerOnlyBeforeFirstMessage, onConversationStartedChange, ...providerProps }: AiChatProps) => _emotion_react_jsx_runtime.JSX.Element;
625
+ declare const AiChat: ({ showConversationList, showNewChatButton, renderNewChatTrigger, showComposerOnlyBeforeFirstMessage, onConversationStartedChange, renderComposerModeAccessory, ...providerProps }: AiChatProps) => _emotion_react_jsx_runtime.JSX.Element;
600
626
 
601
627
  /**
602
628
  * Endpoint overrides for the built-in HTTP transport adapter.
@@ -714,6 +740,7 @@ interface ChatComposerViewProps {
714
740
  modelLoadingLabel: string;
715
741
  modelLoadFailedLabel: string;
716
742
  modelUnavailableLabel: string;
743
+ modeAccessory?: ReactNode;
717
744
  onValueChange: (value: string) => void;
718
745
  onPickImages: (files: FileList | File[]) => void;
719
746
  onPasteImages: (files: File[]) => void;
@@ -729,7 +756,9 @@ interface ChatComposerViewProps {
729
756
  /** Called to send a new user message. */
730
757
  onSend: () => void | Promise<void>;
731
758
  }
732
- declare const ChatComposer: () => _emotion_react_jsx_runtime.JSX.Element;
759
+ declare const ChatComposer: ({ renderModeAccessory, }?: {
760
+ renderModeAccessory?: (props: ChatComposerModeAccessoryRenderProps) => ReactNode;
761
+ }) => _emotion_react_jsx_runtime.JSX.Element;
733
762
 
734
763
  declare const ChatConversationList: () => _emotion_react_jsx_runtime.JSX.Element;
735
764
 
@@ -811,6 +840,10 @@ interface ChatContextValue {
811
840
  retryRef: MutableRefObject<(sessionId: string) => Promise<void>>;
812
841
  /** Stable ref populated by ChatComposer on mount; allows external controls to stop streaming. */
813
842
  stopRef: MutableRefObject<(sessionId: string) => Promise<void>>;
843
+ /** Current stop handler exposed as a plain function for render-time consumers. */
844
+ stopSession?: (sessionId: string) => Promise<void>;
845
+ /** Internal registration hook used by ChatComposer to refresh the stop handler. */
846
+ registerStopSession?: (handler: (sessionId: string) => Promise<void>) => void;
814
847
  /** Optional block renderer used to extend message rendering with custom block types. */
815
848
  renderMessageBlock?: ChatMessageBlockRenderer;
816
849
  /** Optional handler used to intercept questionnaire submissions before the default send flow. */
@@ -831,6 +864,8 @@ interface ChatContextValue {
831
864
  onSelectHistorySession?: ChatHistorySessionListHandlers['onSelectHistorySession'];
832
865
  /** Host callback triggered when a historical session needs older messages. */
833
866
  onLoadMoreHistoryMessages?: ChatHistorySessionListHandlers['onLoadMoreHistoryMessages'];
867
+ /** Optional renderer used to append custom actions to a history session item. */
868
+ renderSessionItemActions?: ChatSessionItemActionsRenderer;
834
869
  }
835
870
 
836
871
  /**
@@ -847,4 +882,4 @@ declare const useChatContext: () => ChatContextValue;
847
882
  */
848
883
  declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
849
884
 
850
- export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatHistorySessionListHandlers, type ChatHistorySessionListState, type ChatHistorySessionMessageLoadArgs, type ChatHistorySessionMessagesPage, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatSessionMessageLoadStatus, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type NewChatTriggerRenderProps, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
885
+ export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerModeAccessoryRenderProps, type ChatComposerViewProps, type ChatConfirmationSubmitHandler, ChatConversationList, type ChatHistorySessionListHandlers, type ChatHistorySessionListState, type ChatHistorySessionMessageLoadArgs, type ChatHistorySessionMessagesPage, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatParameterSummaryItem, type ChatQuestionnaireSubmitHandler, type ChatRole, type ChatSession, type ChatSessionItemActionRenderProps, type ChatSessionItemActionsRenderer, type ChatSessionMessageLoadStatus, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, type ChatSubmissionContext, ChatThread, type ChatToolExecutionPolicy, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type DefaultChatTransportModelsResolver, type DefaultChatTransportRequestBodyBuilder, type DefaultChatTransportRequestBodyBuilderArgs, type ExecutionConfirmationSubmission, type ExecutionProposal, type NewChatTriggerRenderProps, type PlanQuestionOption, type PlanQuestionSubmissionDetail, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
package/dist/index.js CHANGED
@@ -1125,6 +1125,7 @@ var AiChatProvider = (props) => {
1125
1125
  onLoadMoreSessions,
1126
1126
  onSelectHistorySession,
1127
1127
  onLoadMoreHistoryMessages,
1128
+ renderSessionItemActions,
1128
1129
  enableImageAttachments = true,
1129
1130
  children
1130
1131
  } = props;
@@ -1137,6 +1138,17 @@ var AiChatProvider = (props) => {
1137
1138
  });
1138
1139
  const stopRef = (0, import_react2.useRef)(async (_sessionId) => {
1139
1140
  });
1141
+ const [stopSession, setStopSession] = (0, import_react2.useState)(
1142
+ () => async (_sessionId) => {
1143
+ }
1144
+ );
1145
+ const registerStopSession = (0, import_react2.useCallback)(
1146
+ (handler) => {
1147
+ stopRef.current = handler;
1148
+ setStopSession(() => handler);
1149
+ },
1150
+ []
1151
+ );
1140
1152
  const defaultApiBaseUrl = "apiBaseUrl" in props ? props.apiBaseUrl : void 0;
1141
1153
  const defaultAuthToken = "authToken" in props ? props.authToken : void 0;
1142
1154
  const defaultTransformStreamPacket = "transformStreamPacket" in props ? props.transformStreamPacket : void 0;
@@ -1196,6 +1208,8 @@ var AiChatProvider = (props) => {
1196
1208
  sendRef,
1197
1209
  retryRef,
1198
1210
  stopRef,
1211
+ stopSession,
1212
+ registerStopSession,
1199
1213
  renderMessageBlock,
1200
1214
  handleQuestionnaireSubmit,
1201
1215
  handleConfirmationSubmit,
@@ -1205,7 +1219,8 @@ var AiChatProvider = (props) => {
1205
1219
  historySessionList,
1206
1220
  onLoadMoreSessions,
1207
1221
  onSelectHistorySession,
1208
- onLoadMoreHistoryMessages
1222
+ onLoadMoreHistoryMessages,
1223
+ renderSessionItemActions
1209
1224
  }),
1210
1225
  [
1211
1226
  axiosInstance,
@@ -1221,10 +1236,13 @@ var AiChatProvider = (props) => {
1221
1236
  onLoadMoreSessions,
1222
1237
  onLoadMoreHistoryMessages,
1223
1238
  onSelectHistorySession,
1239
+ renderSessionItemActions,
1224
1240
  renderMessageBlock,
1225
1241
  sendRef,
1226
1242
  retryRef,
1227
1243
  stopRef,
1244
+ stopSession,
1245
+ registerStopSession,
1228
1246
  store,
1229
1247
  transport
1230
1248
  ]
@@ -8677,6 +8695,7 @@ var ChatComposerView = ({
8677
8695
  modelLoadingLabel,
8678
8696
  modelLoadFailedLabel,
8679
8697
  modelUnavailableLabel,
8698
+ modeAccessory,
8680
8699
  onValueChange,
8681
8700
  onPickImages,
8682
8701
  onPasteImages,
@@ -8930,7 +8949,8 @@ var ChatComposerView = ({
8930
8949
  labels: modeLabels,
8931
8950
  onChange: onSelectedModeChange
8932
8951
  }
8933
- )
8952
+ ),
8953
+ modeAccessory
8934
8954
  ] }),
8935
8955
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8936
8956
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -8988,8 +9008,17 @@ var ChatComposerView = ({
8988
9008
  ) }) : null
8989
9009
  ] });
8990
9010
  };
8991
- var ChatComposer = () => {
8992
- const { labels, sendRef, retryRef, stopRef, enableImageAttachments } = useChatContext();
9011
+ var ChatComposer = ({
9012
+ renderModeAccessory
9013
+ } = {}) => {
9014
+ const {
9015
+ labels,
9016
+ sendRef,
9017
+ retryRef,
9018
+ registerStopSession = () => {
9019
+ },
9020
+ enableImageAttachments
9021
+ } = useChatContext();
8993
9022
  const { state, actions } = useChatComposer();
8994
9023
  const { send, retry } = actions;
8995
9024
  (0, import_react18.useEffect)(() => {
@@ -8997,13 +9026,17 @@ var ChatComposer = () => {
8997
9026
  retryRef.current = async (sessionId) => {
8998
9027
  retry(sessionId);
8999
9028
  };
9000
- stopRef.current = actions.stopSession;
9001
- }, [actions.stopSession, retry, retryRef, send, sendRef, stopRef]);
9029
+ registerStopSession(actions.stopSession);
9030
+ }, [actions.stopSession, registerStopSession, retry, retryRef, send, sendRef]);
9002
9031
  const modeLabels = {
9003
9032
  ask: labels.modeLabelAsk,
9004
9033
  plan: labels.modeLabelPlan,
9005
9034
  agent: labels.modeLabelAgent
9006
9035
  };
9036
+ const modeAccessory = renderModeAccessory?.({
9037
+ selectedMode: state.selectedMode,
9038
+ disabled: state.isStreaming
9039
+ });
9007
9040
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
9008
9041
  ChatComposerView,
9009
9042
  {
@@ -9033,6 +9066,7 @@ var ChatComposer = () => {
9033
9066
  modelLoadingLabel: labels.modelLoading,
9034
9067
  modelLoadFailedLabel: labels.modelLoadFailed,
9035
9068
  modelUnavailableLabel: labels.modelUnavailable,
9069
+ modeAccessory,
9036
9070
  onValueChange: actions.setValue,
9037
9071
  onPickImages: actions.pickImages,
9038
9072
  onPasteImages: actions.pasteImages,
@@ -9328,19 +9362,30 @@ var import_styled16 = __toESM(require("@emotion/styled"));
9328
9362
  var import_react20 = require("react");
9329
9363
  var import_styled15 = __toESM(require("@emotion/styled"));
9330
9364
  var import_jsx_runtime17 = require("@emotion/react/jsx-runtime");
9331
- var ChatSessionItem = (0, import_react20.memo)(({ session, isActive, onClick }) => {
9332
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
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__ */ (0, import_jsx_runtime17.jsx)(SessionTitle, { children: session.title })
9340
- }
9341
- );
9342
- });
9365
+ var ChatSessionItem = (0, import_react20.memo)(
9366
+ ({ session, isActive, onClick, actions }) => {
9367
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(SessionRow, { "data-active": isActive, children: [
9368
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9369
+ SessionButton,
9370
+ {
9371
+ type: "button",
9372
+ "data-active": isActive,
9373
+ "data-testid": `chat-session-item-${session.sessionId}`,
9374
+ onClick: () => onClick(session.sessionId),
9375
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SessionTitle, { children: session.title })
9376
+ }
9377
+ ),
9378
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SessionActions, { "data-testid": `chat-session-item-actions-${session.sessionId}`, children: actions }) : null
9379
+ ] });
9380
+ }
9381
+ );
9343
9382
  ChatSessionItem.displayName = "ChatSessionItem";
9383
+ var SessionRow = import_styled15.default.div`
9384
+ display: flex;
9385
+ align-items: center;
9386
+ gap: 8px;
9387
+ min-width: 0;
9388
+ `;
9344
9389
  var SessionTitle = import_styled15.default.span`
9345
9390
  display: block;
9346
9391
  min-width: 0;
@@ -9349,6 +9394,8 @@ var SessionTitle = import_styled15.default.span`
9349
9394
  white-space: nowrap;
9350
9395
  `;
9351
9396
  var SessionButton = import_styled15.default.button`
9397
+ flex: 1;
9398
+ min-width: 0;
9352
9399
  border: 1px solid transparent;
9353
9400
  border-radius: 12px;
9354
9401
  padding: 12px;
@@ -9381,6 +9428,12 @@ var SessionButton = import_styled15.default.button`
9381
9428
  background: rgba(255, 255, 255, 0.08);
9382
9429
  }
9383
9430
  `;
9431
+ var SessionActions = import_styled15.default.div`
9432
+ display: flex;
9433
+ align-items: center;
9434
+ gap: 6px;
9435
+ flex: 0 0 auto;
9436
+ `;
9384
9437
 
9385
9438
  // src/components/chat-conversation-list/lib/history-session-selection.ts
9386
9439
  var shouldLoadHistorySessionMessages = ({
@@ -9410,12 +9463,37 @@ var shouldLoadMoreSessions = ({
9410
9463
  threshold = SCROLL_LOAD_MORE_THRESHOLD_PX
9411
9464
  }) => scrollHeight - scrollTop - clientHeight <= threshold;
9412
9465
  var isHistorySessionMessagesPage = (value) => typeof value === "object" && value !== null && Array.isArray(value.messages);
9466
+ var matchesHydratedHistorySessions = (renderedHistorySessionIds, historySessionIds) => {
9467
+ if (renderedHistorySessionIds.length === historySessionIds.length) {
9468
+ return renderedHistorySessionIds.every(
9469
+ (sessionId, index3) => sessionId === historySessionIds[index3]
9470
+ );
9471
+ }
9472
+ if (renderedHistorySessionIds.length !== historySessionIds.length + 1) {
9473
+ return false;
9474
+ }
9475
+ const matchesWithoutLeadingExtra = renderedHistorySessionIds.slice(1).every((sessionId, index3) => sessionId === historySessionIds[index3]);
9476
+ if (matchesWithoutLeadingExtra) {
9477
+ return true;
9478
+ }
9479
+ return renderedHistorySessionIds.slice(0, -1).every((sessionId, index3) => sessionId === historySessionIds[index3]);
9480
+ };
9413
9481
  var ChatConversationList = () => {
9414
- const { labels, historySessionList, onLoadMoreSessions, onSelectHistorySession, store } = useChatContext();
9482
+ const {
9483
+ labels,
9484
+ historySessionList,
9485
+ onLoadMoreSessions,
9486
+ onSelectHistorySession,
9487
+ renderSessionItemActions,
9488
+ stopSession = async (_sessionId) => {
9489
+ },
9490
+ store
9491
+ } = useChatContext();
9415
9492
  const localSessions = useChatStore((s) => s.sessions);
9416
9493
  const activeSessionId = useChatStore((s) => s.activeSessionId);
9417
9494
  const startNewChat = useChatStore((s) => s.startNewChat);
9418
9495
  const setActiveSession = useChatStore((s) => s.setActiveSession);
9496
+ const isStreamingBySession = useChatStore((s) => s.isStreamingBySession);
9419
9497
  const hydrateHistorySessions = useChatStore((s) => s.hydrateHistorySessions);
9420
9498
  const hydrateHistorySessionMessages = useChatStore((s) => s.hydrateHistorySessionMessages);
9421
9499
  const hydrateHistorySessionMessagesPage = useChatStore((s) => s.hydrateHistorySessionMessagesPage);
@@ -9424,6 +9502,7 @@ var ChatConversationList = () => {
9424
9502
  );
9425
9503
  const isLoadingMoreRef = (0, import_react21.useRef)(false);
9426
9504
  const hasSeenLoadingMoreRef = (0, import_react21.useRef)(false);
9505
+ const listRef = (0, import_react21.useRef)(null);
9427
9506
  (0, import_react21.useEffect)(() => {
9428
9507
  if (!historySessionList)
9429
9508
  return;
@@ -9444,23 +9523,50 @@ var ChatConversationList = () => {
9444
9523
  hasSeenLoadingMoreRef.current = false;
9445
9524
  }, [historySessionList?.sessions.length, historySessionList?.hasMore]);
9446
9525
  const sessions = localSessions;
9447
- const handleSessionListScroll = (event) => {
9448
- if (!historySessionList?.hasMore || historySessionList.isLoading || !onLoadMoreSessions || isLoadingMoreRef.current) {
9526
+ const hasMoreSessions = historySessionList?.hasMore ?? false;
9527
+ const isHistorySessionListLoading = historySessionList?.isLoading ?? false;
9528
+ const renderedHistorySessionIds = sessions.filter((session) => !isDraftChatSessionId(session.sessionId)).map((session) => session.sessionId);
9529
+ const isHistorySessionListHydrated = !historySessionList || matchesHydratedHistorySessions(
9530
+ renderedHistorySessionIds,
9531
+ historySessionList.sessions.map((session) => session.sessionId)
9532
+ );
9533
+ const requestLoadMoreSessions = (0, import_react21.useCallback)(() => {
9534
+ if (!hasMoreSessions || isHistorySessionListLoading || !onLoadMoreSessions || isLoadingMoreRef.current) {
9449
9535
  return;
9450
9536
  }
9451
- const target = event.currentTarget;
9452
- if (shouldLoadMoreSessions({
9453
- scrollTop: target.scrollTop,
9454
- clientHeight: target.clientHeight,
9455
- scrollHeight: target.scrollHeight
9456
- })) {
9457
- isLoadingMoreRef.current = true;
9458
- void Promise.resolve(onLoadMoreSessions()).catch(() => {
9459
- isLoadingMoreRef.current = false;
9460
- hasSeenLoadingMoreRef.current = false;
9461
- });
9537
+ isLoadingMoreRef.current = true;
9538
+ void Promise.resolve(onLoadMoreSessions()).catch(() => {
9539
+ isLoadingMoreRef.current = false;
9540
+ hasSeenLoadingMoreRef.current = false;
9541
+ });
9542
+ }, [hasMoreSessions, isHistorySessionListLoading, onLoadMoreSessions]);
9543
+ const maybeLoadMoreSessions = (0, import_react21.useCallback)(
9544
+ (target) => {
9545
+ if (target.clientHeight <= 0 || target.scrollHeight <= 0) {
9546
+ return;
9547
+ }
9548
+ if (shouldLoadMoreSessions({
9549
+ scrollTop: target.scrollTop,
9550
+ clientHeight: target.clientHeight,
9551
+ scrollHeight: target.scrollHeight
9552
+ })) {
9553
+ requestLoadMoreSessions();
9554
+ }
9555
+ },
9556
+ [requestLoadMoreSessions]
9557
+ );
9558
+ (0, import_react21.useEffect)(() => {
9559
+ if (!listRef.current || !isHistorySessionListHydrated) {
9560
+ return;
9462
9561
  }
9463
- };
9562
+ maybeLoadMoreSessions(listRef.current);
9563
+ }, [isHistorySessionListHydrated, hasMoreSessions, localSessions.length, maybeLoadMoreSessions]);
9564
+ const handleSessionListScroll = (0, import_react21.useCallback)(
9565
+ (event) => {
9566
+ maybeLoadMoreSessions(event.currentTarget);
9567
+ },
9568
+ [maybeLoadMoreSessions]
9569
+ );
9464
9570
  const handleSelectSession = async (sessionId) => {
9465
9571
  setActiveSession(sessionId);
9466
9572
  const session = sessions.find((item) => item.sessionId === sessionId);
@@ -9502,13 +9608,21 @@ var ChatConversationList = () => {
9502
9608
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Title3, { children: labels.sessionsTitle }),
9503
9609
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: startNewChat, children: labels.newChat })
9504
9610
  ] }),
9505
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(List2, { "data-testid": "chat-session-list", onScroll: handleSessionListScroll, children: [
9611
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(List2, { ref: listRef, "data-testid": "chat-session-list", onScroll: handleSessionListScroll, children: [
9506
9612
  sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
9507
9613
  ChatSessionItem,
9508
9614
  {
9509
9615
  session,
9510
9616
  isActive: activeSessionId === session.sessionId,
9511
- onClick: (sessionId) => void handleSelectSession(sessionId)
9617
+ onClick: (sessionId) => void handleSelectSession(sessionId),
9618
+ actions: isDraftChatSessionId(session.sessionId) ? void 0 : renderSessionItemActions?.({
9619
+ session,
9620
+ isActive: activeSessionId === session.sessionId,
9621
+ isStreaming: isStreamingBySession[session.sessionId] ?? false,
9622
+ selectSession: () => handleSelectSession(session.sessionId),
9623
+ startNewChat,
9624
+ stopSession: () => stopSession(session.sessionId)
9625
+ })
9512
9626
  },
9513
9627
  session.sessionId
9514
9628
  )),
@@ -9521,6 +9635,7 @@ var ChatConversationList = () => {
9521
9635
  var Container3 = import_styled16.default.aside`
9522
9636
  width: 280px;
9523
9637
  min-width: 280px;
9638
+ min-height: 0;
9524
9639
  border-right: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
9525
9640
  display: flex;
9526
9641
  flex-direction: column;
@@ -9566,6 +9681,8 @@ var CreateButton = import_styled16.default.button`
9566
9681
  }
9567
9682
  `;
9568
9683
  var List2 = import_styled16.default.div`
9684
+ flex: 1 1 0;
9685
+ min-height: 0;
9569
9686
  padding: 0 12px 16px;
9570
9687
  display: flex;
9571
9688
  flex-direction: column;
@@ -9627,7 +9744,8 @@ var AiChatWorkspaceContent = ({
9627
9744
  showNewChatButton,
9628
9745
  renderNewChatTrigger,
9629
9746
  showComposerOnlyBeforeFirstMessage = false,
9630
- onConversationStartedChange
9747
+ onConversationStartedChange,
9748
+ renderComposerModeAccessory
9631
9749
  }) => {
9632
9750
  const isConversationStarted = useChatStore(
9633
9751
  (state) => hasStartedConversation({
@@ -9646,12 +9764,13 @@ var AiChatWorkspaceContent = ({
9646
9764
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Workspace, { children: [
9647
9765
  showNewChatButton && !showConversationList && !shouldShowComposerOnly ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickActions, { renderNewChatTrigger }) : null,
9648
9766
  shouldShowComposerOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatThread, {}),
9649
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, {})
9767
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, { renderModeAccessory: renderComposerModeAccessory })
9650
9768
  ] })
9651
9769
  ] });
9652
9770
  };
9653
9771
  var QuickActions = ({ renderNewChatTrigger }) => {
9654
- const { labels, stopRef, store } = useChatContext();
9772
+ const { labels, stopSession = async (_sessionId) => {
9773
+ }, store } = useChatContext();
9655
9774
  const startNewChat = useChatStore((state) => state.startNewChat);
9656
9775
  const activeSessionId = useChatStore((state) => state.activeSessionId);
9657
9776
  const isActiveSessionStreaming = useChatStore(
@@ -9670,14 +9789,14 @@ var QuickActions = ({ renderNewChatTrigger }) => {
9670
9789
  if (!currentSessionId || !isCurrentSessionStreaming) {
9671
9790
  return;
9672
9791
  }
9673
- await stopRef.current(currentSessionId);
9792
+ await stopSession(currentSessionId);
9674
9793
  };
9675
9794
  const handleStartNewChat = async () => {
9676
9795
  const currentState = store.getState();
9677
9796
  const currentSessionId = currentState.activeSessionId;
9678
9797
  const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
9679
9798
  if (currentSessionId && isCurrentSessionStreaming) {
9680
- void stopRef.current(currentSessionId);
9799
+ void stopSession(currentSessionId);
9681
9800
  }
9682
9801
  createNewSession();
9683
9802
  };
@@ -9721,6 +9840,7 @@ var AiChat = ({
9721
9840
  renderNewChatTrigger,
9722
9841
  showComposerOnlyBeforeFirstMessage = false,
9723
9842
  onConversationStartedChange,
9843
+ renderComposerModeAccessory,
9724
9844
  ...providerProps
9725
9845
  }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
9726
9846
  import_compass_ui4.ConfigProvider,
@@ -9764,7 +9884,8 @@ var AiChat = ({
9764
9884
  showNewChatButton,
9765
9885
  renderNewChatTrigger,
9766
9886
  showComposerOnlyBeforeFirstMessage,
9767
- onConversationStartedChange
9887
+ onConversationStartedChange,
9888
+ renderComposerModeAccessory
9768
9889
  }
9769
9890
  ) })
9770
9891
  }
package/dist/index.mjs CHANGED
@@ -4,7 +4,14 @@ 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
@@ -1078,6 +1085,7 @@ var AiChatProvider = (props) => {
1078
1085
  onLoadMoreSessions,
1079
1086
  onSelectHistorySession,
1080
1087
  onLoadMoreHistoryMessages,
1088
+ renderSessionItemActions,
1081
1089
  enableImageAttachments = true,
1082
1090
  children
1083
1091
  } = props;
@@ -1090,6 +1098,17 @@ var AiChatProvider = (props) => {
1090
1098
  });
1091
1099
  const stopRef = useRef(async (_sessionId) => {
1092
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
+ );
1093
1112
  const defaultApiBaseUrl = "apiBaseUrl" in props ? props.apiBaseUrl : void 0;
1094
1113
  const defaultAuthToken = "authToken" in props ? props.authToken : void 0;
1095
1114
  const defaultTransformStreamPacket = "transformStreamPacket" in props ? props.transformStreamPacket : void 0;
@@ -1149,6 +1168,8 @@ var AiChatProvider = (props) => {
1149
1168
  sendRef,
1150
1169
  retryRef,
1151
1170
  stopRef,
1171
+ stopSession,
1172
+ registerStopSession,
1152
1173
  renderMessageBlock,
1153
1174
  handleQuestionnaireSubmit,
1154
1175
  handleConfirmationSubmit,
@@ -1158,7 +1179,8 @@ var AiChatProvider = (props) => {
1158
1179
  historySessionList,
1159
1180
  onLoadMoreSessions,
1160
1181
  onSelectHistorySession,
1161
- onLoadMoreHistoryMessages
1182
+ onLoadMoreHistoryMessages,
1183
+ renderSessionItemActions
1162
1184
  }),
1163
1185
  [
1164
1186
  axiosInstance,
@@ -1174,10 +1196,13 @@ var AiChatProvider = (props) => {
1174
1196
  onLoadMoreSessions,
1175
1197
  onLoadMoreHistoryMessages,
1176
1198
  onSelectHistorySession,
1199
+ renderSessionItemActions,
1177
1200
  renderMessageBlock,
1178
1201
  sendRef,
1179
1202
  retryRef,
1180
1203
  stopRef,
1204
+ stopSession,
1205
+ registerStopSession,
1181
1206
  store,
1182
1207
  transport
1183
1208
  ]
@@ -1186,7 +1211,7 @@ var AiChatProvider = (props) => {
1186
1211
  };
1187
1212
 
1188
1213
  // src/components/chat-thread/index.tsx
1189
- import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
1214
+ import { useCallback as useCallback4, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef5, useState as useState5 } from "react";
1190
1215
  import styled9 from "@emotion/styled";
1191
1216
 
1192
1217
  // src/context/use-chat-context.ts
@@ -1207,7 +1232,7 @@ var useChatStore = (selector) => {
1207
1232
  var CHAT_THREAD_SCROLL_TOP_GAP = 16;
1208
1233
 
1209
1234
  // src/components/chat-thread/components/chat-message-item.tsx
1210
- import { Fragment, memo, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useState as useState4 } from "react";
1235
+ import { Fragment, memo, useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useState as useState4 } from "react";
1211
1236
  import styled7 from "@emotion/styled";
1212
1237
  import { keyframes } from "@emotion/react";
1213
1238
  import ReactMarkdown from "react-markdown";
@@ -1216,7 +1241,7 @@ import remarkMath from "remark-math";
1216
1241
  import rehypeKatex from "rehype-katex";
1217
1242
 
1218
1243
  // src/components/chat-thread/hooks/use-chat-message-reveal.ts
1219
- 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";
1220
1245
 
1221
1246
  // src/components/chat-thread/lib/message-reveal.ts
1222
1247
  var STREAM_REVEAL_TICK_MS = 36;
@@ -1356,7 +1381,7 @@ var useChatMessageReveal = (message) => {
1356
1381
  createRevealState
1357
1382
  );
1358
1383
  const { batchedTargetUnitCount, displayedUnitCount, isFreshBlockActive } = state;
1359
- const commitBatchedTargetUnitCount = useCallback(
1384
+ const commitBatchedTargetUnitCount = useCallback2(
1360
1385
  (nextTargetUnitCount) => {
1361
1386
  batchedTargetUnitCountRef.current = nextTargetUnitCount;
1362
1387
  dispatch({
@@ -3245,7 +3270,7 @@ var useUserMessageCollapse = ({
3245
3270
  const [isCollapsible, setIsCollapsible] = useState4(false);
3246
3271
  const [isExpanded, setIsExpanded] = useState4(false);
3247
3272
  const [bodyStackElement, setBodyStackElement] = useState4(null);
3248
- const syncCollapseState = useCallback2(
3273
+ const syncCollapseState = useCallback3(
3249
3274
  (element) => {
3250
3275
  const nextCollapsible = enabled && (element?.scrollHeight ?? 0) > USER_MESSAGE_COLLAPSE_HEIGHT_PX;
3251
3276
  setIsCollapsible(nextCollapsible);
@@ -3255,7 +3280,7 @@ var useUserMessageCollapse = ({
3255
3280
  },
3256
3281
  [enabled]
3257
3282
  );
3258
- const bodyStackRef = useCallback2(
3283
+ const bodyStackRef = useCallback3(
3259
3284
  (node) => {
3260
3285
  setBodyStackElement(node);
3261
3286
  syncCollapseState(node);
@@ -4172,7 +4197,7 @@ var ChatThreadView = ({
4172
4197
  const [latestTurnMinHeight, setLatestTurnMinHeight] = useState5(0);
4173
4198
  const [isDetached, setIsDetached] = useState5(false);
4174
4199
  const [pendingNewMessageCount, setPendingNewMessageCount] = useState5(0);
4175
- const measureLatestTurnMinHeight = useCallback3(() => {
4200
+ const measureLatestTurnMinHeight = useCallback4(() => {
4176
4201
  const container = containerRef.current;
4177
4202
  if (!container)
4178
4203
  return;
@@ -4182,7 +4207,7 @@ var ChatThreadView = ({
4182
4207
  const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
4183
4208
  setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
4184
4209
  }, []);
4185
- const scrollToBottom = useCallback3((force = false) => {
4210
+ const scrollToBottom = useCallback4((force = false) => {
4186
4211
  const container = containerRef.current;
4187
4212
  if (!container)
4188
4213
  return false;
@@ -4198,12 +4223,12 @@ var ChatThreadView = ({
4198
4223
  }
4199
4224
  return true;
4200
4225
  }, []);
4201
- const markThreadPinned = useCallback3(() => {
4226
+ const markThreadPinned = useCallback4(() => {
4202
4227
  isPinnedRef.current = true;
4203
4228
  setIsDetached(false);
4204
4229
  setPendingNewMessageCount(0);
4205
4230
  }, []);
4206
- const scrollToBottomAndPin = useCallback3(
4231
+ const scrollToBottomAndPin = useCallback4(
4207
4232
  (force = false) => {
4208
4233
  const didScroll = scrollToBottom(force);
4209
4234
  if (!didScroll)
@@ -4212,7 +4237,7 @@ var ChatThreadView = ({
4212
4237
  },
4213
4238
  [markThreadPinned, scrollToBottom]
4214
4239
  );
4215
- const handleLoadPreviousMessages = useCallback3(async () => {
4240
+ const handleLoadPreviousMessages = useCallback4(async () => {
4216
4241
  const container = containerRef.current;
4217
4242
  if (!container || !onLoadPreviousMessages || isLoadingPreviousMessages) {
4218
4243
  return;
@@ -4237,7 +4262,7 @@ var ChatThreadView = ({
4237
4262
  nextContainer.scrollTop = nextContainer.scrollHeight - previousScrollHeight + previousScrollTop;
4238
4263
  });
4239
4264
  }, [isLoadingPreviousMessages, onLoadPreviousMessages]);
4240
- const handleContainerScroll = useCallback3(() => {
4265
+ const handleContainerScroll = useCallback4(() => {
4241
4266
  const container = containerRef.current;
4242
4267
  if (!container)
4243
4268
  return;
@@ -4466,13 +4491,13 @@ var ChatThread = () => {
4466
4491
  onLoadMoreHistoryMessages,
4467
4492
  labels
4468
4493
  } = useChatContext();
4469
- const handleRetry = useCallback3(() => {
4494
+ const handleRetry = useCallback4(() => {
4470
4495
  if (!activeSessionId)
4471
4496
  return;
4472
4497
  clearSessionError(activeSessionId);
4473
4498
  void retryRef.current(activeSessionId);
4474
4499
  }, [activeSessionId, clearSessionError, retryRef]);
4475
- const handleQuestionnaireSubmit = useCallback3(
4500
+ const handleQuestionnaireSubmit = useCallback4(
4476
4501
  async (submission) => {
4477
4502
  const sourceSessionId = activeSessionId;
4478
4503
  if (customQuestionnaireSubmit) {
@@ -4507,7 +4532,7 @@ var ChatThread = () => {
4507
4532
  },
4508
4533
  [activeSessionId, customQuestionnaireSubmit, preferredMode, sendRef, updateQA]
4509
4534
  );
4510
- const handleConfirmation = useCallback3(
4535
+ const handleConfirmation = useCallback4(
4511
4536
  async (submission) => {
4512
4537
  const sourceSessionId = activeSessionId;
4513
4538
  if (customConfirmationSubmit) {
@@ -4526,7 +4551,7 @@ var ChatThread = () => {
4526
4551
  },
4527
4552
  [activeSessionId, customConfirmationSubmit, preferredMode, sendRef]
4528
4553
  );
4529
- const handleLoadPreviousMessages = useCallback3(async () => {
4554
+ const handleLoadPreviousMessages = useCallback4(async () => {
4530
4555
  if (!activeSession || !onLoadMoreHistoryMessages || !historyMessagePagination?.hasMorePrevious || !historyMessagePagination.previousCursor || historyMessagePagination.isLoadingPrevious) {
4531
4556
  return;
4532
4557
  }
@@ -4721,7 +4746,7 @@ var ScrollToLatestBadge = styled9.span`
4721
4746
  `;
4722
4747
 
4723
4748
  // src/components/chat-composer/index.tsx
4724
- import { useCallback as useCallback8, useEffect as useEffect10, useLayoutEffect as useLayoutEffect6, useRef as useRef11, useState as useState11 } from "react";
4749
+ import { useCallback as useCallback9, useEffect as useEffect10, useLayoutEffect as useLayoutEffect6, useRef as useRef11, useState as useState11 } from "react";
4725
4750
  import styled14 from "@emotion/styled";
4726
4751
 
4727
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
@@ -7319,7 +7344,7 @@ var resolveSendSession = ({
7319
7344
  };
7320
7345
 
7321
7346
  // src/components/chat-composer/hooks/use-chat-composer.ts
7322
- import { useCallback as useCallback7, useEffect as useEffect9, useRef as useRef10, useState as useState9 } from "react";
7347
+ import { useCallback as useCallback8, useEffect as useEffect9, useRef as useRef10, useState as useState9 } from "react";
7323
7348
 
7324
7349
  // src/components/chat-composer/hooks/use-composer-attachments.ts
7325
7350
  import { useEffect as useEffect8, useRef as useRef9, useState as useState8 } from "react";
@@ -7498,7 +7523,7 @@ var useChatComposer = () => {
7498
7523
  const [isSkillsLoading, setIsSkillsLoading] = useState9(
7499
7524
  () => Boolean(skillsLoader) && !getCachedSkills(skillsLoader).resolved
7500
7525
  );
7501
- const fetchModels = useCallback7(async () => {
7526
+ const fetchModels = useCallback8(async () => {
7502
7527
  setIsModelsLoading(true);
7503
7528
  setIsModelsError(false);
7504
7529
  try {
@@ -7519,7 +7544,7 @@ var useChatComposer = () => {
7519
7544
  setAvailableSkills(cachedSkills.skills);
7520
7545
  setIsSkillsLoading(Boolean(skillsLoader) && !cachedSkills.resolved);
7521
7546
  }, [skillsLoader]);
7522
- const fetchSkills = useCallback7(async () => {
7547
+ const fetchSkills = useCallback8(async () => {
7523
7548
  if (!skillsLoader) {
7524
7549
  setAvailableSkills([]);
7525
7550
  setIsSkillsLoading(false);
@@ -7594,11 +7619,11 @@ var useChatComposer = () => {
7594
7619
  window.clearTimeout(stopRequest.timeoutId);
7595
7620
  stopRequest.timeoutId = null;
7596
7621
  };
7597
- const clearStopRequest = useCallback7((sessionId) => {
7622
+ const clearStopRequest = useCallback8((sessionId) => {
7598
7623
  clearStopTimeout(sessionId);
7599
7624
  stopRequestBySessionRef.current.delete(sessionId);
7600
7625
  }, []);
7601
- const moveSessionRuntimeState = useCallback7(
7626
+ const moveSessionRuntimeState = useCallback8(
7602
7627
  (previousSessionId, nextSessionId) => {
7603
7628
  if (previousSessionId === nextSessionId) {
7604
7629
  return;
@@ -7616,7 +7641,7 @@ var useChatComposer = () => {
7616
7641
  },
7617
7642
  []
7618
7643
  );
7619
- const finalizeStop = useCallback7(
7644
+ const finalizeStop = useCallback8(
7620
7645
  (sessionId) => {
7621
7646
  const stopRequest = stopRequestBySessionRef.current.get(sessionId);
7622
7647
  if (stopRequest) {
@@ -7633,7 +7658,7 @@ var useChatComposer = () => {
7633
7658
  },
7634
7659
  [clearStopRequest, finalizeStoppedStreamingMessage]
7635
7660
  );
7636
- const runStream = useCallback7(
7661
+ const runStream = useCallback8(
7637
7662
  async ({
7638
7663
  localSessionId,
7639
7664
  sessionId,
@@ -7758,7 +7783,7 @@ var useChatComposer = () => {
7758
7783
  store
7759
7784
  ]
7760
7785
  );
7761
- const send = useCallback7(
7786
+ const send = useCallback8(
7762
7787
  async (contentOverride, options) => {
7763
7788
  const content = (contentOverride ?? value).trim();
7764
7789
  const includeComposerAttachments = options?.includeComposerAttachments ?? true;
@@ -7838,7 +7863,7 @@ var useChatComposer = () => {
7838
7863
  store
7839
7864
  ]
7840
7865
  );
7841
- const openSkillPicker = useCallback7(() => {
7866
+ const openSkillPicker = useCallback8(() => {
7842
7867
  setValue((current) => {
7843
7868
  const matchedSkillQuery = current.match(/(^|\s)\/([^\s/]*)$/);
7844
7869
  if (matchedSkillQuery && matchedSkillQuery.index !== void 0) {
@@ -7851,7 +7876,7 @@ var useChatComposer = () => {
7851
7876
  return /\s$/.test(current) ? `${current}/` : `${current} /`;
7852
7877
  });
7853
7878
  }, []);
7854
- const stopSession = useCallback7(
7879
+ const stopSession = useCallback8(
7855
7880
  async (sessionId) => {
7856
7881
  const storeState = store.getState();
7857
7882
  const isSessionStreaming = storeState.isStreamingBySession[sessionId] ?? false;
@@ -8634,6 +8659,7 @@ var ChatComposerView = ({
8634
8659
  modelLoadingLabel,
8635
8660
  modelLoadFailedLabel,
8636
8661
  modelUnavailableLabel,
8662
+ modeAccessory,
8637
8663
  onValueChange,
8638
8664
  onPickImages,
8639
8665
  onPasteImages,
@@ -8694,13 +8720,13 @@ var ChatComposerView = ({
8694
8720
  ],
8695
8721
  whileElementsMounted: autoUpdate
8696
8722
  });
8697
- const setSkillMenuReference = useCallback8(
8723
+ const setSkillMenuReference = useCallback9(
8698
8724
  (element) => {
8699
8725
  refs.setReference(element);
8700
8726
  },
8701
8727
  [refs]
8702
8728
  );
8703
- const setSkillMenuFloating = useCallback8(
8729
+ const setSkillMenuFloating = useCallback9(
8704
8730
  (element) => {
8705
8731
  refs.setFloating(element);
8706
8732
  },
@@ -8887,7 +8913,8 @@ var ChatComposerView = ({
8887
8913
  labels: modeLabels,
8888
8914
  onChange: onSelectedModeChange
8889
8915
  }
8890
- )
8916
+ ),
8917
+ modeAccessory
8891
8918
  ] }),
8892
8919
  /* @__PURE__ */ jsxs11(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8893
8920
  /* @__PURE__ */ jsx16(
@@ -8945,8 +8972,17 @@ var ChatComposerView = ({
8945
8972
  ) }) : null
8946
8973
  ] });
8947
8974
  };
8948
- var ChatComposer = () => {
8949
- 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();
8950
8986
  const { state, actions } = useChatComposer();
8951
8987
  const { send, retry } = actions;
8952
8988
  useEffect10(() => {
@@ -8954,13 +8990,17 @@ var ChatComposer = () => {
8954
8990
  retryRef.current = async (sessionId) => {
8955
8991
  retry(sessionId);
8956
8992
  };
8957
- stopRef.current = actions.stopSession;
8958
- }, [actions.stopSession, retry, retryRef, send, sendRef, stopRef]);
8993
+ registerStopSession(actions.stopSession);
8994
+ }, [actions.stopSession, registerStopSession, retry, retryRef, send, sendRef]);
8959
8995
  const modeLabels = {
8960
8996
  ask: labels.modeLabelAsk,
8961
8997
  plan: labels.modeLabelPlan,
8962
8998
  agent: labels.modeLabelAgent
8963
8999
  };
9000
+ const modeAccessory = renderModeAccessory?.({
9001
+ selectedMode: state.selectedMode,
9002
+ disabled: state.isStreaming
9003
+ });
8964
9004
  return /* @__PURE__ */ jsx16(
8965
9005
  ChatComposerView,
8966
9006
  {
@@ -8990,6 +9030,7 @@ var ChatComposer = () => {
8990
9030
  modelLoadingLabel: labels.modelLoading,
8991
9031
  modelLoadFailedLabel: labels.modelLoadFailed,
8992
9032
  modelUnavailableLabel: labels.modelUnavailable,
9033
+ modeAccessory,
8993
9034
  onValueChange: actions.setValue,
8994
9035
  onPickImages: actions.pickImages,
8995
9036
  onPasteImages: actions.pasteImages,
@@ -9278,26 +9319,37 @@ var SkillButton = styled14.button`
9278
9319
  `;
9279
9320
 
9280
9321
  // src/components/chat-conversation-list/index.tsx
9281
- import { useEffect as useEffect11, useRef as useRef12 } from "react";
9322
+ import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef12 } from "react";
9282
9323
  import styled16 from "@emotion/styled";
9283
9324
 
9284
9325
  // src/components/chat-conversation-list/components/chat-session-item.tsx
9285
9326
  import { memo as memo2 } from "react";
9286
9327
  import styled15 from "@emotion/styled";
9287
- import { jsx as jsx17 } from "@emotion/react/jsx-runtime";
9288
- var ChatSessionItem = memo2(({ session, isActive, onClick }) => {
9289
- return /* @__PURE__ */ jsx17(
9290
- SessionButton,
9291
- {
9292
- type: "button",
9293
- "data-active": isActive,
9294
- "data-testid": `chat-session-item-${session.sessionId}`,
9295
- onClick: () => onClick(session.sessionId),
9296
- children: /* @__PURE__ */ jsx17(SessionTitle, { children: session.title })
9297
- }
9298
- );
9299
- });
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
+ );
9300
9346
  ChatSessionItem.displayName = "ChatSessionItem";
9347
+ var SessionRow = styled15.div`
9348
+ display: flex;
9349
+ align-items: center;
9350
+ gap: 8px;
9351
+ min-width: 0;
9352
+ `;
9301
9353
  var SessionTitle = styled15.span`
9302
9354
  display: block;
9303
9355
  min-width: 0;
@@ -9306,6 +9358,8 @@ var SessionTitle = styled15.span`
9306
9358
  white-space: nowrap;
9307
9359
  `;
9308
9360
  var SessionButton = styled15.button`
9361
+ flex: 1;
9362
+ min-width: 0;
9309
9363
  border: 1px solid transparent;
9310
9364
  border-radius: 12px;
9311
9365
  padding: 12px;
@@ -9338,6 +9392,12 @@ var SessionButton = styled15.button`
9338
9392
  background: rgba(255, 255, 255, 0.08);
9339
9393
  }
9340
9394
  `;
9395
+ var SessionActions = styled15.div`
9396
+ display: flex;
9397
+ align-items: center;
9398
+ gap: 6px;
9399
+ flex: 0 0 auto;
9400
+ `;
9341
9401
 
9342
9402
  // src/components/chat-conversation-list/lib/history-session-selection.ts
9343
9403
  var shouldLoadHistorySessionMessages = ({
@@ -9358,7 +9418,7 @@ var shouldLoadHistorySessionMessages = ({
9358
9418
  };
9359
9419
 
9360
9420
  // src/components/chat-conversation-list/index.tsx
9361
- 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";
9362
9422
  var SCROLL_LOAD_MORE_THRESHOLD_PX = 80;
9363
9423
  var shouldLoadMoreSessions = ({
9364
9424
  scrollTop,
@@ -9367,12 +9427,37 @@ var shouldLoadMoreSessions = ({
9367
9427
  threshold = SCROLL_LOAD_MORE_THRESHOLD_PX
9368
9428
  }) => scrollHeight - scrollTop - clientHeight <= threshold;
9369
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
+ };
9370
9445
  var ChatConversationList = () => {
9371
- 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();
9372
9456
  const localSessions = useChatStore((s) => s.sessions);
9373
9457
  const activeSessionId = useChatStore((s) => s.activeSessionId);
9374
9458
  const startNewChat = useChatStore((s) => s.startNewChat);
9375
9459
  const setActiveSession = useChatStore((s) => s.setActiveSession);
9460
+ const isStreamingBySession = useChatStore((s) => s.isStreamingBySession);
9376
9461
  const hydrateHistorySessions = useChatStore((s) => s.hydrateHistorySessions);
9377
9462
  const hydrateHistorySessionMessages = useChatStore((s) => s.hydrateHistorySessionMessages);
9378
9463
  const hydrateHistorySessionMessagesPage = useChatStore((s) => s.hydrateHistorySessionMessagesPage);
@@ -9381,6 +9466,7 @@ var ChatConversationList = () => {
9381
9466
  );
9382
9467
  const isLoadingMoreRef = useRef12(false);
9383
9468
  const hasSeenLoadingMoreRef = useRef12(false);
9469
+ const listRef = useRef12(null);
9384
9470
  useEffect11(() => {
9385
9471
  if (!historySessionList)
9386
9472
  return;
@@ -9401,23 +9487,50 @@ var ChatConversationList = () => {
9401
9487
  hasSeenLoadingMoreRef.current = false;
9402
9488
  }, [historySessionList?.sessions.length, historySessionList?.hasMore]);
9403
9489
  const sessions = localSessions;
9404
- const handleSessionListScroll = (event) => {
9405
- 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) {
9406
9499
  return;
9407
9500
  }
9408
- const target = event.currentTarget;
9409
- if (shouldLoadMoreSessions({
9410
- scrollTop: target.scrollTop,
9411
- clientHeight: target.clientHeight,
9412
- scrollHeight: target.scrollHeight
9413
- })) {
9414
- isLoadingMoreRef.current = true;
9415
- void Promise.resolve(onLoadMoreSessions()).catch(() => {
9416
- isLoadingMoreRef.current = false;
9417
- hasSeenLoadingMoreRef.current = false;
9418
- });
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;
9419
9525
  }
9420
- };
9526
+ maybeLoadMoreSessions(listRef.current);
9527
+ }, [isHistorySessionListHydrated, hasMoreSessions, localSessions.length, maybeLoadMoreSessions]);
9528
+ const handleSessionListScroll = useCallback10(
9529
+ (event) => {
9530
+ maybeLoadMoreSessions(event.currentTarget);
9531
+ },
9532
+ [maybeLoadMoreSessions]
9533
+ );
9421
9534
  const handleSelectSession = async (sessionId) => {
9422
9535
  setActiveSession(sessionId);
9423
9536
  const session = sessions.find((item) => item.sessionId === sessionId);
@@ -9454,18 +9567,26 @@ var ChatConversationList = () => {
9454
9567
  );
9455
9568
  }
9456
9569
  };
9457
- return /* @__PURE__ */ jsxs12(Container3, { children: [
9458
- /* @__PURE__ */ jsxs12(Toolbar, { children: [
9570
+ return /* @__PURE__ */ jsxs13(Container3, { children: [
9571
+ /* @__PURE__ */ jsxs13(Toolbar, { children: [
9459
9572
  /* @__PURE__ */ jsx18(Title3, { children: labels.sessionsTitle }),
9460
9573
  /* @__PURE__ */ jsx18(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: startNewChat, children: labels.newChat })
9461
9574
  ] }),
9462
- /* @__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: [
9463
9576
  sessions.map((session) => /* @__PURE__ */ jsx18(
9464
9577
  ChatSessionItem,
9465
9578
  {
9466
9579
  session,
9467
9580
  isActive: activeSessionId === session.sessionId,
9468
- 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
+ })
9469
9590
  },
9470
9591
  session.sessionId
9471
9592
  )),
@@ -9478,6 +9599,7 @@ var ChatConversationList = () => {
9478
9599
  var Container3 = styled16.aside`
9479
9600
  width: 280px;
9480
9601
  min-width: 280px;
9602
+ min-height: 0;
9481
9603
  border-right: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
9482
9604
  display: flex;
9483
9605
  flex-direction: column;
@@ -9523,6 +9645,8 @@ var CreateButton = styled16.button`
9523
9645
  }
9524
9646
  `;
9525
9647
  var List2 = styled16.div`
9648
+ flex: 1 1 0;
9649
+ min-height: 0;
9526
9650
  padding: 0 12px 16px;
9527
9651
  display: flex;
9528
9652
  flex-direction: column;
@@ -9536,7 +9660,7 @@ var StateRow = styled16.div`
9536
9660
  `;
9537
9661
 
9538
9662
  // src/components/ai-chat/index.tsx
9539
- 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";
9540
9664
  var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9541
9665
  "svg",
9542
9666
  {
@@ -9547,7 +9671,7 @@ var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9547
9671
  fill: "none",
9548
9672
  style: { display: "block" },
9549
9673
  xmlns: "http://www.w3.org/2000/svg",
9550
- children: /* @__PURE__ */ jsxs13(
9674
+ children: /* @__PURE__ */ jsxs14(
9551
9675
  "g",
9552
9676
  {
9553
9677
  transform: "translate(1.8909 2.0364)",
@@ -9556,7 +9680,7 @@ var NewTalkIcon = () => /* @__PURE__ */ jsx19(
9556
9680
  strokeLinejoin: "round",
9557
9681
  strokeWidth: "1.36533333",
9558
9682
  children: [
9559
- /* @__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: [
9560
9684
  /* @__PURE__ */ jsx19("path", { d: "M0 0C0 0 1.06666667 1.06666667 3.2 3.2" }),
9561
9685
  /* @__PURE__ */ jsx19("path", { d: "M3.2 0C3.2 0 2.13333333 1.06666667 0 3.2" })
9562
9686
  ] }),
@@ -9584,7 +9708,8 @@ var AiChatWorkspaceContent = ({
9584
9708
  showNewChatButton,
9585
9709
  renderNewChatTrigger,
9586
9710
  showComposerOnlyBeforeFirstMessage = false,
9587
- onConversationStartedChange
9711
+ onConversationStartedChange,
9712
+ renderComposerModeAccessory
9588
9713
  }) => {
9589
9714
  const isConversationStarted = useChatStore(
9590
9715
  (state) => hasStartedConversation({
@@ -9598,17 +9723,18 @@ var AiChatWorkspaceContent = ({
9598
9723
  useEffect12(() => {
9599
9724
  onConversationStartedChange?.(isConversationStarted);
9600
9725
  }, [isConversationStarted, onConversationStartedChange]);
9601
- return /* @__PURE__ */ jsxs13(Root, { "data-testid": "ai-chat", children: [
9726
+ return /* @__PURE__ */ jsxs14(Root, { "data-testid": "ai-chat", children: [
9602
9727
  showConversationList ? /* @__PURE__ */ jsx19(ChatConversationList, {}) : null,
9603
- /* @__PURE__ */ jsxs13(Workspace, { children: [
9728
+ /* @__PURE__ */ jsxs14(Workspace, { children: [
9604
9729
  showNewChatButton && !showConversationList && !shouldShowComposerOnly ? /* @__PURE__ */ jsx19(QuickActions, { renderNewChatTrigger }) : null,
9605
9730
  shouldShowComposerOnly ? null : /* @__PURE__ */ jsx19(ChatThread, {}),
9606
- /* @__PURE__ */ jsx19(ChatComposer, {})
9731
+ /* @__PURE__ */ jsx19(ChatComposer, { renderModeAccessory: renderComposerModeAccessory })
9607
9732
  ] })
9608
9733
  ] });
9609
9734
  };
9610
9735
  var QuickActions = ({ renderNewChatTrigger }) => {
9611
- const { labels, stopRef, store } = useChatContext();
9736
+ const { labels, stopSession = async (_sessionId) => {
9737
+ }, store } = useChatContext();
9612
9738
  const startNewChat = useChatStore((state) => state.startNewChat);
9613
9739
  const activeSessionId = useChatStore((state) => state.activeSessionId);
9614
9740
  const isActiveSessionStreaming = useChatStore(
@@ -9627,14 +9753,14 @@ var QuickActions = ({ renderNewChatTrigger }) => {
9627
9753
  if (!currentSessionId || !isCurrentSessionStreaming) {
9628
9754
  return;
9629
9755
  }
9630
- await stopRef.current(currentSessionId);
9756
+ await stopSession(currentSessionId);
9631
9757
  };
9632
9758
  const handleStartNewChat = async () => {
9633
9759
  const currentState = store.getState();
9634
9760
  const currentSessionId = currentState.activeSessionId;
9635
9761
  const isCurrentSessionStreaming = currentSessionId ? currentState.isStreamingBySession[currentSessionId] ?? false : false;
9636
9762
  if (currentSessionId && isCurrentSessionStreaming) {
9637
- void stopRef.current(currentSessionId);
9763
+ void stopSession(currentSessionId);
9638
9764
  }
9639
9765
  createNewSession();
9640
9766
  };
@@ -9678,6 +9804,7 @@ var AiChat = ({
9678
9804
  renderNewChatTrigger,
9679
9805
  showComposerOnlyBeforeFirstMessage = false,
9680
9806
  onConversationStartedChange,
9807
+ renderComposerModeAccessory,
9681
9808
  ...providerProps
9682
9809
  }) => /* @__PURE__ */ jsx19(
9683
9810
  ConfigProvider,
@@ -9721,7 +9848,8 @@ var AiChat = ({
9721
9848
  showNewChatButton,
9722
9849
  renderNewChatTrigger,
9723
9850
  showComposerOnlyBeforeFirstMessage,
9724
- onConversationStartedChange
9851
+ onConversationStartedChange,
9852
+ renderComposerModeAccessory
9725
9853
  }
9726
9854
  ) })
9727
9855
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghunm/ai-chat",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "AI chat React component library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",