@gendive/chatllm 0.21.5 → 0.21.6

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.
@@ -1819,6 +1819,46 @@ interface UseFloatingWidgetReturn {
1819
1819
  toggle: () => void;
1820
1820
  setTab: (tabKey: string) => void;
1821
1821
  }
1822
+ /**
1823
+ * @description 대화 검색 결과 스니펫
1824
+ * @Todo vibecode - DB에서 반환되는 과거 대화 조각
1825
+ */
1826
+ interface ConversationSnippet {
1827
+ /** 세션 ID */
1828
+ sessionId: string;
1829
+ /** 세션 제목 */
1830
+ sessionTitle?: string;
1831
+ /** 발화자 */
1832
+ role: 'user' | 'assistant';
1833
+ /** 메시지 내용 (또는 발췌) */
1834
+ content: string;
1835
+ /** 메시지 타임스탬프 */
1836
+ timestamp?: number;
1837
+ /** 검색 관련도 점수 (0-1) */
1838
+ relevance?: number;
1839
+ }
1840
+ /**
1841
+ * @description 대화 검색 옵션
1842
+ * @Todo vibecode - 검색 범위 제한용
1843
+ */
1844
+ interface ConversationSearchOptions {
1845
+ /** 최대 결과 수 (기본: 5) */
1846
+ limit?: number;
1847
+ /** 특정 세션으로 제한 */
1848
+ sessionId?: string;
1849
+ }
1850
+ /**
1851
+ * @description createConversationSearchSkill 팩토리 옵션
1852
+ * @Todo vibecode - 소비 앱이 DB 검색 구현을 제공
1853
+ */
1854
+ interface ConversationSearchSkillOptions {
1855
+ /** 검색 구현 (호스트 DB 쿼리) */
1856
+ onSearch: (query: string, options?: ConversationSearchOptions) => Promise<ConversationSnippet[]>;
1857
+ /** 스킬 라벨 (기본: '대화 검색') */
1858
+ label?: string;
1859
+ /** 비활성화 여부 */
1860
+ disabled?: boolean;
1861
+ }
1822
1862
 
1823
1863
  /**
1824
1864
  * @description ChatUI 메인 컴포넌트
@@ -2401,6 +2441,30 @@ declare const createAdvancedResearchSkill: (options: AdvancedResearchOptions) =>
2401
2441
  */
2402
2442
  declare const createDeepResearchSkill: (callbacks: DeepResearchCallbacks) => SkillConfig;
2403
2443
 
2444
+ /**
2445
+ * @description 대화 검색 스킬 팩토리
2446
+ * @Todo vibecode - 호스트 DB 검색을 SkillConfig로 래핑하여 LLM tool use로 과거 대화 검색
2447
+ *
2448
+ * 사용 예시:
2449
+ * ```ts
2450
+ * import { createConversationSearchSkill } from '@gendive/chatllm/react';
2451
+ *
2452
+ * const searchSkill = createConversationSearchSkill({
2453
+ * onSearch: async (query, options) => {
2454
+ * return await db.conversations.search(query, options?.limit);
2455
+ * },
2456
+ * });
2457
+ *
2458
+ * <ChatUI skills={{ conversation_search: searchSkill }} />
2459
+ * ```
2460
+ */
2461
+
2462
+ /**
2463
+ * @description 대화 검색 스킬 생성 팩토리
2464
+ * @Todo vibecode - LLM이 tool use로 과거 대화를 검색할 수 있는 스킬 생성
2465
+ */
2466
+ declare const createConversationSearchSkill: (options: ConversationSearchSkillOptions) => SkillConfig;
2467
+
2404
2468
  /**
2405
2469
  * @description Remix Icons wrapper component
2406
2470
  * @see https://remixicon.com/
@@ -3018,4 +3082,4 @@ declare const DEFAULT_PROJECT_TITLE = "\uAE30\uBCF8 \uD504\uB85C\uC81D\uD2B8";
3018
3082
  */
3019
3083
  declare const migrateSessionsToProjects: (storageKey: string) => void;
3020
3084
 
3021
- export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
3085
+ export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, type ConversationSearchOptions, type ConversationSearchSkillOptions, type ConversationSnippet, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createConversationSearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
@@ -1819,6 +1819,46 @@ interface UseFloatingWidgetReturn {
1819
1819
  toggle: () => void;
1820
1820
  setTab: (tabKey: string) => void;
1821
1821
  }
1822
+ /**
1823
+ * @description 대화 검색 결과 스니펫
1824
+ * @Todo vibecode - DB에서 반환되는 과거 대화 조각
1825
+ */
1826
+ interface ConversationSnippet {
1827
+ /** 세션 ID */
1828
+ sessionId: string;
1829
+ /** 세션 제목 */
1830
+ sessionTitle?: string;
1831
+ /** 발화자 */
1832
+ role: 'user' | 'assistant';
1833
+ /** 메시지 내용 (또는 발췌) */
1834
+ content: string;
1835
+ /** 메시지 타임스탬프 */
1836
+ timestamp?: number;
1837
+ /** 검색 관련도 점수 (0-1) */
1838
+ relevance?: number;
1839
+ }
1840
+ /**
1841
+ * @description 대화 검색 옵션
1842
+ * @Todo vibecode - 검색 범위 제한용
1843
+ */
1844
+ interface ConversationSearchOptions {
1845
+ /** 최대 결과 수 (기본: 5) */
1846
+ limit?: number;
1847
+ /** 특정 세션으로 제한 */
1848
+ sessionId?: string;
1849
+ }
1850
+ /**
1851
+ * @description createConversationSearchSkill 팩토리 옵션
1852
+ * @Todo vibecode - 소비 앱이 DB 검색 구현을 제공
1853
+ */
1854
+ interface ConversationSearchSkillOptions {
1855
+ /** 검색 구현 (호스트 DB 쿼리) */
1856
+ onSearch: (query: string, options?: ConversationSearchOptions) => Promise<ConversationSnippet[]>;
1857
+ /** 스킬 라벨 (기본: '대화 검색') */
1858
+ label?: string;
1859
+ /** 비활성화 여부 */
1860
+ disabled?: boolean;
1861
+ }
1822
1862
 
1823
1863
  /**
1824
1864
  * @description ChatUI 메인 컴포넌트
@@ -2401,6 +2441,30 @@ declare const createAdvancedResearchSkill: (options: AdvancedResearchOptions) =>
2401
2441
  */
2402
2442
  declare const createDeepResearchSkill: (callbacks: DeepResearchCallbacks) => SkillConfig;
2403
2443
 
2444
+ /**
2445
+ * @description 대화 검색 스킬 팩토리
2446
+ * @Todo vibecode - 호스트 DB 검색을 SkillConfig로 래핑하여 LLM tool use로 과거 대화 검색
2447
+ *
2448
+ * 사용 예시:
2449
+ * ```ts
2450
+ * import { createConversationSearchSkill } from '@gendive/chatllm/react';
2451
+ *
2452
+ * const searchSkill = createConversationSearchSkill({
2453
+ * onSearch: async (query, options) => {
2454
+ * return await db.conversations.search(query, options?.limit);
2455
+ * },
2456
+ * });
2457
+ *
2458
+ * <ChatUI skills={{ conversation_search: searchSkill }} />
2459
+ * ```
2460
+ */
2461
+
2462
+ /**
2463
+ * @description 대화 검색 스킬 생성 팩토리
2464
+ * @Todo vibecode - LLM이 tool use로 과거 대화를 검색할 수 있는 스킬 생성
2465
+ */
2466
+ declare const createConversationSearchSkill: (options: ConversationSearchSkillOptions) => SkillConfig;
2467
+
2404
2468
  /**
2405
2469
  * @description Remix Icons wrapper component
2406
2470
  * @see https://remixicon.com/
@@ -3018,4 +3082,4 @@ declare const DEFAULT_PROJECT_TITLE = "\uAE30\uBCF8 \uD504\uB85C\uC81D\uD2B8";
3018
3082
  */
3019
3083
  declare const migrateSessionsToProjects: (storageKey: string) => void;
3020
3084
 
3021
- export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
3085
+ export { type ActionItem, type ActionMenuProps, type AdvancedResearchOptions, type AlternativeResponse, ArtifactCard, type ArtifactCardProps, type ArtifactContentPart, type ChatAttachment, ChatFloatingWidget, type ChatFloatingWidgetProps, ChatHeader, ChatInput, type ChatMessage, type ChatProject, type ChatSession, ChatSidebar, type ChatToolDefinition, type ChatToolParameter, ChatUI, type ChatUIComponents, type ChatUIProps, type ChecklistBlock, ChecklistCard, type ChecklistCardProps, type ChecklistItem, ChecklistMiniIndicator, type ChecklistMiniIndicatorProps, ChecklistPanel, type ChecklistPanelProps, CompactChatView, type CompactChatViewProps, ContentPartRenderer, type ContentPartRendererProps, type ConversationSearchOptions, type ConversationSearchSkillOptions, type ConversationSnippet, DEFAULT_PROJECT_ID, DEFAULT_PROJECT_TITLE, type DeepResearchCallbacks, type DeepResearchProgress, DeepResearchProgressUI, DevDiveAvatar, type DevDiveAvatarProps, DevDiveFabCharacter, type DevDiveFabCharacterProps, EmptyState, type EmptyStateProps, type ErrorContentPart, FileContentCard, type FileContentCardProps, type FileContentPart, FloatingFab, type FloatingFabProps, type FloatingNotification, FloatingPanel, type FloatingPanelProps, type FloatingPosition, FloatingTabBar, type FloatingTabBarProps, type FloatingTabItem, type FloatingWidgetTab, type HeaderProps, Icon, type IconName, type IconProps, IconSvg, ImageContentCard, type ImageContentCardProps, type ImageContentPart, type InputProps, LinkChip, type LinkChipProps, type ManualSkillItem, MarkdownRenderer, type MarkdownRendererProps, type MemoryItem, MemoryPanel, type MemoryPanelProps, MessageBubble, type MessageBubbleProps, type MessageContentPart, MessageList, type MessageListProps, type ModelConfig, type ModelSelectorProps, type OpenAITool, type PatternAnalysisResult, type PersonalizationConfig, type PollBlock, PollCard, type PollCardProps, type PollOption, type PollQuestion, type PollResponse, type PollState, type ProjectFile, ProjectSelector, type ProjectSelectorProps, ProjectSettingsModal, type ProjectSettingsModalProps, type PromptTemplate, type ProviderType, type ResizeEdge, ResizeHandles, type ResizeHandlesProps, type ResponseStyle, type SearchResult, type SearchResultContentPart, type SendMessageParams, type SendMessageResponse, type SendMessageResponseBase, type SendMessageResponseWithHeaders, type SessionContext, type SessionContextItem, SettingsModal, type SettingsModalProps, type SettingsTab, type SidebarProps, type SkillConfig, type SkillExecuteCallbacks, type SkillExecution, type SkillExecutionResult, type SkillProgress, SkillProgressUI, type SkillProgressUIProps, type SkillTrigger, type SourceItem, type SubAgentProgress, type SuggestedPrompt, type SystemPromptControl, type TextContentPart, type ThemeConfig, type ThemeMode, type ToolCallAccumulator, type ToolCallResult, type ToolLoadingContentPart, type ToolResultContentPart, type UseChatUIOptions, type UseChatUIReturn, type UseDeepResearchOptions, type UseDragResizeOptions, type UseDragResizeReturn, type UseFloatingWidgetOptions, type UseFloatingWidgetReturn, type UseObserverOptions, type UseObserverReturn, type UseProjectOptions, type UseProjectReturn, type UseSkillsOptions, type UseSkillsReturn, type UserProfile, type WorkflowSuggestion, convertSkillsToOpenAITools, convertToolsToSkills, createAdvancedResearchSkill, createConversationSearchSkill, createDeepResearchSkill, migrateSessionsToProjects, useChatUI, useDeepResearch, useDragResize, useFloatingWidget, useObserver, useProject, useSkills };
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  convertSkillsToOpenAITools: () => convertSkillsToOpenAITools,
69
69
  convertToolsToSkills: () => convertToolsToSkills,
70
70
  createAdvancedResearchSkill: () => createAdvancedResearchSkill,
71
+ createConversationSearchSkill: () => createConversationSearchSkill,
71
72
  createDeepResearchSkill: () => createDeepResearchSkill,
72
73
  migrateSessionsToProjects: () => migrateSessionsToProjects,
73
74
  useChatUI: () => useChatUI,
@@ -2843,6 +2844,11 @@ ${projectMemoryContext}`);
2843
2844
  - \uB370\uC774\uD130 \uBE44\uAD50/\uCD94\uC774 \u2192 html (Chart.js CDN \uC0AC\uC6A9, <artifact> \uD0DC\uADF8)
2844
2845
  - \uC544\uC774\uCF58/\uB85C\uACE0/\uC77C\uB7EC\uC2A4\uD2B8 \u2192 svg
2845
2846
 
2847
+ **mermaid \uC8FC\uC758\uC0AC\uD56D:**
2848
+ - \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uAD04\uD638 () \uC0AC\uC6A9 \uAE08\uC9C0 \u2192 \uB300\uAD04\uD638 [] \uC0AC\uC6A9: B[\uC0C1\uD0DC \uAD00\uB9AC useState] \u2705 B[\uC0C1\uD0DC \uAD00\uB9AC (useState)] \u274C
2849
+ - \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uD2B9\uC218\uBB38\uC790\uAC00 \uC788\uC73C\uBA74 \uD070\uB530\uC634\uD45C\uB85C \uAC10\uC2F8\uAE30: B["API \uD638\uCD9C \u2192 \uC751\uB2F5"]
2850
+ - \uC18C\uAD04\uD638 ()\uB294 mermaid \uBB38\uBC95\uC5D0\uC11C \uB465\uADFC \uB178\uB4DC\uB97C \uC758\uBBF8\uD558\uBBC0\uB85C \uB77C\uBCA8 \uD14D\uC2A4\uD2B8\uC5D0 \uC808\uB300 \uD3EC\uD568\uD558\uC9C0 \uB9C8\uC138\uC694
2851
+
2846
2852
  **mermaid \uC608\uC2DC:**
2847
2853
  \`\`\`mermaid
2848
2854
  graph TD
@@ -2867,7 +2873,7 @@ new Chart(document.getElementById('chart'), {
2867
2873
  - html \uC2DC\uAC01\uD654\uB294 \uBC18\uB4DC\uC2DC <artifact language="html"> \uD0DC\uADF8\uB85C \uAC10\uC2F8\uC57C \uD568 (\`\`\`html \uCF54\uB4DC\uBE14\uB85D \uC0AC\uC6A9 \uAE08\uC9C0)
2868
2874
  - \uC0AC\uC6A9\uC790\uAC00 \uC2DC\uAC01\uD654\uB97C \uC694\uCCAD\uD558\uC9C0 \uC54A\uC544\uB3C4 \uC801\uC808\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uD3EC\uD568
2869
2875
  - \uC2DC\uAC01\uD654 \uC55E\uB4A4\uC5D0 \uAC04\uACB0\uD55C \uD14D\uC2A4\uD2B8 \uC124\uBA85 \uD3EC\uD568
2870
- - mermaid \uBB38\uBC95 \uC624\uB958 \uC8FC\uC758 (\uD2B9\uD788 \uAD04\uD638, \uB530\uC634\uD45C \uC774\uC2A4\uCF00\uC774\uD504)`);
2876
+ - mermaid \uB178\uB4DC \uB77C\uBCA8\uC5D0 \uC18C\uAD04\uD638 () \uC808\uB300 \uC0AC\uC6A9 \uAE08\uC9C0 \u2014 \uD30C\uC2F1 \uC5D0\uB7EC \uC6D0\uC778`);
2871
2877
  if (shouldIncludeSkills) {
2872
2878
  const skillsPrompt = buildSkillsPrompt();
2873
2879
  if (skillsPrompt) {
@@ -3200,18 +3206,29 @@ ${newConversation}
3200
3206
  setIsSessionLoading(true);
3201
3207
  try {
3202
3208
  const sessionDetail = await onLoadSessionRef.current(id);
3203
- let loadedMessages = sessionDetail.messages.map((m, idx) => ({
3204
- id: m.id || generateId5("msg"),
3205
- role: typeof m.role === "string" ? m.role.toLowerCase() : m.role,
3206
- // API는 message 필드, 내부는 content 필드 사용
3207
- content: m.content || m.message || "",
3208
- timestamp: m.created_at ? new Date(m.created_at).getTime() : Date.now() - (sessionDetail.messages.length - idx) * 1e3,
3209
- model: m.model,
3210
- alternatives: m.alternatives,
3211
- sources: m.sources,
3212
- /** @Todo vibecode - 백엔드에서 전달받은 contentParts 복원 (이미지/파일 등) */
3213
- ...m.contentParts && { contentParts: m.contentParts }
3214
- }));
3209
+ let loadedMessages = sessionDetail.messages.map((m, idx) => {
3210
+ const msg = {
3211
+ id: m.id || generateId5("msg"),
3212
+ role: typeof m.role === "string" ? m.role.toLowerCase() : m.role,
3213
+ // API는 message 필드, 내부는 content 필드 사용
3214
+ content: m.content || m.message || "",
3215
+ timestamp: m.created_at ? new Date(m.created_at).getTime() : Date.now() - (sessionDetail.messages.length - idx) * 1e3,
3216
+ model: m.model,
3217
+ alternatives: m.alternatives,
3218
+ sources: m.sources,
3219
+ /** @Todo vibecode - 백엔드에서 전달받은 contentParts 복원 (이미지/파일 등) */
3220
+ ...m.contentParts && { contentParts: m.contentParts }
3221
+ };
3222
+ if (!msg.contentParts && msg.role === "assistant" && hasArtifactTag(msg.content)) {
3223
+ const { artifacts, cleanContent } = parseArtifactsFromContent(msg.content);
3224
+ if (artifacts.length > 0) {
3225
+ const textPart = cleanContent.trim() ? [{ type: "text", content: cleanContent }] : [];
3226
+ msg.content = cleanContent;
3227
+ msg.contentParts = [...textPart, ...artifacts];
3228
+ }
3229
+ }
3230
+ return msg;
3231
+ });
3215
3232
  let resolvedTitle = sessionDetail.title;
3216
3233
  let resolvedSessionContext = sessionDetail.sessionContext;
3217
3234
  if (loadedMessages.length === 0) {
@@ -10285,9 +10302,39 @@ var MermaidArtifact = ({ code }) => {
10285
10302
  }
10286
10303
  );
10287
10304
  };
10288
- var ArtifactActions = ({ code, language }) => {
10305
+ var svgToPngBlob = (svgString, scale = 2) => {
10306
+ return new Promise((resolve) => {
10307
+ const parser = new DOMParser();
10308
+ const doc = parser.parseFromString(svgString, "image/svg+xml");
10309
+ const svgEl = doc.querySelector("svg");
10310
+ if (!svgEl) return resolve(null);
10311
+ const w = parseInt(svgEl.getAttribute("width") || "800", 10);
10312
+ const h = parseInt(svgEl.getAttribute("height") || "600", 10);
10313
+ const canvas = document.createElement("canvas");
10314
+ canvas.width = w * scale;
10315
+ canvas.height = h * scale;
10316
+ const ctx = canvas.getContext("2d");
10317
+ if (!ctx) return resolve(null);
10318
+ ctx.scale(scale, scale);
10319
+ const blob = new Blob([new XMLSerializer().serializeToString(svgEl)], { type: "image/svg+xml;charset=utf-8" });
10320
+ const url = URL.createObjectURL(blob);
10321
+ const img = new Image();
10322
+ img.onload = () => {
10323
+ ctx.drawImage(img, 0, 0, w, h);
10324
+ URL.revokeObjectURL(url);
10325
+ canvas.toBlob((pngBlob) => resolve(pngBlob), "image/png");
10326
+ };
10327
+ img.onerror = () => {
10328
+ URL.revokeObjectURL(url);
10329
+ resolve(null);
10330
+ };
10331
+ img.src = url;
10332
+ });
10333
+ };
10334
+ var ArtifactActions = ({ code, language, containerRef }) => {
10289
10335
  const [showCode, setShowCode] = (0, import_react17.useState)(false);
10290
10336
  const [copied, setCopied] = (0, import_react17.useState)(false);
10337
+ const [saving, setSaving] = (0, import_react17.useState)(false);
10291
10338
  const handleCopy = async () => {
10292
10339
  try {
10293
10340
  await navigator.clipboard.writeText(code);
@@ -10296,6 +10343,39 @@ var ArtifactActions = ({ code, language }) => {
10296
10343
  } catch {
10297
10344
  }
10298
10345
  };
10346
+ const handleSaveImage = async () => {
10347
+ setSaving(true);
10348
+ try {
10349
+ let blob = null;
10350
+ if (language === "svg" || language === "mermaid") {
10351
+ const svgEl = containerRef?.current?.querySelector("svg");
10352
+ if (svgEl) {
10353
+ blob = await svgToPngBlob(svgEl.outerHTML);
10354
+ }
10355
+ } else if (language === "html") {
10356
+ const htmlBlob = new Blob([code], { type: "text/html;charset=utf-8" });
10357
+ const url = URL.createObjectURL(htmlBlob);
10358
+ const a = document.createElement("a");
10359
+ a.href = url;
10360
+ a.download = `artifact.html`;
10361
+ a.click();
10362
+ URL.revokeObjectURL(url);
10363
+ setSaving(false);
10364
+ return;
10365
+ }
10366
+ if (blob) {
10367
+ const url = URL.createObjectURL(blob);
10368
+ const a = document.createElement("a");
10369
+ a.href = url;
10370
+ a.download = `artifact.png`;
10371
+ a.click();
10372
+ URL.revokeObjectURL(url);
10373
+ }
10374
+ } catch {
10375
+ } finally {
10376
+ setSaving(false);
10377
+ }
10378
+ };
10299
10379
  const langLabel = language === "mermaid" ? "Mermaid" : language === "svg" ? "SVG" : "HTML";
10300
10380
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
10301
10381
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
@@ -10361,6 +10441,30 @@ var ArtifactActions = ({ code, language }) => {
10361
10441
  copied ? "\uBCF5\uC0AC\uB428" : "\uBCF5\uC0AC"
10362
10442
  ]
10363
10443
  }
10444
+ ),
10445
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
10446
+ "button",
10447
+ {
10448
+ onClick: handleSaveImage,
10449
+ disabled: saving,
10450
+ style: {
10451
+ display: "flex",
10452
+ alignItems: "center",
10453
+ gap: "4px",
10454
+ padding: "4px 8px",
10455
+ fontSize: "12px",
10456
+ color: "var(--chatllm-text-muted, #64748b)",
10457
+ backgroundColor: "transparent",
10458
+ border: "none",
10459
+ borderRadius: "4px",
10460
+ cursor: saving ? "wait" : "pointer",
10461
+ opacity: saving ? 0.5 : 1
10462
+ },
10463
+ children: [
10464
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IconSvg, { name: "download-line", size: 12, color: "var(--chatllm-text-muted, #64748b)" }),
10465
+ language === "html" ? "HTML \uC800\uC7A5" : "\uC774\uBBF8\uC9C0 \uC800\uC7A5"
10466
+ ]
10467
+ }
10364
10468
  )
10365
10469
  ]
10366
10470
  }
@@ -10395,6 +10499,7 @@ var ArtifactActions = ({ code, language }) => {
10395
10499
  ] });
10396
10500
  };
10397
10501
  var ArtifactCard = ({ part, index = 0 }) => {
10502
+ const contentRef = (0, import_react17.useRef)(null);
10398
10503
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
10399
10504
  "div",
10400
10505
  {
@@ -10434,12 +10539,12 @@ var ArtifactCard = ({ part, index = 0 }) => {
10434
10539
  ]
10435
10540
  }
10436
10541
  ),
10437
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { padding: part.language === "html" ? 0 : "12px" }, children: [
10542
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { ref: contentRef, style: { padding: part.language === "html" ? 0 : "12px" }, children: [
10438
10543
  part.language === "html" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(HtmlArtifact, { code: part.code }),
10439
10544
  part.language === "svg" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SvgArtifact, { code: part.code }),
10440
10545
  part.language === "mermaid" && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MermaidArtifact, { code: part.code })
10441
10546
  ] }),
10442
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArtifactActions, { code: part.code, language: part.language })
10547
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ArtifactActions, { code: part.code, language: part.language, containerRef: contentRef })
10443
10548
  ]
10444
10549
  }
10445
10550
  );
@@ -15278,6 +15383,7 @@ var useDragResize = (options) => {
15278
15383
  const dragStartRef = (0, import_react24.useRef)(null);
15279
15384
  const isDraggingRef = (0, import_react24.useRef)(false);
15280
15385
  const wasDragRef = (0, import_react24.useRef)(false);
15386
+ const userDraggedRef = (0, import_react24.useRef)(false);
15281
15387
  const dragDeltaRef = (0, import_react24.useRef)({ dx: 0, dy: 0 });
15282
15388
  const fabElRef = (0, import_react24.useRef)(null);
15283
15389
  const dragCleanupRef = (0, import_react24.useRef)(null);
@@ -15344,6 +15450,7 @@ var useDragResize = (options) => {
15344
15450
  if (!isDraggingRef.current && distance > DRAG_THRESHOLD) {
15345
15451
  isDraggingRef.current = true;
15346
15452
  wasDragRef.current = true;
15453
+ userDraggedRef.current = true;
15347
15454
  setIsDragging(true);
15348
15455
  fabElRef.current?.classList.add("chatllm-fab-dragging");
15349
15456
  }
@@ -15583,7 +15690,11 @@ var useDragResize = (options) => {
15583
15690
  const newVw = window.innerWidth;
15584
15691
  const newVh = window.innerHeight;
15585
15692
  setViewport({ width: newVw, height: newVh });
15586
- setFabPos((prev) => clampToViewport(prev.x, prev.y, newVw, newVh));
15693
+ if (!userDraggedRef.current) {
15694
+ setFabPos(getInitialFabPosition(initialPosition));
15695
+ } else {
15696
+ setFabPos((prev) => clampToViewport(prev.x, prev.y, newVw, newVh));
15697
+ }
15587
15698
  };
15588
15699
  window.addEventListener("resize", handleResize);
15589
15700
  return () => window.removeEventListener("resize", handleResize);
@@ -17183,23 +17294,39 @@ var ChatFloatingWidget = ({
17183
17294
  return [chatTab, ...customTabs];
17184
17295
  }, [tabs]);
17185
17296
  const [unreadBadge, setUnreadBadge] = (0, import_react31.useState)(0);
17186
- const seenMessageIdsRef = (0, import_react31.useRef)(new Set(chatState.messages.map((m) => m.id)));
17297
+ const seenMessageIdsRef = (0, import_react31.useRef)(/* @__PURE__ */ new Set());
17187
17298
  (0, import_react31.useEffect)(() => {
17188
- if (!isOpen) {
17299
+ if (seenMessageIdsRef.current.size === 0 && chatState.messages.length > 0) {
17300
+ for (const m of chatState.messages) {
17301
+ seenMessageIdsRef.current.add(m.id);
17302
+ }
17303
+ }
17304
+ }, [chatState.messages]);
17305
+ (0, import_react31.useEffect)(() => {
17306
+ if (isOpen) {
17307
+ for (const m of chatState.messages) {
17308
+ seenMessageIdsRef.current.add(m.id);
17309
+ }
17310
+ } else {
17189
17311
  const newAssistant = chatState.messages.filter(
17190
17312
  (m) => m.role === "assistant" && !seenMessageIdsRef.current.has(m.id)
17191
17313
  );
17192
17314
  if (newAssistant.length > 0) {
17193
17315
  setUnreadBadge((prev) => prev + newAssistant.length);
17316
+ for (const m of newAssistant) {
17317
+ seenMessageIdsRef.current.add(m.id);
17318
+ }
17194
17319
  }
17195
17320
  }
17196
- for (const m of chatState.messages) {
17197
- seenMessageIdsRef.current.add(m.id);
17198
- }
17199
17321
  }, [chatState.messages, isOpen]);
17200
17322
  (0, import_react31.useEffect)(() => {
17201
- if (isOpen) setUnreadBadge(0);
17202
- }, [isOpen]);
17323
+ if (isOpen) {
17324
+ setUnreadBadge(0);
17325
+ for (const m of chatState.messages) {
17326
+ seenMessageIdsRef.current.add(m.id);
17327
+ }
17328
+ }
17329
+ }, [isOpen, chatState.messages]);
17203
17330
  const totalBadge = (0, import_react31.useMemo)(() => {
17204
17331
  return tabs.reduce((sum, t) => sum + (t.badge || 0), 0) + unreadBadge;
17205
17332
  }, [tabs, unreadBadge]);
@@ -17635,6 +17762,57 @@ var useDeepResearch = (options) => {
17635
17762
  };
17636
17763
  };
17637
17764
 
17765
+ // src/react/utils/conversationSearchAdapter.ts
17766
+ var formatSearchResults = (results) => {
17767
+ if (results.length === 0) {
17768
+ return "\uAD00\uB828 \uB300\uD654\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.";
17769
+ }
17770
+ const formatted = results.map((r, i) => {
17771
+ const header = r.sessionTitle ? `[${r.sessionTitle}]` : "";
17772
+ const time = r.timestamp ? ` (${new Date(r.timestamp).toLocaleDateString("ko-KR")})` : "";
17773
+ return `[${i + 1}] ${header}${time} (${r.role}): ${r.content}`;
17774
+ }).join("\n\n");
17775
+ return `\uAC80\uC0C9 \uACB0\uACFC (${results.length}\uAC74):
17776
+
17777
+ ${formatted}`;
17778
+ };
17779
+ var createConversationSearchSkill = (options) => {
17780
+ const { onSearch, label, disabled } = options;
17781
+ return {
17782
+ description: '\uC774\uC804 \uB300\uD654 \uB0B4\uC6A9\uC744 \uAC80\uC0C9\uD569\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC \uD638\uCD9C\uD574\uC57C \uD558\uB294 \uC0C1\uD669: \uC0AC\uC6A9\uC790\uAC00 "\uC804\uC5D0", "\uC9C0\uB09C\uBC88\uC5D0", "\uC608\uC804\uC5D0", "\uC544\uAE4C", "\uC774\uC804\uC5D0", "\uB2E4\uC2DC \uC54C\uB824\uC918", "\uB610", "\uADF8\uB54C" \uB4F1 \uACFC\uAC70 \uB300\uD654\uB97C \uCC38\uC870\uD558\uB294 \uD45C\uD604\uC744 \uC0AC\uC6A9\uD560 \uB54C. \uACFC\uAC70\uC5D0 \uB17C\uC758\uD588\uB358 \uC8FC\uC81C\uB97C \uB2E4\uC2DC \uBB3C\uC5B4\uBCFC \uB54C\uB3C4 \uD638\uCD9C\uD558\uC138\uC694.',
17783
+ trigger: "auto",
17784
+ label: label || "\uB300\uD654 \uAC80\uC0C9",
17785
+ icon: "search-line",
17786
+ disabled,
17787
+ persist: false,
17788
+ parameters: {
17789
+ type: "object",
17790
+ properties: {
17791
+ query: {
17792
+ type: "string",
17793
+ description: "\uAC80\uC0C9 \uD0A4\uC6CC\uB4DC \uB610\uB294 \uBB38\uC7A5 (\uC0AC\uC6A9\uC790 \uC758\uB3C4\uB97C \uBC18\uC601\uD55C \uAC80\uC0C9\uC5B4)"
17794
+ },
17795
+ limit: {
17796
+ type: "number",
17797
+ description: "\uCD5C\uB300 \uACB0\uACFC \uC218 (\uAE30\uBCF8: 5)"
17798
+ }
17799
+ },
17800
+ required: ["query"]
17801
+ },
17802
+ execute: async (params) => {
17803
+ const { query, limit } = params;
17804
+ if (!query) {
17805
+ return { content: "\uAC80\uC0C9\uC5B4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
17806
+ }
17807
+ const results = await onSearch(query, { limit: limit || 5 });
17808
+ return {
17809
+ content: formatSearchResults(results),
17810
+ metadata: { resultCount: results.length }
17811
+ };
17812
+ }
17813
+ };
17814
+ };
17815
+
17638
17816
  // src/react/components/EmptyState.tsx
17639
17817
  var import_jsx_runtime32 = require("react/jsx-runtime");
17640
17818
  var EmptyState = ({
@@ -18151,6 +18329,7 @@ var MemoryPanel = ({
18151
18329
  convertSkillsToOpenAITools,
18152
18330
  convertToolsToSkills,
18153
18331
  createAdvancedResearchSkill,
18332
+ createConversationSearchSkill,
18154
18333
  createDeepResearchSkill,
18155
18334
  migrateSessionsToProjects,
18156
18335
  useChatUI,