@yushaw/sanqian-chat 0.2.39 → 0.2.40

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.
@@ -1260,6 +1260,10 @@ interface HistoryListProps {
1260
1260
  yesterday?: string;
1261
1261
  delete?: string;
1262
1262
  };
1263
+ /** Whether a conversation should be highlighted (host-defined relation) */
1264
+ isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
1265
+ /** Label for highlighted conversations */
1266
+ highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1263
1267
  }
1264
1268
  declare const HistoryList: react.NamedExoticComponent<HistoryListProps>;
1265
1269
 
@@ -1318,6 +1322,34 @@ interface AlertConfig {
1318
1322
  action?: AlertAction;
1319
1323
  dismissible?: boolean;
1320
1324
  }
1325
+ interface CompactChatStateSnapshot {
1326
+ messages: ChatMessage[];
1327
+ isLoading: boolean;
1328
+ isStreaming: boolean;
1329
+ error: string | null;
1330
+ conversationId: string | null;
1331
+ conversationTitle: string | null;
1332
+ capabilities?: UseChatCapabilities;
1333
+ }
1334
+ interface CompactChatController {
1335
+ sendMessage: (content: string) => Promise<void>;
1336
+ trySendMessage: (content: string) => Promise<boolean>;
1337
+ newConversation: (options?: ConversationSwitchOptions) => void;
1338
+ loadConversation: (id: string, options?: ConversationSwitchOptions) => Promise<void>;
1339
+ stopStreaming: () => void;
1340
+ focusInput: () => void;
1341
+ setInputText: (text: string) => void;
1342
+ getState: () => CompactChatStateSnapshot;
1343
+ }
1344
+ interface CompactChatHistoryConfig {
1345
+ /**
1346
+ * Whether a conversation is related to host-side context and should be highlighted.
1347
+ * Example: highlight conversations linked to the current note.
1348
+ */
1349
+ isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
1350
+ /** Label shown on highlighted rows. */
1351
+ highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1352
+ }
1321
1353
  interface CompactChatProps {
1322
1354
  /** Chat adapter for backend communication */
1323
1355
  adapter: ChatAdapter;
@@ -1335,11 +1367,10 @@ interface CompactChatProps {
1335
1367
  onMessageReceived?: (message: ChatMessage) => void;
1336
1368
  /** Called when loading state changes */
1337
1369
  onLoadingChange?: (isLoading: boolean) => void;
1338
- /** Called when chat state changes (messages, conversationId) */
1339
- onStateChange?: (state: {
1340
- messages: ChatMessage[];
1341
- conversationId: string | null;
1342
- }) => void;
1370
+ /** Called when conversation changes (including detached/background completion metadata). */
1371
+ onConversationChange?: (conversationId: string, title?: string, meta?: ConversationChangeMeta) => void;
1372
+ /** Called when chat state changes */
1373
+ onStateChange?: (state: CompactChatStateSnapshot) => void;
1343
1374
  /** Header content (left side) */
1344
1375
  headerLeft?: ReactNode;
1345
1376
  /** Header content (right side) */
@@ -1353,11 +1384,21 @@ interface CompactChatProps {
1353
1384
  /** Ref to expose sendMessage function for external input */
1354
1385
  sendMessageRef?: React.MutableRefObject<((message: string) => void) | null>;
1355
1386
  /** Ref to expose newConversation function */
1356
- newConversationRef?: React.MutableRefObject<(() => void) | null>;
1387
+ newConversationRef?: React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1388
+ /** Ref to expose loadConversation function */
1389
+ loadConversationRef?: React.MutableRefObject<((id: string, options?: ConversationSwitchOptions) => Promise<void>) | null>;
1390
+ /** Ref to expose stopStreaming function */
1391
+ stopStreamingRef?: React.MutableRefObject<(() => void) | null>;
1357
1392
  /** Ref to expose focusInput function */
1358
1393
  focusInputRef?: React.MutableRefObject<(() => void) | null>;
1359
1394
  /** Ref to expose setText function (for filling input externally) */
1360
1395
  setTextRef?: React.MutableRefObject<((text: string) => void) | null>;
1396
+ /** Ref to expose a full controller for advanced integrations */
1397
+ controllerRef?: React.MutableRefObject<CompactChatController | null>;
1398
+ /** Called after a conversation is deleted from history */
1399
+ onConversationDeleted?: (id: string) => void;
1400
+ /** History list customization */
1401
+ historyConfig?: CompactChatHistoryConfig;
1361
1402
  /** Custom message renderer */
1362
1403
  renderMessage?: (message: ChatMessage) => ReactNode;
1363
1404
  /** Empty state content (when no messages) */
@@ -1696,4 +1737,4 @@ declare function ensureChatBaseStyles(): void;
1696
1737
  */
1697
1738
  declare function ensureFullChatStyles(): void;
1698
1739
 
1699
- export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatProps, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
1740
+ export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
@@ -1260,6 +1260,10 @@ interface HistoryListProps {
1260
1260
  yesterday?: string;
1261
1261
  delete?: string;
1262
1262
  };
1263
+ /** Whether a conversation should be highlighted (host-defined relation) */
1264
+ isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
1265
+ /** Label for highlighted conversations */
1266
+ highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1263
1267
  }
1264
1268
  declare const HistoryList: react.NamedExoticComponent<HistoryListProps>;
1265
1269
 
@@ -1318,6 +1322,34 @@ interface AlertConfig {
1318
1322
  action?: AlertAction;
1319
1323
  dismissible?: boolean;
1320
1324
  }
1325
+ interface CompactChatStateSnapshot {
1326
+ messages: ChatMessage[];
1327
+ isLoading: boolean;
1328
+ isStreaming: boolean;
1329
+ error: string | null;
1330
+ conversationId: string | null;
1331
+ conversationTitle: string | null;
1332
+ capabilities?: UseChatCapabilities;
1333
+ }
1334
+ interface CompactChatController {
1335
+ sendMessage: (content: string) => Promise<void>;
1336
+ trySendMessage: (content: string) => Promise<boolean>;
1337
+ newConversation: (options?: ConversationSwitchOptions) => void;
1338
+ loadConversation: (id: string, options?: ConversationSwitchOptions) => Promise<void>;
1339
+ stopStreaming: () => void;
1340
+ focusInput: () => void;
1341
+ setInputText: (text: string) => void;
1342
+ getState: () => CompactChatStateSnapshot;
1343
+ }
1344
+ interface CompactChatHistoryConfig {
1345
+ /**
1346
+ * Whether a conversation is related to host-side context and should be highlighted.
1347
+ * Example: highlight conversations linked to the current note.
1348
+ */
1349
+ isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
1350
+ /** Label shown on highlighted rows. */
1351
+ highlightedLabel?: string | ((conversation: ConversationInfo) => string | null | undefined);
1352
+ }
1321
1353
  interface CompactChatProps {
1322
1354
  /** Chat adapter for backend communication */
1323
1355
  adapter: ChatAdapter;
@@ -1335,11 +1367,10 @@ interface CompactChatProps {
1335
1367
  onMessageReceived?: (message: ChatMessage) => void;
1336
1368
  /** Called when loading state changes */
1337
1369
  onLoadingChange?: (isLoading: boolean) => void;
1338
- /** Called when chat state changes (messages, conversationId) */
1339
- onStateChange?: (state: {
1340
- messages: ChatMessage[];
1341
- conversationId: string | null;
1342
- }) => void;
1370
+ /** Called when conversation changes (including detached/background completion metadata). */
1371
+ onConversationChange?: (conversationId: string, title?: string, meta?: ConversationChangeMeta) => void;
1372
+ /** Called when chat state changes */
1373
+ onStateChange?: (state: CompactChatStateSnapshot) => void;
1343
1374
  /** Header content (left side) */
1344
1375
  headerLeft?: ReactNode;
1345
1376
  /** Header content (right side) */
@@ -1353,11 +1384,21 @@ interface CompactChatProps {
1353
1384
  /** Ref to expose sendMessage function for external input */
1354
1385
  sendMessageRef?: React.MutableRefObject<((message: string) => void) | null>;
1355
1386
  /** Ref to expose newConversation function */
1356
- newConversationRef?: React.MutableRefObject<(() => void) | null>;
1387
+ newConversationRef?: React.MutableRefObject<((options?: ConversationSwitchOptions) => void) | null>;
1388
+ /** Ref to expose loadConversation function */
1389
+ loadConversationRef?: React.MutableRefObject<((id: string, options?: ConversationSwitchOptions) => Promise<void>) | null>;
1390
+ /** Ref to expose stopStreaming function */
1391
+ stopStreamingRef?: React.MutableRefObject<(() => void) | null>;
1357
1392
  /** Ref to expose focusInput function */
1358
1393
  focusInputRef?: React.MutableRefObject<(() => void) | null>;
1359
1394
  /** Ref to expose setText function (for filling input externally) */
1360
1395
  setTextRef?: React.MutableRefObject<((text: string) => void) | null>;
1396
+ /** Ref to expose a full controller for advanced integrations */
1397
+ controllerRef?: React.MutableRefObject<CompactChatController | null>;
1398
+ /** Called after a conversation is deleted from history */
1399
+ onConversationDeleted?: (id: string) => void;
1400
+ /** History list customization */
1401
+ historyConfig?: CompactChatHistoryConfig;
1361
1402
  /** Custom message renderer */
1362
1403
  renderMessage?: (message: ChatMessage) => ReactNode;
1363
1404
  /** Empty state content (when no messages) */
@@ -1696,4 +1737,4 @@ declare function ensureChatBaseStyles(): void;
1696
1737
  */
1697
1738
  declare function ensureFullChatStyles(): void;
1698
1739
 
1699
- export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatProps, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
1740
+ export { AddResourceButton, type AddResourceButtonProps, type AlertAction, AlertBanner, type AlertBannerProps, type AlertConfig, type AlertType, AttachButton, type AttachButtonProps, type AttachConfig, type AttachPosition, type AttachState, type AttachedResource, AttachedResourceTags, type AttachedResourceTagsProps, type AttachmentMenuItem, type AttachmentMenuItemType, type ChatAdapter, type ChatAdapterConfig, type ChatFontSize, ChatInput, type ChatInputHandle, type ChatInputProps, type ChatPanelConfig, type ChatPanelMode, type ChatPanelPosition, type ChatThemeMode, type ChatUiConfig, type ChatUiConfigSerializable, type ChatUiStrings, CompactChat, type CompactChatController, type CompactChatHistoryConfig, type CompactChatProps, type CompactChatStateSnapshot, type ConnectionErrorCode, type ConnectionStatus, type ContextProviderInfo, type ConversationChangeMeta, type ConversationDetail, type ConversationInfo, type ConversationSwitchOptions, FloatingChat, type FloatingChatProps, type FloatingWindowConfig, HistoryList, type HistoryListProps, HistoryModal, type HistoryModalProps, HitlCard, type HitlCardProps, type HitlInterruptData, I18nProvider, type I18nProviderProps, IntermediateSteps, type IntermediateStepsProps, type LinkClickEvent, type LinkHandlerConfig, type Locale, MarkdownRenderer, type MarkdownRendererProps, type MessageBlock, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, type MessageRole, ModeToggleButton, type ModeToggleButtonProps, PanelControls, type PanelControlsProps, PanelHeaderButtons, PanelResizer, Resizer, type ResizerProps, type ResolvedTheme, ResourceChip, ResourceChipList, type ResourceChipListProps, type ResourceChipProps, ResourcePicker, type ResourcePickerItem, type ResourcePickerProps, type ResourcePickerState, SanqianChat, SanqianChatMessage, type SanqianChatMessageProps, type SanqianChatProps, SanqianMessageList, type SanqianMessageListHandle, type SanqianMessageListProps, type SdkAdapterConfig, type SendMessage, type SendMessageOptions, type SessionResource, type SessionResourceEvent, type StoredSessionResource, type StreamEvent, StreamingTimeline, type StreamingTimelineProps, type ThemeMode, ThemeProvider, type ThemeProviderProps, ThinkingSection, type ThinkingSectionProps, ToolArgumentsDisplay, type ToolArgumentsDisplayProps, type ToolCall, type ToolCallStatus, type Translations, type UseAttachStateReturn, type UseChatCapabilities, type UseChatOptions, type UseChatPanelReturn, type UseChatReturn, type UseConnectionOptions, type UseConnectionReturn, type UseConversationsOptions, type UseConversationsReturn, type UseFocusPersistenceOptions, type UseResourcePickerOptions, type UseResourcePickerReturn, type WindowPosition, createChatAdapter, createIpcAdapter, createSdkAdapter, ensureChatBaseStyles, ensureFullChatStyles, getTranslations, resolveChatStrings, useAttachState, useChat, useChatPanel, useChatStyles, useConnection, useConversations, useFocusPersistence, useI18n, useIpcUiConfig, useResolvedUiConfig, useResourcePicker, useStandaloneI18n, useStandaloneTheme, useTheme, useWindowDragLock };
@@ -1920,33 +1920,56 @@ code {
1920
1920
 
1921
1921
  [data-streamdown="code-block-header"] {
1922
1922
  position: absolute !important;
1923
- top: 14px !important;
1924
- right: 8px !important;
1923
+ top: 10px !important;
1924
+ left: 12px !important;
1925
+ right: auto !important;
1925
1926
  z-index: 10;
1926
1927
  padding: 0 !important;
1927
1928
  background: transparent !important;
1928
1929
  border: none !important;
1929
1930
  opacity: 0;
1930
1931
  transition: opacity 150ms ease;
1932
+ pointer-events: none;
1931
1933
  }
1932
1934
 
1933
- [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"] {
1935
+ [data-streamdown="code-block-actions"] {
1936
+ position: absolute !important;
1937
+ top: 10px !important;
1938
+ right: 8px !important;
1939
+ z-index: 11;
1940
+ display: flex !important;
1941
+ align-items: center;
1942
+ gap: 4px;
1943
+ padding: 0 !important;
1944
+ background: transparent !important;
1945
+ border: none !important;
1946
+ opacity: 0;
1947
+ transition: opacity 150ms ease;
1948
+ pointer-events: auto !important;
1949
+ }
1950
+
1951
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"],
1952
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] {
1934
1953
  opacity: 1;
1935
1954
  }
1936
1955
 
1937
1956
  [data-streamdown="code-block-header"] > span:first-child {
1957
+ display: inline-flex;
1938
1958
  font-size: 11px;
1939
1959
  color: var(--color-muted);
1940
1960
  text-transform: lowercase;
1941
- margin-right: 8px;
1942
1961
  }
1943
1962
 
1944
1963
  [data-streamdown="code-block-header"] > div {
1945
1964
  display: flex;
1946
1965
  gap: 4px;
1966
+ margin-left: 8px;
1967
+ pointer-events: auto;
1947
1968
  }
1948
1969
 
1949
- [data-streamdown="code-block-header"] button {
1970
+ /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
1971
+ [data-streamdown="code-block-header"] button,
1972
+ [data-streamdown="code-block-actions"] button {
1950
1973
  padding: 4px !important;
1951
1974
  border: none !important;
1952
1975
  border-radius: 4px !important;
@@ -1955,18 +1978,21 @@ code {
1955
1978
  cursor: pointer;
1956
1979
  }
1957
1980
 
1958
- [data-streamdown="code-block-header"] button:hover {
1981
+ [data-streamdown="code-block-header"] button:hover,
1982
+ [data-streamdown="code-block-actions"] button:hover {
1959
1983
  opacity: 1;
1960
1984
  background: var(--color-border) !important;
1961
1985
  }
1962
1986
 
1963
- [data-streamdown="code-block-header"] svg {
1987
+ [data-streamdown="code-block-header"] svg,
1988
+ [data-streamdown="code-block-actions"] svg {
1964
1989
  color: var(--color-muted) !important;
1965
1990
  width: 12px !important;
1966
1991
  height: 12px !important;
1967
1992
  }
1968
1993
 
1969
- [data-streamdown="code-block-header"] button:hover svg {
1994
+ [data-streamdown="code-block-header"] button:hover svg,
1995
+ [data-streamdown="code-block-actions"] button:hover svg {
1970
1996
  color: var(--color-text) !important;
1971
1997
  }
1972
1998
 
@@ -6967,9 +6993,8 @@ var useChatHeader = (config) => {
6967
6993
  }
6968
6994
  if (typeof config?.alwaysOnTop === "boolean") {
6969
6995
  setIsPinned(config.alwaysOnTop);
6970
- resolvedOnPin?.(config.alwaysOnTop);
6971
6996
  }
6972
- }, [config?.alwaysOnTop, resolvedOnPin]);
6997
+ }, [config?.alwaysOnTop]);
6973
6998
  const showPin = !!resolvedOnPin || typeof config?.alwaysOnTop === "boolean";
6974
6999
  const showClose = !!resolvedOnClose;
6975
7000
  const logoNode = (0, import_react17.useMemo)(() => resolveLogoNode(config?.logo, "header"), [config?.logo]);
@@ -10252,7 +10277,9 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10252
10277
  onDelete,
10253
10278
  onLoadMore,
10254
10279
  isDarkMode = false,
10255
- strings = {}
10280
+ strings = {},
10281
+ isConversationHighlighted,
10282
+ highlightedLabel
10256
10283
  }) {
10257
10284
  const [hoveredId, setHoveredId] = (0, import_react36.useState)(null);
10258
10285
  const loadMoreRef = (0, import_react36.useRef)(null);
@@ -10308,6 +10335,8 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10308
10335
  conversations.map((conv) => {
10309
10336
  const isSelected = conv.id === selectedId;
10310
10337
  const isHovered = conv.id === hoveredId;
10338
+ const isHighlighted = isConversationHighlighted?.(conv) === true;
10339
+ const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? "RELATED" : null;
10311
10340
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
10312
10341
  "div",
10313
10342
  {
@@ -10319,21 +10348,39 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10319
10348
  padding: "0.5rem 0.75rem",
10320
10349
  textAlign: "left",
10321
10350
  cursor: "pointer",
10322
- transition: "background-color 0.15s ease",
10323
- background: isSelected || isHovered ? colors.hover : "transparent"
10351
+ transition: "background-color 0.15s ease, border-color 0.15s ease",
10352
+ border: isHighlighted ? "1px solid var(--chat-accent)" : "1px solid transparent",
10353
+ background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 10%, transparent)" : "transparent"
10324
10354
  },
10325
10355
  onClick: () => onSelect(conv.id),
10326
10356
  onMouseEnter: () => setHoveredId(conv.id),
10327
10357
  onMouseLeave: () => setHoveredId(null),
10328
10358
  children: [
10329
10359
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
10330
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: {
10331
- fontSize: "0.875rem",
10332
- color: colors.text,
10333
- overflow: "hidden",
10334
- textOverflow: "ellipsis",
10335
- whiteSpace: "nowrap"
10336
- }, children: conv.title || "Untitled" }),
10360
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem", minWidth: 0 }, children: [
10361
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: {
10362
+ fontSize: "0.875rem",
10363
+ color: colors.text,
10364
+ overflow: "hidden",
10365
+ textOverflow: "ellipsis",
10366
+ whiteSpace: "nowrap"
10367
+ }, children: conv.title || "Untitled" }),
10368
+ resolvedHighlightLabel && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10369
+ "span",
10370
+ {
10371
+ style: {
10372
+ fontSize: "0.625rem",
10373
+ lineHeight: 1,
10374
+ padding: "0.2rem 0.35rem",
10375
+ borderRadius: 999,
10376
+ background: "var(--chat-accent)",
10377
+ color: "#fff",
10378
+ flexShrink: 0
10379
+ },
10380
+ children: resolvedHighlightLabel
10381
+ }
10382
+ )
10383
+ ] }),
10337
10384
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { fontSize: "0.75rem", color: colors.muted }, children: formatRelativeTime(conv.updatedAt, strings) })
10338
10385
  ] }),
10339
10386
  isHovered && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
@@ -10594,6 +10641,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10594
10641
  onError,
10595
10642
  onMessageReceived,
10596
10643
  onLoadingChange,
10644
+ onConversationChange,
10597
10645
  onStateChange,
10598
10646
  headerLeft,
10599
10647
  headerRight,
@@ -10602,8 +10650,13 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10602
10650
  inputPortalContainer,
10603
10651
  sendMessageRef,
10604
10652
  newConversationRef,
10653
+ loadConversationRef,
10654
+ stopStreamingRef,
10605
10655
  focusInputRef: parentFocusInputRef,
10606
10656
  setTextRef: parentSetTextRef,
10657
+ controllerRef,
10658
+ onConversationDeleted,
10659
+ historyConfig,
10607
10660
  renderMessage,
10608
10661
  emptyState,
10609
10662
  className = "",
@@ -10674,7 +10727,8 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10674
10727
  });
10675
10728
  const chat = useChat({
10676
10729
  adapter,
10677
- onError
10730
+ onError,
10731
+ onConversationChange
10678
10732
  });
10679
10733
  useFocusPersistence({
10680
10734
  containerRef: chatContainerRef,
@@ -10718,6 +10772,16 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10718
10772
  newConversationRef.current = chat.newConversation;
10719
10773
  }
10720
10774
  }, [newConversationRef, chat.newConversation]);
10775
+ (0, import_react38.useEffect)(() => {
10776
+ if (loadConversationRef) {
10777
+ loadConversationRef.current = chat.loadConversation;
10778
+ }
10779
+ }, [loadConversationRef, chat.loadConversation]);
10780
+ (0, import_react38.useEffect)(() => {
10781
+ if (stopStreamingRef) {
10782
+ stopStreamingRef.current = chat.stopStreaming;
10783
+ }
10784
+ }, [stopStreamingRef, chat.stopStreaming]);
10721
10785
  (0, import_react38.useEffect)(() => {
10722
10786
  if (parentFocusInputRef) {
10723
10787
  parentFocusInputRef.current = () => chatInputRef.current?.focus();
@@ -10728,6 +10792,63 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10728
10792
  parentSetTextRef.current = (text) => chatInputRef.current?.setValue(text);
10729
10793
  }
10730
10794
  }, [parentSetTextRef]);
10795
+ const getStateSnapshot = (0, import_react38.useCallback)(() => ({
10796
+ messages: chat.messages,
10797
+ isLoading: chat.isLoading,
10798
+ isStreaming: chat.isStreaming,
10799
+ error: chat.error,
10800
+ conversationId: chat.conversationId,
10801
+ conversationTitle: chat.conversationTitle,
10802
+ capabilities: chat.capabilities
10803
+ }), [
10804
+ chat.messages,
10805
+ chat.isLoading,
10806
+ chat.isStreaming,
10807
+ chat.error,
10808
+ chat.conversationId,
10809
+ chat.conversationTitle,
10810
+ chat.capabilities
10811
+ ]);
10812
+ (0, import_react38.useEffect)(() => {
10813
+ if (!controllerRef) return;
10814
+ controllerRef.current = {
10815
+ sendMessage: chat.sendMessage,
10816
+ trySendMessage: chat.trySendMessage,
10817
+ newConversation: chat.newConversation,
10818
+ loadConversation: chat.loadConversation,
10819
+ stopStreaming: chat.stopStreaming,
10820
+ focusInput: () => chatInputRef.current?.focus(),
10821
+ setInputText: (text) => chatInputRef.current?.setValue(text),
10822
+ getState: getStateSnapshot
10823
+ };
10824
+ }, [
10825
+ controllerRef,
10826
+ chat.sendMessage,
10827
+ chat.trySendMessage,
10828
+ chat.newConversation,
10829
+ chat.loadConversation,
10830
+ chat.stopStreaming,
10831
+ getStateSnapshot
10832
+ ]);
10833
+ (0, import_react38.useEffect)(() => {
10834
+ return () => {
10835
+ if (sendMessageRef) sendMessageRef.current = null;
10836
+ if (newConversationRef) newConversationRef.current = null;
10837
+ if (loadConversationRef) loadConversationRef.current = null;
10838
+ if (stopStreamingRef) stopStreamingRef.current = null;
10839
+ if (parentFocusInputRef) parentFocusInputRef.current = null;
10840
+ if (parentSetTextRef) parentSetTextRef.current = null;
10841
+ if (controllerRef) controllerRef.current = null;
10842
+ };
10843
+ }, [
10844
+ sendMessageRef,
10845
+ newConversationRef,
10846
+ loadConversationRef,
10847
+ stopStreamingRef,
10848
+ parentFocusInputRef,
10849
+ parentSetTextRef,
10850
+ controllerRef
10851
+ ]);
10731
10852
  (0, import_react38.useEffect)(() => {
10732
10853
  if (!adapter.onFocusInput) return;
10733
10854
  return adapter.onFocusInput(() => {
@@ -10749,12 +10870,9 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10749
10870
  }, [chat.isLoading, onLoadingChange]);
10750
10871
  (0, import_react38.useEffect)(() => {
10751
10872
  if (onStateChange) {
10752
- onStateChange({
10753
- messages: chat.messages,
10754
- conversationId: chat.conversationId
10755
- });
10873
+ onStateChange(getStateSnapshot());
10756
10874
  }
10757
- }, [chat.messages, chat.conversationId, onStateChange]);
10875
+ }, [getStateSnapshot, onStateChange]);
10758
10876
  (0, import_react38.useEffect)(() => {
10759
10877
  if (connection.isConnected) {
10760
10878
  resourcePicker.refreshProviders();
@@ -10782,11 +10900,12 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10782
10900
  const handleDeleteConversation = (0, import_react38.useCallback)(
10783
10901
  async (id) => {
10784
10902
  await conversations.deleteConversation(id);
10903
+ onConversationDeleted?.(id);
10785
10904
  if (id === chat.conversationId) {
10786
10905
  chat.newConversation();
10787
10906
  }
10788
10907
  },
10789
- [conversations, chat]
10908
+ [conversations, chat, onConversationDeleted]
10790
10909
  );
10791
10910
  const handleNewChat = (0, import_react38.useCallback)(() => {
10792
10911
  chat.newConversation();
@@ -11047,6 +11166,8 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
11047
11166
  onDelete: handleDeleteConversation,
11048
11167
  onLoadMore: conversations.loadMore,
11049
11168
  isDarkMode: resolvedIsDarkMode,
11169
+ isConversationHighlighted: historyConfig?.isConversationHighlighted,
11170
+ highlightedLabel: historyConfig?.highlightedLabel,
11050
11171
  strings: {
11051
11172
  noHistory: mergedStrings.noHistory,
11052
11173
  loadMore: mergedStrings.loadMore,
@@ -1832,33 +1832,56 @@ code {
1832
1832
 
1833
1833
  [data-streamdown="code-block-header"] {
1834
1834
  position: absolute !important;
1835
- top: 14px !important;
1836
- right: 8px !important;
1835
+ top: 10px !important;
1836
+ left: 12px !important;
1837
+ right: auto !important;
1837
1838
  z-index: 10;
1838
1839
  padding: 0 !important;
1839
1840
  background: transparent !important;
1840
1841
  border: none !important;
1841
1842
  opacity: 0;
1842
1843
  transition: opacity 150ms ease;
1844
+ pointer-events: none;
1843
1845
  }
1844
1846
 
1845
- [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"] {
1847
+ [data-streamdown="code-block-actions"] {
1848
+ position: absolute !important;
1849
+ top: 10px !important;
1850
+ right: 8px !important;
1851
+ z-index: 11;
1852
+ display: flex !important;
1853
+ align-items: center;
1854
+ gap: 4px;
1855
+ padding: 0 !important;
1856
+ background: transparent !important;
1857
+ border: none !important;
1858
+ opacity: 0;
1859
+ transition: opacity 150ms ease;
1860
+ pointer-events: auto !important;
1861
+ }
1862
+
1863
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"],
1864
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] {
1846
1865
  opacity: 1;
1847
1866
  }
1848
1867
 
1849
1868
  [data-streamdown="code-block-header"] > span:first-child {
1869
+ display: inline-flex;
1850
1870
  font-size: 11px;
1851
1871
  color: var(--color-muted);
1852
1872
  text-transform: lowercase;
1853
- margin-right: 8px;
1854
1873
  }
1855
1874
 
1856
1875
  [data-streamdown="code-block-header"] > div {
1857
1876
  display: flex;
1858
1877
  gap: 4px;
1878
+ margin-left: 8px;
1879
+ pointer-events: auto;
1859
1880
  }
1860
1881
 
1861
- [data-streamdown="code-block-header"] button {
1882
+ /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
1883
+ [data-streamdown="code-block-header"] button,
1884
+ [data-streamdown="code-block-actions"] button {
1862
1885
  padding: 4px !important;
1863
1886
  border: none !important;
1864
1887
  border-radius: 4px !important;
@@ -1867,18 +1890,21 @@ code {
1867
1890
  cursor: pointer;
1868
1891
  }
1869
1892
 
1870
- [data-streamdown="code-block-header"] button:hover {
1893
+ [data-streamdown="code-block-header"] button:hover,
1894
+ [data-streamdown="code-block-actions"] button:hover {
1871
1895
  opacity: 1;
1872
1896
  background: var(--color-border) !important;
1873
1897
  }
1874
1898
 
1875
- [data-streamdown="code-block-header"] svg {
1899
+ [data-streamdown="code-block-header"] svg,
1900
+ [data-streamdown="code-block-actions"] svg {
1876
1901
  color: var(--color-muted) !important;
1877
1902
  width: 12px !important;
1878
1903
  height: 12px !important;
1879
1904
  }
1880
1905
 
1881
- [data-streamdown="code-block-header"] button:hover svg {
1906
+ [data-streamdown="code-block-header"] button:hover svg,
1907
+ [data-streamdown="code-block-actions"] button:hover svg {
1882
1908
  color: var(--color-text) !important;
1883
1909
  }
1884
1910
 
@@ -6879,9 +6905,8 @@ var useChatHeader = (config) => {
6879
6905
  }
6880
6906
  if (typeof config?.alwaysOnTop === "boolean") {
6881
6907
  setIsPinned(config.alwaysOnTop);
6882
- resolvedOnPin?.(config.alwaysOnTop);
6883
6908
  }
6884
- }, [config?.alwaysOnTop, resolvedOnPin]);
6909
+ }, [config?.alwaysOnTop]);
6885
6910
  const showPin = !!resolvedOnPin || typeof config?.alwaysOnTop === "boolean";
6886
6911
  const showClose = !!resolvedOnClose;
6887
6912
  const logoNode = useMemo4(() => resolveLogoNode(config?.logo, "header"), [config?.logo]);
@@ -10180,7 +10205,9 @@ var HistoryList = memo16(function HistoryList2({
10180
10205
  onDelete,
10181
10206
  onLoadMore,
10182
10207
  isDarkMode = false,
10183
- strings = {}
10208
+ strings = {},
10209
+ isConversationHighlighted,
10210
+ highlightedLabel
10184
10211
  }) {
10185
10212
  const [hoveredId, setHoveredId] = useState20(null);
10186
10213
  const loadMoreRef = useRef16(null);
@@ -10236,6 +10263,8 @@ var HistoryList = memo16(function HistoryList2({
10236
10263
  conversations.map((conv) => {
10237
10264
  const isSelected = conv.id === selectedId;
10238
10265
  const isHovered = conv.id === hoveredId;
10266
+ const isHighlighted = isConversationHighlighted?.(conv) === true;
10267
+ const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? "RELATED" : null;
10239
10268
  return /* @__PURE__ */ jsxs17(
10240
10269
  "div",
10241
10270
  {
@@ -10247,21 +10276,39 @@ var HistoryList = memo16(function HistoryList2({
10247
10276
  padding: "0.5rem 0.75rem",
10248
10277
  textAlign: "left",
10249
10278
  cursor: "pointer",
10250
- transition: "background-color 0.15s ease",
10251
- background: isSelected || isHovered ? colors.hover : "transparent"
10279
+ transition: "background-color 0.15s ease, border-color 0.15s ease",
10280
+ border: isHighlighted ? "1px solid var(--chat-accent)" : "1px solid transparent",
10281
+ background: isSelected || isHovered ? colors.hover : isHighlighted ? "color-mix(in srgb, var(--chat-accent) 10%, transparent)" : "transparent"
10252
10282
  },
10253
10283
  onClick: () => onSelect(conv.id),
10254
10284
  onMouseEnter: () => setHoveredId(conv.id),
10255
10285
  onMouseLeave: () => setHoveredId(null),
10256
10286
  children: [
10257
10287
  /* @__PURE__ */ jsxs17("div", { style: { minWidth: 0, flex: 1 }, children: [
10258
- /* @__PURE__ */ jsx23("div", { style: {
10259
- fontSize: "0.875rem",
10260
- color: colors.text,
10261
- overflow: "hidden",
10262
- textOverflow: "ellipsis",
10263
- whiteSpace: "nowrap"
10264
- }, children: conv.title || "Untitled" }),
10288
+ /* @__PURE__ */ jsxs17("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem", minWidth: 0 }, children: [
10289
+ /* @__PURE__ */ jsx23("div", { style: {
10290
+ fontSize: "0.875rem",
10291
+ color: colors.text,
10292
+ overflow: "hidden",
10293
+ textOverflow: "ellipsis",
10294
+ whiteSpace: "nowrap"
10295
+ }, children: conv.title || "Untitled" }),
10296
+ resolvedHighlightLabel && /* @__PURE__ */ jsx23(
10297
+ "span",
10298
+ {
10299
+ style: {
10300
+ fontSize: "0.625rem",
10301
+ lineHeight: 1,
10302
+ padding: "0.2rem 0.35rem",
10303
+ borderRadius: 999,
10304
+ background: "var(--chat-accent)",
10305
+ color: "#fff",
10306
+ flexShrink: 0
10307
+ },
10308
+ children: resolvedHighlightLabel
10309
+ }
10310
+ )
10311
+ ] }),
10265
10312
  /* @__PURE__ */ jsx23("div", { style: { fontSize: "0.75rem", color: colors.muted }, children: formatRelativeTime(conv.updatedAt, strings) })
10266
10313
  ] }),
10267
10314
  isHovered && /* @__PURE__ */ jsx23(
@@ -10522,6 +10569,7 @@ var CompactChat = memo18(function CompactChat2({
10522
10569
  onError,
10523
10570
  onMessageReceived,
10524
10571
  onLoadingChange,
10572
+ onConversationChange,
10525
10573
  onStateChange,
10526
10574
  headerLeft,
10527
10575
  headerRight,
@@ -10530,8 +10578,13 @@ var CompactChat = memo18(function CompactChat2({
10530
10578
  inputPortalContainer,
10531
10579
  sendMessageRef,
10532
10580
  newConversationRef,
10581
+ loadConversationRef,
10582
+ stopStreamingRef,
10533
10583
  focusInputRef: parentFocusInputRef,
10534
10584
  setTextRef: parentSetTextRef,
10585
+ controllerRef,
10586
+ onConversationDeleted,
10587
+ historyConfig,
10535
10588
  renderMessage,
10536
10589
  emptyState,
10537
10590
  className = "",
@@ -10602,7 +10655,8 @@ var CompactChat = memo18(function CompactChat2({
10602
10655
  });
10603
10656
  const chat = useChat({
10604
10657
  adapter,
10605
- onError
10658
+ onError,
10659
+ onConversationChange
10606
10660
  });
10607
10661
  useFocusPersistence({
10608
10662
  containerRef: chatContainerRef,
@@ -10646,6 +10700,16 @@ var CompactChat = memo18(function CompactChat2({
10646
10700
  newConversationRef.current = chat.newConversation;
10647
10701
  }
10648
10702
  }, [newConversationRef, chat.newConversation]);
10703
+ useEffect26(() => {
10704
+ if (loadConversationRef) {
10705
+ loadConversationRef.current = chat.loadConversation;
10706
+ }
10707
+ }, [loadConversationRef, chat.loadConversation]);
10708
+ useEffect26(() => {
10709
+ if (stopStreamingRef) {
10710
+ stopStreamingRef.current = chat.stopStreaming;
10711
+ }
10712
+ }, [stopStreamingRef, chat.stopStreaming]);
10649
10713
  useEffect26(() => {
10650
10714
  if (parentFocusInputRef) {
10651
10715
  parentFocusInputRef.current = () => chatInputRef.current?.focus();
@@ -10656,6 +10720,63 @@ var CompactChat = memo18(function CompactChat2({
10656
10720
  parentSetTextRef.current = (text) => chatInputRef.current?.setValue(text);
10657
10721
  }
10658
10722
  }, [parentSetTextRef]);
10723
+ const getStateSnapshot = useCallback22(() => ({
10724
+ messages: chat.messages,
10725
+ isLoading: chat.isLoading,
10726
+ isStreaming: chat.isStreaming,
10727
+ error: chat.error,
10728
+ conversationId: chat.conversationId,
10729
+ conversationTitle: chat.conversationTitle,
10730
+ capabilities: chat.capabilities
10731
+ }), [
10732
+ chat.messages,
10733
+ chat.isLoading,
10734
+ chat.isStreaming,
10735
+ chat.error,
10736
+ chat.conversationId,
10737
+ chat.conversationTitle,
10738
+ chat.capabilities
10739
+ ]);
10740
+ useEffect26(() => {
10741
+ if (!controllerRef) return;
10742
+ controllerRef.current = {
10743
+ sendMessage: chat.sendMessage,
10744
+ trySendMessage: chat.trySendMessage,
10745
+ newConversation: chat.newConversation,
10746
+ loadConversation: chat.loadConversation,
10747
+ stopStreaming: chat.stopStreaming,
10748
+ focusInput: () => chatInputRef.current?.focus(),
10749
+ setInputText: (text) => chatInputRef.current?.setValue(text),
10750
+ getState: getStateSnapshot
10751
+ };
10752
+ }, [
10753
+ controllerRef,
10754
+ chat.sendMessage,
10755
+ chat.trySendMessage,
10756
+ chat.newConversation,
10757
+ chat.loadConversation,
10758
+ chat.stopStreaming,
10759
+ getStateSnapshot
10760
+ ]);
10761
+ useEffect26(() => {
10762
+ return () => {
10763
+ if (sendMessageRef) sendMessageRef.current = null;
10764
+ if (newConversationRef) newConversationRef.current = null;
10765
+ if (loadConversationRef) loadConversationRef.current = null;
10766
+ if (stopStreamingRef) stopStreamingRef.current = null;
10767
+ if (parentFocusInputRef) parentFocusInputRef.current = null;
10768
+ if (parentSetTextRef) parentSetTextRef.current = null;
10769
+ if (controllerRef) controllerRef.current = null;
10770
+ };
10771
+ }, [
10772
+ sendMessageRef,
10773
+ newConversationRef,
10774
+ loadConversationRef,
10775
+ stopStreamingRef,
10776
+ parentFocusInputRef,
10777
+ parentSetTextRef,
10778
+ controllerRef
10779
+ ]);
10659
10780
  useEffect26(() => {
10660
10781
  if (!adapter.onFocusInput) return;
10661
10782
  return adapter.onFocusInput(() => {
@@ -10677,12 +10798,9 @@ var CompactChat = memo18(function CompactChat2({
10677
10798
  }, [chat.isLoading, onLoadingChange]);
10678
10799
  useEffect26(() => {
10679
10800
  if (onStateChange) {
10680
- onStateChange({
10681
- messages: chat.messages,
10682
- conversationId: chat.conversationId
10683
- });
10801
+ onStateChange(getStateSnapshot());
10684
10802
  }
10685
- }, [chat.messages, chat.conversationId, onStateChange]);
10803
+ }, [getStateSnapshot, onStateChange]);
10686
10804
  useEffect26(() => {
10687
10805
  if (connection.isConnected) {
10688
10806
  resourcePicker.refreshProviders();
@@ -10710,11 +10828,12 @@ var CompactChat = memo18(function CompactChat2({
10710
10828
  const handleDeleteConversation = useCallback22(
10711
10829
  async (id) => {
10712
10830
  await conversations.deleteConversation(id);
10831
+ onConversationDeleted?.(id);
10713
10832
  if (id === chat.conversationId) {
10714
10833
  chat.newConversation();
10715
10834
  }
10716
10835
  },
10717
- [conversations, chat]
10836
+ [conversations, chat, onConversationDeleted]
10718
10837
  );
10719
10838
  const handleNewChat = useCallback22(() => {
10720
10839
  chat.newConversation();
@@ -10975,6 +11094,8 @@ var CompactChat = memo18(function CompactChat2({
10975
11094
  onDelete: handleDeleteConversation,
10976
11095
  onLoadMore: conversations.loadMore,
10977
11096
  isDarkMode: resolvedIsDarkMode,
11097
+ isConversationHighlighted: historyConfig?.isConversationHighlighted,
11098
+ highlightedLabel: historyConfig?.highlightedLabel,
10978
11099
  strings: {
10979
11100
  noHistory: mergedStrings.noHistory,
10980
11101
  loadMore: mergedStrings.loadMore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yushaw/sanqian-chat",
3
- "version": "0.2.39",
3
+ "version": "0.2.40",
4
4
  "description": "Floating chat window SDK for Sanqian AI Assistant",
5
5
  "main": "./dist/main/index.js",
6
6
  "types": "./dist/main/index.d.ts",
@@ -413,33 +413,56 @@ code {
413
413
 
414
414
  [data-streamdown="code-block-header"] {
415
415
  position: absolute !important;
416
- top: 14px !important;
417
- right: 8px !important;
416
+ top: 10px !important;
417
+ left: 12px !important;
418
+ right: auto !important;
418
419
  z-index: 10;
419
420
  padding: 0 !important;
420
421
  background: transparent !important;
421
422
  border: none !important;
422
423
  opacity: 0;
423
424
  transition: opacity 150ms ease;
425
+ pointer-events: none;
426
+ }
427
+
428
+ [data-streamdown="code-block-actions"] {
429
+ position: absolute !important;
430
+ top: 10px !important;
431
+ right: 8px !important;
432
+ z-index: 11;
433
+ display: flex !important;
434
+ align-items: center;
435
+ gap: 4px;
436
+ padding: 0 !important;
437
+ background: transparent !important;
438
+ border: none !important;
439
+ opacity: 0;
440
+ transition: opacity 150ms ease;
441
+ pointer-events: auto !important;
424
442
  }
425
443
 
426
- [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"] {
444
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"],
445
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] {
427
446
  opacity: 1;
428
447
  }
429
448
 
430
449
  [data-streamdown="code-block-header"] > span:first-child {
450
+ display: inline-flex;
431
451
  font-size: 11px;
432
452
  color: var(--color-muted);
433
453
  text-transform: lowercase;
434
- margin-right: 8px;
435
454
  }
436
455
 
437
456
  [data-streamdown="code-block-header"] > div {
438
457
  display: flex;
439
458
  gap: 4px;
459
+ margin-left: 8px;
460
+ pointer-events: auto;
440
461
  }
441
462
 
442
- [data-streamdown="code-block-header"] button {
463
+ /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
464
+ [data-streamdown="code-block-header"] button,
465
+ [data-streamdown="code-block-actions"] button {
443
466
  padding: 4px !important;
444
467
  border: none !important;
445
468
  border-radius: 4px !important;
@@ -448,18 +471,21 @@ code {
448
471
  cursor: pointer;
449
472
  }
450
473
 
451
- [data-streamdown="code-block-header"] button:hover {
474
+ [data-streamdown="code-block-header"] button:hover,
475
+ [data-streamdown="code-block-actions"] button:hover {
452
476
  opacity: 1;
453
477
  background: var(--color-border) !important;
454
478
  }
455
479
 
456
- [data-streamdown="code-block-header"] svg {
480
+ [data-streamdown="code-block-header"] svg,
481
+ [data-streamdown="code-block-actions"] svg {
457
482
  color: var(--color-muted) !important;
458
483
  width: 12px !important;
459
484
  height: 12px !important;
460
485
  }
461
486
 
462
- [data-streamdown="code-block-header"] button:hover svg {
487
+ [data-streamdown="code-block-header"] button:hover svg,
488
+ [data-streamdown="code-block-actions"] button:hover svg {
463
489
  color: var(--color-text) !important;
464
490
  }
465
491
 
@@ -420,33 +420,56 @@ code {
420
420
 
421
421
  [data-streamdown="code-block-header"] {
422
422
  position: absolute !important;
423
- top: 14px !important;
424
- right: 8px !important;
423
+ top: 10px !important;
424
+ left: 12px !important;
425
+ right: auto !important;
425
426
  z-index: 10;
426
427
  padding: 0 !important;
427
428
  background: transparent !important;
428
429
  border: none !important;
429
430
  opacity: 0;
430
431
  transition: opacity 150ms ease;
432
+ pointer-events: none;
433
+ }
434
+
435
+ [data-streamdown="code-block-actions"] {
436
+ position: absolute !important;
437
+ top: 10px !important;
438
+ right: 8px !important;
439
+ z-index: 11;
440
+ display: flex !important;
441
+ align-items: center;
442
+ gap: 4px;
443
+ padding: 0 !important;
444
+ background: transparent !important;
445
+ border: none !important;
446
+ opacity: 0;
447
+ transition: opacity 150ms ease;
448
+ pointer-events: auto !important;
431
449
  }
432
450
 
433
- [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"] {
451
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"],
452
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] {
434
453
  opacity: 1;
435
454
  }
436
455
 
437
456
  [data-streamdown="code-block-header"] > span:first-child {
457
+ display: inline-flex;
438
458
  font-size: 11px;
439
459
  color: var(--color-muted);
440
460
  text-transform: lowercase;
441
- margin-right: 8px;
442
461
  }
443
462
 
444
463
  [data-streamdown="code-block-header"] > div {
445
464
  display: flex;
446
465
  gap: 4px;
466
+ margin-left: 8px;
467
+ pointer-events: auto;
447
468
  }
448
469
 
449
- [data-streamdown="code-block-header"] button {
470
+ /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
471
+ [data-streamdown="code-block-header"] button,
472
+ [data-streamdown="code-block-actions"] button {
450
473
  padding: 4px !important;
451
474
  border: none !important;
452
475
  border-radius: 4px !important;
@@ -455,18 +478,21 @@ code {
455
478
  cursor: pointer;
456
479
  }
457
480
 
458
- [data-streamdown="code-block-header"] button:hover {
481
+ [data-streamdown="code-block-header"] button:hover,
482
+ [data-streamdown="code-block-actions"] button:hover {
459
483
  opacity: 1;
460
484
  background: var(--color-border) !important;
461
485
  }
462
486
 
463
- [data-streamdown="code-block-header"] svg {
487
+ [data-streamdown="code-block-header"] svg,
488
+ [data-streamdown="code-block-actions"] svg {
464
489
  color: var(--color-muted) !important;
465
490
  width: 12px !important;
466
491
  height: 12px !important;
467
492
  }
468
493
 
469
- [data-streamdown="code-block-header"] button:hover svg {
494
+ [data-streamdown="code-block-header"] button:hover svg,
495
+ [data-streamdown="code-block-actions"] button:hover svg {
470
496
  color: var(--color-text) !important;
471
497
  }
472
498
 
@@ -413,33 +413,56 @@ code {
413
413
 
414
414
  [data-streamdown="code-block-header"] {
415
415
  position: absolute !important;
416
- top: 14px !important;
417
- right: 8px !important;
416
+ top: 10px !important;
417
+ left: 12px !important;
418
+ right: auto !important;
418
419
  z-index: 10;
419
420
  padding: 0 !important;
420
421
  background: transparent !important;
421
422
  border: none !important;
422
423
  opacity: 0;
423
424
  transition: opacity 150ms ease;
425
+ pointer-events: none;
426
+ }
427
+
428
+ [data-streamdown="code-block-actions"] {
429
+ position: absolute !important;
430
+ top: 10px !important;
431
+ right: 8px !important;
432
+ z-index: 11;
433
+ display: flex !important;
434
+ align-items: center;
435
+ gap: 4px;
436
+ padding: 0 !important;
437
+ background: transparent !important;
438
+ border: none !important;
439
+ opacity: 0;
440
+ transition: opacity 150ms ease;
441
+ pointer-events: auto !important;
424
442
  }
425
443
 
426
- [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"] {
444
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-header"],
445
+ [data-streamdown="code-block"]:hover [data-streamdown="code-block-actions"] {
427
446
  opacity: 1;
428
447
  }
429
448
 
430
449
  [data-streamdown="code-block-header"] > span:first-child {
450
+ display: inline-flex;
431
451
  font-size: 11px;
432
452
  color: var(--color-muted);
433
453
  text-transform: lowercase;
434
- margin-right: 8px;
435
454
  }
436
455
 
437
456
  [data-streamdown="code-block-header"] > div {
438
457
  display: flex;
439
458
  gap: 4px;
459
+ margin-left: 8px;
460
+ pointer-events: auto;
440
461
  }
441
462
 
442
- [data-streamdown="code-block-header"] button {
463
+ /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
464
+ [data-streamdown="code-block-header"] button,
465
+ [data-streamdown="code-block-actions"] button {
443
466
  padding: 4px !important;
444
467
  border: none !important;
445
468
  border-radius: 4px !important;
@@ -448,18 +471,21 @@ code {
448
471
  cursor: pointer;
449
472
  }
450
473
 
451
- [data-streamdown="code-block-header"] button:hover {
474
+ [data-streamdown="code-block-header"] button:hover,
475
+ [data-streamdown="code-block-actions"] button:hover {
452
476
  opacity: 1;
453
477
  background: var(--color-border) !important;
454
478
  }
455
479
 
456
- [data-streamdown="code-block-header"] svg {
480
+ [data-streamdown="code-block-header"] svg,
481
+ [data-streamdown="code-block-actions"] svg {
457
482
  color: var(--color-muted) !important;
458
483
  width: 12px !important;
459
484
  height: 12px !important;
460
485
  }
461
486
 
462
- [data-streamdown="code-block-header"] button:hover svg {
487
+ [data-streamdown="code-block-header"] button:hover svg,
488
+ [data-streamdown="code-block-actions"] button:hover svg {
463
489
  color: var(--color-text) !important;
464
490
  }
465
491