@gendive/chatllm 0.4.0 → 0.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.
@@ -57,6 +57,14 @@ interface AlternativeResponse {
57
57
  model: string;
58
58
  content: string;
59
59
  timestamp: number;
60
+ sources?: SourceItem[];
61
+ }
62
+ interface SourceItem {
63
+ id: string;
64
+ title: string;
65
+ url: string;
66
+ snippet?: string;
67
+ favicon?: string;
60
68
  }
61
69
  interface ChatMessage {
62
70
  id: string;
@@ -65,6 +73,7 @@ interface ChatMessage {
65
73
  model?: string;
66
74
  timestamp: number;
67
75
  alternatives?: AlternativeResponse[];
76
+ sources?: SourceItem[];
68
77
  }
69
78
  interface ChatSession {
70
79
  id: string;
@@ -139,7 +148,7 @@ interface ChatUIProps {
139
148
  /** CSS 클래스 */
140
149
  className?: string;
141
150
  /** 메시지 전송 핸들러 (커스텀 API 사용 시) */
142
- onSendMessage?: (params: SendMessageParams) => Promise<ReadableStream<Uint8Array> | string>;
151
+ onSendMessage?: (params: SendMessageParams) => Promise<SendMessageResponse>;
143
152
  /** 세션 변경 핸들러 */
144
153
  onSessionChange?: (session: ChatSession | null) => void;
145
154
  /** 에러 핸들러 */
@@ -155,6 +164,11 @@ interface SendMessageParams {
155
164
  apiKey?: string;
156
165
  systemPrompt?: string;
157
166
  }
167
+ /** onSendMessage 반환 타입 */
168
+ type SendMessageResponse = ReadableStream<Uint8Array> | string | {
169
+ content: string;
170
+ sources?: SourceItem[];
171
+ };
158
172
  interface ChatUIComponents {
159
173
  /** 사이드바 컴포넌트 */
160
174
  Sidebar?: React.ComponentType<SidebarProps>;
@@ -191,7 +205,9 @@ interface MessageListProps {
191
205
  onEdit: (message: ChatMessage) => void;
192
206
  onRegenerate: (id: string) => void;
193
207
  onQuote: (text: string) => void;
194
- onAskOtherModel?: (messageId: string, targetModel: string) => void;
208
+ onAskOtherModel?: (userMessageId: string, assistantMessageId: string, targetModel: string) => void;
209
+ onSetActiveAlternative?: (assistantMessageId: string, index: number) => void;
210
+ activeAlternatives?: Record<string, number>;
195
211
  models?: ModelConfig[];
196
212
  copiedId: string | null;
197
213
  editingId: string | null;
@@ -206,10 +222,11 @@ interface MessageBubbleProps {
206
222
  onRegenerate?: () => void;
207
223
  onQuote?: (text: string) => void;
208
224
  onAskOtherModel?: (targetModel: string) => void;
225
+ onAlternativeChange?: (index: number) => void;
226
+ nextAssistantMessage?: ChatMessage | null;
227
+ activeAlternativeIndex?: number;
209
228
  models?: ModelConfig[];
210
229
  alternatives?: AlternativeResponse[];
211
- activeAlternativeIndex?: number;
212
- onAlternativeChange?: (index: number) => void;
213
230
  }
214
231
  interface InputProps {
215
232
  value: string;
@@ -268,6 +285,7 @@ interface UseChatUIReturn {
268
285
  copiedMessageId: string | null;
269
286
  editingMessageId: string | null;
270
287
  personalization: PersonalizationConfig;
288
+ activeAlternatives: Record<string, number>;
271
289
  setInput: (value: string) => void;
272
290
  sendMessage: (content?: string) => Promise<void>;
273
291
  stopGeneration: () => void;
@@ -286,6 +304,8 @@ interface UseChatUIReturn {
286
304
  saveEdit: (content: string) => Promise<void>;
287
305
  regenerate: (messageId: string) => Promise<void>;
288
306
  askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
307
+ setActiveAlternative: (assistantMessageId: string, index: number) => void;
308
+ getActiveAlternative: (assistantMessageId: string) => number;
289
309
  updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
290
310
  models: ModelConfig[];
291
311
  }
@@ -322,7 +342,7 @@ interface UseChatUIOptions {
322
342
  /** 압축 후 유지할 메시지 수 */
323
343
  keepRecentMessages?: number;
324
344
  /** 커스텀 메시지 전송 핸들러 */
325
- onSendMessage?: (params: SendMessageParams) => Promise<ReadableStream<Uint8Array> | string>;
345
+ onSendMessage?: (params: SendMessageParams) => Promise<SendMessageResponse>;
326
346
  /** 세션 변경 핸들러 */
327
347
  onSessionChange?: (session: ChatSession | null) => void;
328
348
  /** 에러 핸들러 */
@@ -493,4 +513,4 @@ interface SettingsModalProps {
493
513
  }
494
514
  declare const SettingsModal: React$1.FC<SettingsModalProps>;
495
515
 
496
- export { type ActionItem, type ActionMenuProps, type AlternativeResponse, ChatHeader, ChatInput, type ChatMessage, type ChatSession, ChatSidebar, ChatUI, type ChatUIComponents, type ChatUIProps, EmptyState, type EmptyStateProps, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, type InputProps, LinkChip, type LinkChipProps, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type PersonalizationConfig, type PromptTemplate, type ProviderType, type ResponseStyle, type SendMessageParams, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type ThemeConfig, type ThemeMode, type UseChatUIOptions, type UseChatUIReturn, type UserProfile, useChatUI };
516
+ export { type ActionItem, type ActionMenuProps, type AlternativeResponse, ChatHeader, ChatInput, type ChatMessage, type ChatSession, ChatSidebar, ChatUI, type ChatUIComponents, type ChatUIProps, EmptyState, type EmptyStateProps, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, type InputProps, LinkChip, type LinkChipProps, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type PersonalizationConfig, type PromptTemplate, type ProviderType, type ResponseStyle, type SendMessageParams, type SendMessageResponse, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SourceItem, type ThemeConfig, type ThemeMode, type UseChatUIOptions, type UseChatUIReturn, type UserProfile, useChatUI };
@@ -57,6 +57,14 @@ interface AlternativeResponse {
57
57
  model: string;
58
58
  content: string;
59
59
  timestamp: number;
60
+ sources?: SourceItem[];
61
+ }
62
+ interface SourceItem {
63
+ id: string;
64
+ title: string;
65
+ url: string;
66
+ snippet?: string;
67
+ favicon?: string;
60
68
  }
61
69
  interface ChatMessage {
62
70
  id: string;
@@ -65,6 +73,7 @@ interface ChatMessage {
65
73
  model?: string;
66
74
  timestamp: number;
67
75
  alternatives?: AlternativeResponse[];
76
+ sources?: SourceItem[];
68
77
  }
69
78
  interface ChatSession {
70
79
  id: string;
@@ -139,7 +148,7 @@ interface ChatUIProps {
139
148
  /** CSS 클래스 */
140
149
  className?: string;
141
150
  /** 메시지 전송 핸들러 (커스텀 API 사용 시) */
142
- onSendMessage?: (params: SendMessageParams) => Promise<ReadableStream<Uint8Array> | string>;
151
+ onSendMessage?: (params: SendMessageParams) => Promise<SendMessageResponse>;
143
152
  /** 세션 변경 핸들러 */
144
153
  onSessionChange?: (session: ChatSession | null) => void;
145
154
  /** 에러 핸들러 */
@@ -155,6 +164,11 @@ interface SendMessageParams {
155
164
  apiKey?: string;
156
165
  systemPrompt?: string;
157
166
  }
167
+ /** onSendMessage 반환 타입 */
168
+ type SendMessageResponse = ReadableStream<Uint8Array> | string | {
169
+ content: string;
170
+ sources?: SourceItem[];
171
+ };
158
172
  interface ChatUIComponents {
159
173
  /** 사이드바 컴포넌트 */
160
174
  Sidebar?: React.ComponentType<SidebarProps>;
@@ -191,7 +205,9 @@ interface MessageListProps {
191
205
  onEdit: (message: ChatMessage) => void;
192
206
  onRegenerate: (id: string) => void;
193
207
  onQuote: (text: string) => void;
194
- onAskOtherModel?: (messageId: string, targetModel: string) => void;
208
+ onAskOtherModel?: (userMessageId: string, assistantMessageId: string, targetModel: string) => void;
209
+ onSetActiveAlternative?: (assistantMessageId: string, index: number) => void;
210
+ activeAlternatives?: Record<string, number>;
195
211
  models?: ModelConfig[];
196
212
  copiedId: string | null;
197
213
  editingId: string | null;
@@ -206,10 +222,11 @@ interface MessageBubbleProps {
206
222
  onRegenerate?: () => void;
207
223
  onQuote?: (text: string) => void;
208
224
  onAskOtherModel?: (targetModel: string) => void;
225
+ onAlternativeChange?: (index: number) => void;
226
+ nextAssistantMessage?: ChatMessage | null;
227
+ activeAlternativeIndex?: number;
209
228
  models?: ModelConfig[];
210
229
  alternatives?: AlternativeResponse[];
211
- activeAlternativeIndex?: number;
212
- onAlternativeChange?: (index: number) => void;
213
230
  }
214
231
  interface InputProps {
215
232
  value: string;
@@ -268,6 +285,7 @@ interface UseChatUIReturn {
268
285
  copiedMessageId: string | null;
269
286
  editingMessageId: string | null;
270
287
  personalization: PersonalizationConfig;
288
+ activeAlternatives: Record<string, number>;
271
289
  setInput: (value: string) => void;
272
290
  sendMessage: (content?: string) => Promise<void>;
273
291
  stopGeneration: () => void;
@@ -286,6 +304,8 @@ interface UseChatUIReturn {
286
304
  saveEdit: (content: string) => Promise<void>;
287
305
  regenerate: (messageId: string) => Promise<void>;
288
306
  askOtherModel: (messageId: string, targetModel: string) => Promise<void>;
307
+ setActiveAlternative: (assistantMessageId: string, index: number) => void;
308
+ getActiveAlternative: (assistantMessageId: string) => number;
289
309
  updatePersonalization: (config: Partial<PersonalizationConfig>) => void;
290
310
  models: ModelConfig[];
291
311
  }
@@ -322,7 +342,7 @@ interface UseChatUIOptions {
322
342
  /** 압축 후 유지할 메시지 수 */
323
343
  keepRecentMessages?: number;
324
344
  /** 커스텀 메시지 전송 핸들러 */
325
- onSendMessage?: (params: SendMessageParams) => Promise<ReadableStream<Uint8Array> | string>;
345
+ onSendMessage?: (params: SendMessageParams) => Promise<SendMessageResponse>;
326
346
  /** 세션 변경 핸들러 */
327
347
  onSessionChange?: (session: ChatSession | null) => void;
328
348
  /** 에러 핸들러 */
@@ -493,4 +513,4 @@ interface SettingsModalProps {
493
513
  }
494
514
  declare const SettingsModal: React$1.FC<SettingsModalProps>;
495
515
 
496
- export { type ActionItem, type ActionMenuProps, type AlternativeResponse, ChatHeader, ChatInput, type ChatMessage, type ChatSession, ChatSidebar, ChatUI, type ChatUIComponents, type ChatUIProps, EmptyState, type EmptyStateProps, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, type InputProps, LinkChip, type LinkChipProps, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type PersonalizationConfig, type PromptTemplate, type ProviderType, type ResponseStyle, type SendMessageParams, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type ThemeConfig, type ThemeMode, type UseChatUIOptions, type UseChatUIReturn, type UserProfile, useChatUI };
516
+ export { type ActionItem, type ActionMenuProps, type AlternativeResponse, ChatHeader, ChatInput, type ChatMessage, type ChatSession, ChatSidebar, ChatUI, type ChatUIComponents, type ChatUIProps, EmptyState, type EmptyStateProps, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, type InputProps, LinkChip, type LinkChipProps, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type PersonalizationConfig, type PromptTemplate, type ProviderType, type ResponseStyle, type SendMessageParams, type SendMessageResponse, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SourceItem, type ThemeConfig, type ThemeMode, type UseChatUIOptions, type UseChatUIReturn, type UserProfile, useChatUI };
@@ -108,6 +108,7 @@ var useChatUI = (options) => {
108
108
  ...DEFAULT_PERSONALIZATION,
109
109
  ...initialPersonalization
110
110
  });
111
+ const [activeAlternatives, setActiveAlternatives] = (0, import_react.useState)({});
111
112
  const abortControllerRef = (0, import_react.useRef)(null);
112
113
  const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
113
114
  const messages = currentSession?.messages || [];
@@ -408,6 +409,22 @@ ${contextSummary}` },
408
409
  );
409
410
  return;
410
411
  }
412
+ if (typeof result === "object" && "content" in result) {
413
+ setSessions(
414
+ (prev) => prev.map((s) => {
415
+ if (s.id === capturedSessionId) {
416
+ return {
417
+ ...s,
418
+ messages: s.messages.map(
419
+ (m) => m.id === assistantMessageId ? { ...m, content: result.content, sources: result.sources } : m
420
+ )
421
+ };
422
+ }
423
+ return s;
424
+ })
425
+ );
426
+ return;
427
+ }
411
428
  response = new Response(result);
412
429
  } else {
413
430
  response = await fetch(apiEndpoint, {
@@ -567,6 +584,7 @@ ${currentSession.contextSummary}` },
567
584
  const modelConfig = models.find((m) => m.id === targetModel);
568
585
  const provider = modelConfig?.provider || "ollama";
569
586
  let responseContent = "";
587
+ let responseSources;
570
588
  if (onSendMessage) {
571
589
  const result = await onSendMessage({
572
590
  messages: messagesForApi,
@@ -577,6 +595,9 @@ ${currentSession.contextSummary}` },
577
595
  });
578
596
  if (typeof result === "string") {
579
597
  responseContent = result;
598
+ } else if (typeof result === "object" && "content" in result) {
599
+ responseContent = result.content;
600
+ responseSources = result.sources;
580
601
  } else {
581
602
  const reader = result.getReader();
582
603
  const decoder = new TextDecoder();
@@ -640,7 +661,8 @@ ${currentSession.contextSummary}` },
640
661
  id: generateId("alt"),
641
662
  model: targetModel,
642
663
  content: responseContent,
643
- timestamp: Date.now()
664
+ timestamp: Date.now(),
665
+ sources: responseSources
644
666
  };
645
667
  const capturedSessionId = currentSessionId;
646
668
  setSessions(
@@ -686,6 +708,15 @@ ${currentSession.contextSummary}` },
686
708
  onSendMessage,
687
709
  onError
688
710
  ]);
711
+ const setActiveAlternative = (0, import_react.useCallback)((assistantMessageId, index) => {
712
+ setActiveAlternatives((prev) => ({
713
+ ...prev,
714
+ [assistantMessageId]: index
715
+ }));
716
+ }, []);
717
+ const getActiveAlternative = (0, import_react.useCallback)((assistantMessageId) => {
718
+ return activeAlternatives[assistantMessageId] ?? 0;
719
+ }, [activeAlternatives]);
689
720
  return {
690
721
  // State
691
722
  sessions,
@@ -702,6 +733,7 @@ ${currentSession.contextSummary}` },
702
733
  copiedMessageId,
703
734
  editingMessageId,
704
735
  personalization,
736
+ activeAlternatives,
705
737
  // Actions
706
738
  setInput,
707
739
  sendMessage,
@@ -721,6 +753,8 @@ ${currentSession.contextSummary}` },
721
753
  saveEdit,
722
754
  regenerate,
723
755
  askOtherModel,
756
+ setActiveAlternative,
757
+ getActiveAlternative,
724
758
  updatePersonalization,
725
759
  models
726
760
  };
@@ -2186,15 +2220,20 @@ var MessageBubble = ({
2186
2220
  models,
2187
2221
  alternatives,
2188
2222
  activeAlternativeIndex = 0,
2189
- onAlternativeChange
2223
+ onAlternativeChange,
2224
+ nextAssistantMessage
2190
2225
  }) => {
2191
2226
  const [showActions, setShowActions] = (0, import_react6.useState)(false);
2192
2227
  const [showModelMenu, setShowModelMenu] = (0, import_react6.useState)(false);
2193
2228
  const isUser = message.role === "user";
2194
2229
  const isAssistant = message.role === "assistant";
2195
- const otherModels = models?.filter((m) => m.id !== message.model) || [];
2196
- const displayContent = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.content || message.content : message.content;
2197
- const displayModel = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.model : message.model;
2230
+ const relevantAlternatives = isUser ? alternatives : message.alternatives;
2231
+ const relevantActiveIndex = activeAlternativeIndex;
2232
+ const currentAssistantModel = isUser ? nextAssistantMessage?.model : message.model;
2233
+ const otherModels = models?.filter((m) => m.id !== currentAssistantModel) || [];
2234
+ const displayContent = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.content || message.content : message.content;
2235
+ const displayModel = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.model : message.model;
2236
+ const displaySources = isAssistant && relevantAlternatives && relevantAlternatives.length > 0 && relevantActiveIndex > 0 ? relevantAlternatives[relevantActiveIndex - 1]?.sources : message.sources;
2198
2237
  const handleMouseUp = () => {
2199
2238
  if (!onQuote) return;
2200
2239
  const selection = window.getSelection();
@@ -2319,7 +2358,44 @@ var MessageBubble = ({
2319
2358
  ]
2320
2359
  }
2321
2360
  ),
2322
- alternatives && alternatives.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2361
+ displaySources && displaySources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2362
+ "div",
2363
+ {
2364
+ style: {
2365
+ display: "flex",
2366
+ flexWrap: "wrap",
2367
+ gap: "8px",
2368
+ marginTop: "12px",
2369
+ paddingTop: "12px",
2370
+ borderTop: "1px solid var(--chatllm-border-light, #f3f4f6)"
2371
+ },
2372
+ children: [
2373
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2374
+ "span",
2375
+ {
2376
+ style: {
2377
+ fontSize: "12px",
2378
+ fontWeight: 500,
2379
+ color: "var(--chatllm-text-muted, #9ca3af)",
2380
+ marginRight: "4px"
2381
+ },
2382
+ children: "\uCD9C\uCC98:"
2383
+ }
2384
+ ),
2385
+ displaySources.map((source, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2386
+ LinkChip,
2387
+ {
2388
+ text: source.title,
2389
+ url: source.url,
2390
+ index: index + 1,
2391
+ showFavicon: true
2392
+ },
2393
+ source.id
2394
+ ))
2395
+ ]
2396
+ }
2397
+ ),
2398
+ isUser && relevantAlternatives && relevantAlternatives.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2323
2399
  "div",
2324
2400
  {
2325
2401
  style: {
@@ -2334,30 +2410,30 @@ var MessageBubble = ({
2334
2410
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2335
2411
  "button",
2336
2412
  {
2337
- onClick: () => onAlternativeChange?.(Math.max(0, activeAlternativeIndex - 1)),
2338
- disabled: activeAlternativeIndex === 0,
2413
+ onClick: () => onAlternativeChange?.(Math.max(0, relevantActiveIndex - 1)),
2414
+ disabled: relevantActiveIndex === 0,
2339
2415
  style: {
2340
2416
  ...navButtonStyle,
2341
- opacity: activeAlternativeIndex === 0 ? 0.5 : 1,
2342
- cursor: activeAlternativeIndex === 0 ? "not-allowed" : "pointer"
2417
+ opacity: relevantActiveIndex === 0 ? 0.5 : 1,
2418
+ cursor: relevantActiveIndex === 0 ? "not-allowed" : "pointer"
2343
2419
  },
2344
2420
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "arrow-left-line", size: 14 })
2345
2421
  }
2346
2422
  ),
2347
2423
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: [
2348
- activeAlternativeIndex + 1,
2424
+ relevantActiveIndex + 1,
2349
2425
  " / ",
2350
- alternatives.length + 1
2426
+ relevantAlternatives.length + 1
2351
2427
  ] }),
2352
2428
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2353
2429
  "button",
2354
2430
  {
2355
- onClick: () => onAlternativeChange?.(Math.min(alternatives.length, activeAlternativeIndex + 1)),
2356
- disabled: activeAlternativeIndex === alternatives.length,
2431
+ onClick: () => onAlternativeChange?.(Math.min(relevantAlternatives.length, relevantActiveIndex + 1)),
2432
+ disabled: relevantActiveIndex === relevantAlternatives.length,
2357
2433
  style: {
2358
2434
  ...navButtonStyle,
2359
- opacity: activeAlternativeIndex === alternatives.length ? 0.5 : 1,
2360
- cursor: activeAlternativeIndex === alternatives.length ? "not-allowed" : "pointer"
2435
+ opacity: relevantActiveIndex === relevantAlternatives.length ? 0.5 : 1,
2436
+ cursor: relevantActiveIndex === relevantAlternatives.length ? "not-allowed" : "pointer"
2361
2437
  },
2362
2438
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "arrow-right-line", size: 14 })
2363
2439
  }
@@ -2384,7 +2460,7 @@ var MessageBubble = ({
2384
2460
  ) }),
2385
2461
  isUser && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: onEdit, style: actionButtonStyle, title: "\uC218\uC815", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "edit-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) }),
2386
2462
  isAssistant && onRegenerate && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: onRegenerate, style: actionButtonStyle, title: "\uB2E4\uC2DC \uC0DD\uC131", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconSvg, { name: "refresh-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) }),
2387
- isAssistant && onAskOtherModel && otherModels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
2463
+ isUser && onAskOtherModel && otherModels.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
2388
2464
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2389
2465
  "button",
2390
2466
  {
@@ -2534,6 +2610,8 @@ var MessageList = ({
2534
2610
  onRegenerate,
2535
2611
  onQuote,
2536
2612
  onAskOtherModel,
2613
+ onSetActiveAlternative,
2614
+ activeAlternatives = {},
2537
2615
  models,
2538
2616
  copiedId,
2539
2617
  editingId
@@ -2588,23 +2666,31 @@ var MessageList = ({
2588
2666
  },
2589
2667
  onMouseUp: handleMouseUp,
2590
2668
  children: [
2591
- messages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2592
- MessageBubble,
2593
- {
2594
- message,
2595
- isLoading: isLoading && index === messages.length - 1 && message.role === "assistant",
2596
- isCopied: copiedId === message.id,
2597
- isEditing: editingId === message.id,
2598
- onCopy: () => onCopy(message.content, message.id),
2599
- onEdit: () => onEdit(message),
2600
- onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
2601
- onQuote,
2602
- onAskOtherModel: message.role === "assistant" && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, targetModel) : void 0,
2603
- models,
2604
- alternatives: message.alternatives
2605
- },
2606
- message.id
2607
- )),
2669
+ messages.map((message, index) => {
2670
+ const nextAssistant = message.role === "user" && index + 1 < messages.length ? messages[index + 1] : null;
2671
+ const assistantForAlts = nextAssistant?.role === "assistant" ? nextAssistant : null;
2672
+ const activeAltIndex = assistantForAlts ? activeAlternatives[assistantForAlts.id] ?? 0 : 0;
2673
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2674
+ MessageBubble,
2675
+ {
2676
+ message,
2677
+ isLoading: isLoading && index === messages.length - 1 && message.role === "assistant",
2678
+ isCopied: copiedId === message.id,
2679
+ isEditing: editingId === message.id,
2680
+ onCopy: () => onCopy(message.content, message.id),
2681
+ onEdit: () => onEdit(message),
2682
+ onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
2683
+ onQuote,
2684
+ nextAssistantMessage: assistantForAlts,
2685
+ activeAlternativeIndex: message.role === "user" ? activeAltIndex : activeAlternatives[message.id] ?? 0,
2686
+ onAskOtherModel: message.role === "user" && assistantForAlts && onAskOtherModel ? (targetModel) => onAskOtherModel(message.id, assistantForAlts.id, targetModel) : void 0,
2687
+ onAlternativeChange: message.role === "user" && assistantForAlts && onSetActiveAlternative ? (idx) => onSetActiveAlternative(assistantForAlts.id, idx) : message.role === "assistant" && onSetActiveAlternative ? (idx) => onSetActiveAlternative(message.id, idx) : void 0,
2688
+ models,
2689
+ alternatives: message.role === "assistant" ? message.alternatives : assistantForAlts?.alternatives
2690
+ },
2691
+ message.id
2692
+ );
2693
+ }),
2608
2694
  selectionPosition && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2609
2695
  "div",
2610
2696
  {