@sqlrooms/ai-core 0.27.0-rc.5 → 0.28.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisAnswer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAiB,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAQpD,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAChD,CAAC;AA6GF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,iDA6EzB,CAAC"}
1
+ {"version":3,"file":"AnalysisAnswer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAiB,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAKpD,KAAK,mBAAmB,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAChD,CAAC;AAgHF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,iDAoFzB,CAAC"}
@@ -1,16 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useState, useCallback, useMemo } from 'react';
2
+ import { cn, CopyButton } from '@sqlrooms/ui';
3
+ import { truncate } from '@sqlrooms/utils';
4
+ import { BrainIcon } from 'lucide-react';
5
+ import React, { useCallback, useMemo, useState } from 'react';
3
6
  import Markdown from 'react-markdown';
4
- import remarkGfm from 'remark-gfm';
5
7
  import rehypeRaw from 'rehype-raw';
6
- import { truncate } from '@sqlrooms/utils';
8
+ import remarkGfm from 'remark-gfm';
7
9
  import { MessageContainer } from './MessageContainer';
8
- import { BrainIcon } from 'lucide-react';
9
- import { cn } from '@sqlrooms/ui';
10
10
  // Constants moved outside component to prevent recreation
11
11
  const THINK_WORD_LIMIT = 10;
12
12
  const COMPLETE_THINK_REGEX = /<think>([\s\S]*?)<\/think>/g;
13
13
  const INCOMPLETE_THINK_REGEX = /<think>([\s\S]*)$/;
14
+ const sanitizeThinkTags = (value) => value.replace(COMPLETE_THINK_REGEX, '').replace(INCOMPLETE_THINK_REGEX, '');
14
15
  /**
15
16
  * Processes content and extracts think content in one pass
16
17
  */
@@ -64,6 +65,8 @@ ThinkBlock.displayName = 'ThinkBlock';
64
65
  export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
65
66
  const { content, isAnswer, customMarkdownComponents } = props;
66
67
  const [expandedThink, setExpandedThink] = useState(new Set());
68
+ const copyableText = useMemo(() => sanitizeThinkTags(content), [content]);
69
+ const hasTextContent = copyableText.trim().length > 0;
67
70
  const toggleThinkExpansion = useCallback((content) => {
68
71
  setExpandedThink((prev) => {
69
72
  const newExpanded = new Set(prev);
@@ -78,6 +81,7 @@ export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
78
81
  }, []);
79
82
  // Memoize content processing to avoid recalculation on every render
80
83
  const { processedContent, thinkContents } = useMemo(() => processContent(content), [content]);
84
+ const footerActions = hasTextContent ? (_jsx(CopyButton, { text: copyableText, tooltipLabel: isAnswer ? 'Copy response' : 'Copy message' })) : null;
81
85
  // Memoize the think-block component to prevent unnecessary re-renders
82
86
  const thinkBlockComponent = useCallback((thinkBlock) => {
83
87
  try {
@@ -95,9 +99,9 @@ export const AnalysisAnswer = React.memo(function AnalysisAnswer(props) {
95
99
  return null;
96
100
  }
97
101
  }, [thinkContents, expandedThink, toggleThinkExpansion]);
98
- return (_jsx("div", { className: "flex flex-col gap-5", children: _jsx(MessageContainer, { isSuccess: true, type: isAnswer ? 'answer' : 'thinking', content: { content, isAnswer }, children: _jsx("div", { className: "prose dark:prose-invert max-w-none text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw], components: {
99
- 'think-block': thinkBlockComponent,
100
- ...customMarkdownComponents,
101
- }, children: processedContent }) }) }) }));
102
+ return (_jsx(MessageContainer, { isSuccess: true, type: isAnswer ? 'answer' : 'thinking', content: { content, isAnswer }, footerActions: footerActions, children: _jsx("div", { className: "prose dark:prose-invert max-w-none text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeRaw], components: {
103
+ 'think-block': thinkBlockComponent,
104
+ ...customMarkdownComponents,
105
+ }, children: processedContent }) }) }));
102
106
  });
103
107
  //# sourceMappingURL=AnalysisAnswer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisAnswer.js","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useState, useCallback, useMemo} from 'react';\nimport Markdown, {Components} from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport rehypeRaw from 'rehype-raw';\nimport {truncate} from '@sqlrooms/utils';\nimport {MessageContainer} from './MessageContainer';\nimport {BrainIcon} from 'lucide-react';\nimport {cn} from '@sqlrooms/ui';\n\ntype AnalysisAnswerProps = {\n content: string;\n isAnswer: boolean;\n customMarkdownComponents?: Partial<Components>;\n};\n\ntype ThinkContent = {\n content: string;\n isComplete: boolean;\n index: number;\n};\n\n// Constants moved outside component to prevent recreation\nconst THINK_WORD_LIMIT = 10;\nconst COMPLETE_THINK_REGEX = /<think>([\\s\\S]*?)<\\/think>/g;\nconst INCOMPLETE_THINK_REGEX = /<think>([\\s\\S]*)$/;\n\n/**\n * Processes content and extracts think content in one pass\n */\nconst processContent = (\n originalContent: string,\n): {\n processedContent: string;\n thinkContents: ThinkContent[];\n} => {\n const thinkContents: ThinkContent[] = [];\n let processedContent = originalContent;\n let index = 0;\n\n // Replace complete think tags\n processedContent = processedContent.replace(\n COMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: true,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n // Replace incomplete think tags (no closing tag)\n processedContent = processedContent.replace(\n INCOMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: false,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n return {processedContent, thinkContents};\n};\n\n/**\n * ThinkBlock component for rendering individual think blocks\n */\nconst ThinkBlock = React.memo<{\n className?: string;\n thinkContent: ThinkContent;\n isExpanded: boolean;\n onToggleExpansion: (content: string) => void;\n}>(({thinkContent, isExpanded, onToggleExpansion, className}) => {\n const {content, isComplete, index} = thinkContent;\n\n const displayText =\n isComplete && !isExpanded ? truncate(content, THINK_WORD_LIMIT) : content;\n const needsTruncation =\n isComplete && content.split(' ').length > THINK_WORD_LIMIT;\n\n return (\n <span\n key={`think-${index}`}\n className={cn(\n 'inline-block rounded-lg px-3 py-2 text-xs font-normal text-gray-100 dark:text-gray-400',\n isExpanded && 'bg-gray-50 dark:bg-gray-800/50',\n className,\n )}\n >\n <span className=\"inline-flex items-start gap-2\">\n <span className=\"text-gray-400\">\n <BrainIcon\n className=\"mb-1 inline-block opacity-60 grayscale\"\n size={12}\n />{' '}\n {displayText}\n </span>\n </span>{' '}\n {needsTruncation && (\n <button\n onClick={() => onToggleExpansion(content)}\n className=\"text-xs text-gray-500 underline hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300\"\n >\n {isExpanded ? 'Show less' : 'Show more thinking'}\n </button>\n )}\n </span>\n );\n});\n\nThinkBlock.displayName = 'ThinkBlock';\n\n/**\n * Renders an analysis answer with markdown content of the final streaming response.\n * Supports streaming think content that may arrive in chunks (e.g., \"<think>Hello\" before \"</think>\").\n *\n * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.\n * @returns {JSX.Element} The rendered answer tool call\n */\nexport const AnalysisAnswer = React.memo(function AnalysisAnswer(\n props: AnalysisAnswerProps,\n) {\n const {content, isAnswer, customMarkdownComponents} = props;\n const [expandedThink, setExpandedThink] = useState<Set<string>>(new Set());\n\n const toggleThinkExpansion = useCallback((content: string) => {\n setExpandedThink((prev) => {\n const newExpanded = new Set(prev);\n if (newExpanded.has(content)) {\n newExpanded.delete(content);\n } else {\n newExpanded.add(content);\n }\n return newExpanded;\n });\n }, []);\n\n // Memoize content processing to avoid recalculation on every render\n const {processedContent, thinkContents} = useMemo(\n () => processContent(content),\n [content],\n );\n\n // Memoize the think-block component to prevent unnecessary re-renders\n const thinkBlockComponent = useCallback(\n (thinkBlock: any) => {\n try {\n const index = parseInt(thinkBlock.props?.['data-index'] || '0', 10);\n const thinkContent = thinkContents[index];\n\n if (!thinkContent) {\n console.warn(`Think content not found for index: ${index}`);\n return null;\n }\n\n const isExpanded = expandedThink.has(thinkContent.content);\n\n return (\n <ThinkBlock\n thinkContent={thinkContent}\n isExpanded={isExpanded}\n onToggleExpansion={toggleThinkExpansion}\n />\n );\n } catch (error) {\n console.error('Error rendering think block:', error);\n return null;\n }\n },\n [thinkContents, expandedThink, toggleThinkExpansion],\n );\n\n return (\n <div className=\"flex flex-col gap-5\">\n <MessageContainer\n isSuccess={true}\n type={isAnswer ? 'answer' : 'thinking'}\n content={{content, isAnswer}}\n >\n <div className=\"prose dark:prose-invert max-w-none text-sm\">\n <Markdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n components={\n {\n 'think-block': thinkBlockComponent,\n ...customMarkdownComponents,\n } as Partial<Components>\n }\n >\n {processedContent}\n </Markdown>\n </div>\n </MessageContainer>\n </div>\n );\n});\n"]}
1
+ {"version":3,"file":"AnalysisAnswer.js","sourceRoot":"","sources":["../../src/components/AnalysisAnswer.tsx"],"names":[],"mappings":"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","sourcesContent":["import {cn, CopyButton} from '@sqlrooms/ui';\nimport {truncate} from '@sqlrooms/utils';\nimport {BrainIcon} from 'lucide-react';\nimport React, {useCallback, useMemo, useState} from 'react';\nimport Markdown, {Components} from 'react-markdown';\nimport rehypeRaw from 'rehype-raw';\nimport remarkGfm from 'remark-gfm';\nimport {MessageContainer} from './MessageContainer';\n\ntype AnalysisAnswerProps = {\n content: string;\n isAnswer: boolean;\n customMarkdownComponents?: Partial<Components>;\n};\n\ntype ThinkContent = {\n content: string;\n isComplete: boolean;\n index: number;\n};\n\n// Constants moved outside component to prevent recreation\nconst THINK_WORD_LIMIT = 10;\nconst COMPLETE_THINK_REGEX = /<think>([\\s\\S]*?)<\\/think>/g;\nconst INCOMPLETE_THINK_REGEX = /<think>([\\s\\S]*)$/;\n\nconst sanitizeThinkTags = (value: string): string =>\n value.replace(COMPLETE_THINK_REGEX, '').replace(INCOMPLETE_THINK_REGEX, '');\n\n/**\n * Processes content and extracts think content in one pass\n */\nconst processContent = (\n originalContent: string,\n): {\n processedContent: string;\n thinkContents: ThinkContent[];\n} => {\n const thinkContents: ThinkContent[] = [];\n let processedContent = originalContent;\n let index = 0;\n\n // Replace complete think tags\n processedContent = processedContent.replace(\n COMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: true,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n // Replace incomplete think tags (no closing tag)\n processedContent = processedContent.replace(\n INCOMPLETE_THINK_REGEX,\n (match, content) => {\n if (content) {\n thinkContents.push({\n content: content.trim(),\n isComplete: false,\n index: index++,\n });\n return `\\n\\n<think-block data-index=\"${index - 1}\"></think-block>\\n\\n`;\n }\n return match;\n },\n );\n\n return {processedContent, thinkContents};\n};\n\n/**\n * ThinkBlock component for rendering individual think blocks\n */\nconst ThinkBlock = React.memo<{\n className?: string;\n thinkContent: ThinkContent;\n isExpanded: boolean;\n onToggleExpansion: (content: string) => void;\n}>(({thinkContent, isExpanded, onToggleExpansion, className}) => {\n const {content, isComplete, index} = thinkContent;\n\n const displayText =\n isComplete && !isExpanded ? truncate(content, THINK_WORD_LIMIT) : content;\n const needsTruncation =\n isComplete && content.split(' ').length > THINK_WORD_LIMIT;\n\n return (\n <span\n key={`think-${index}`}\n className={cn(\n 'inline-block rounded-lg px-3 py-2 text-xs font-normal text-gray-100 dark:text-gray-400',\n isExpanded && 'bg-gray-50 dark:bg-gray-800/50',\n className,\n )}\n >\n <span className=\"inline-flex items-start gap-2\">\n <span className=\"text-gray-400\">\n <BrainIcon\n className=\"mb-1 inline-block opacity-60 grayscale\"\n size={12}\n />{' '}\n {displayText}\n </span>\n </span>{' '}\n {needsTruncation && (\n <button\n onClick={() => onToggleExpansion(content)}\n className=\"text-xs text-gray-500 underline hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300\"\n >\n {isExpanded ? 'Show less' : 'Show more thinking'}\n </button>\n )}\n </span>\n );\n});\n\nThinkBlock.displayName = 'ThinkBlock';\n\n/**\n * Renders an analysis answer with markdown content of the final streaming response.\n * Supports streaming think content that may arrive in chunks (e.g., \"<think>Hello\" before \"</think>\").\n *\n * @param {AnalysisAnswerProps} props - The component props. See {@link AnalysisAnswerProps} for more details.\n * @returns {JSX.Element} The rendered answer tool call\n */\nexport const AnalysisAnswer = React.memo(function AnalysisAnswer(\n props: AnalysisAnswerProps,\n) {\n const {content, isAnswer, customMarkdownComponents} = props;\n const [expandedThink, setExpandedThink] = useState<Set<string>>(new Set());\n const copyableText = useMemo(() => sanitizeThinkTags(content), [content]);\n const hasTextContent = copyableText.trim().length > 0;\n\n const toggleThinkExpansion = useCallback((content: string) => {\n setExpandedThink((prev) => {\n const newExpanded = new Set(prev);\n if (newExpanded.has(content)) {\n newExpanded.delete(content);\n } else {\n newExpanded.add(content);\n }\n return newExpanded;\n });\n }, []);\n\n // Memoize content processing to avoid recalculation on every render\n const {processedContent, thinkContents} = useMemo(\n () => processContent(content),\n [content],\n );\n const footerActions = hasTextContent ? (\n <CopyButton\n text={copyableText}\n tooltipLabel={isAnswer ? 'Copy response' : 'Copy message'}\n />\n ) : null;\n\n // Memoize the think-block component to prevent unnecessary re-renders\n const thinkBlockComponent = useCallback(\n (thinkBlock: any) => {\n try {\n const index = parseInt(thinkBlock.props?.['data-index'] || '0', 10);\n const thinkContent = thinkContents[index];\n\n if (!thinkContent) {\n console.warn(`Think content not found for index: ${index}`);\n return null;\n }\n\n const isExpanded = expandedThink.has(thinkContent.content);\n\n return (\n <ThinkBlock\n thinkContent={thinkContent}\n isExpanded={isExpanded}\n onToggleExpansion={toggleThinkExpansion}\n />\n );\n } catch (error) {\n console.error('Error rendering think block:', error);\n return null;\n }\n },\n [thinkContents, expandedThink, toggleThinkExpansion],\n );\n\n return (\n <MessageContainer\n isSuccess={true}\n type={isAnswer ? 'answer' : 'thinking'}\n content={{content, isAnswer}}\n footerActions={footerActions}\n >\n <div className=\"prose dark:prose-invert max-w-none text-sm\">\n <Markdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeRaw]}\n components={\n {\n 'think-block': thinkBlockComponent,\n ...customMarkdownComponents,\n } as Partial<Components>\n }\n >\n {processedContent}\n </Markdown>\n </div>\n </MessageContainer>\n );\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResult.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAKzD,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAI1C,OAAO,EAAe,KAAK,0BAA0B,EAAC,MAAM,gBAAgB,CAAC;AAI7E;;;;;;GAMG;AACH,KAAK,mBAAmB,GAAG;IACzB,cAAc,EAAE,oBAAoB,CAAC;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;CACzE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiGxD,CAAC"}
1
+ {"version":3,"file":"AnalysisResult.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAKzD,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAI1C,OAAO,EAAe,KAAK,0BAA0B,EAAC,MAAM,gBAAgB,CAAC;AAI7E;;;;;;GAMG;AACH,KAAK,mBAAmB,GAAG;IACzB,cAAc,EAAE,oBAAoB,CAAC;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;CACzE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA8FxD,CAAC"}
@@ -45,7 +45,7 @@ export const AnalysisResult = ({ analysisResult, enableReasoningBox = false, cus
45
45
  }, []);
46
46
  // Group consecutive tool parts together for rendering in ReasoningBox (only if enabled)
47
47
  const groupedParts = useToolGrouping(uiMessageParts, divWidth, userTools, toolAdditionalData);
48
- return (_jsxs("div", { className: "group flex w-full flex-col gap-2 pb-2 text-sm", children: [_jsx("div", { className: "mb-2 flex items-center gap-2 rounded-md text-gray-700 dark:text-gray-100", children: _jsxs("div", { className: "bg-muted flex w-full items-center gap-2 rounded-md border p-2 text-sm", children: [_jsx(SquareTerminalIcon, { className: "h-4 w-4" }), _jsx("div", { className: "flex-1", children: analysisResult.prompt }), _jsx("div", { className: "flex gap-2 opacity-0 transition-opacity group-hover:opacity-100", children: _jsx(CopyButton, { text: analysisResult.prompt, variant: "ghost", size: "icon", className: "h-6 w-6", ariaLabel: "Copy prompt" }) })] }) }), _jsxs("div", { ref: divRef, className: "flex w-full flex-col gap-4", children: [enableReasoningBox ? (_jsx(GroupedMessageParts, { groupedParts: groupedParts, totalPartsCount: uiMessageParts.length, toolAdditionalData: toolAdditionalData, customMarkdownComponents: customMarkdownComponents })) : (_jsx(MessagePartsList, { parts: uiMessageParts, toolAdditionalData: toolAdditionalData, customMarkdownComponents: customMarkdownComponents })), analysisResult.errorMessage &&
48
+ return (_jsxs("div", { className: "group flex w-full flex-col gap-2 pb-2 text-sm", children: [_jsx("div", { className: "mb-2 flex items-center gap-2 rounded-md text-gray-700 dark:text-gray-100", children: _jsxs("div", { className: "group/prompt bg-muted flex w-full items-center gap-2 rounded-md border p-2 text-sm", children: [_jsx(SquareTerminalIcon, { className: "h-4 w-4" }), _jsx("div", { className: "flex-1", children: analysisResult.prompt }), _jsx("div", { className: "flex gap-2 opacity-0 transition-opacity group-focus-within/prompt:opacity-100 group-hover/prompt:opacity-100", children: _jsx(CopyButton, { text: analysisResult.prompt, tooltipLabel: "Copy message" }) })] }) }), _jsxs("div", { ref: divRef, className: "flex w-full flex-col gap-4", children: [enableReasoningBox ? (_jsx(GroupedMessageParts, { groupedParts: groupedParts, totalPartsCount: uiMessageParts.length, toolAdditionalData: toolAdditionalData, customMarkdownComponents: customMarkdownComponents })) : (_jsx(MessagePartsList, { parts: uiMessageParts, toolAdditionalData: toolAdditionalData, customMarkdownComponents: customMarkdownComponents })), analysisResult.errorMessage &&
49
49
  (ErrorMessageComponent ? (_jsx(ErrorMessageComponent, { errorMessage: analysisResult.errorMessage.error })) : (_jsx(ErrorMessage, { errorMessage: analysisResult.errorMessage.error })))] })] }));
50
50
  };
51
51
  //# sourceMappingURL=AnalysisResult.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResult.js","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"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","sourcesContent":["import {AnalysisResultSchema} from '@sqlrooms/ai-config';\nimport {CopyButton} from '@sqlrooms/ui';\nimport type {UIMessage} from 'ai';\nimport {SquareTerminalIcon} from 'lucide-react';\nimport {useEffect, useRef, useState} from 'react';\nimport {Components} from 'react-markdown';\nimport {useStoreWithAi} from '../AiSlice';\nimport {useAssistantMessageParts} from '../hooks/useAssistantMessageParts';\nimport {useToolGrouping} from '../hooks/useToolGrouping';\nimport {ErrorMessage, type ErrorMessageComponentProps} from './ErrorMessage';\nimport {GroupedMessageParts} from './GroupedMessageParts';\nimport {MessagePartsList} from './MessagePartsList';\n\n/**\n * Props for the AnalysisResult component\n * @property {AnalysisResultSchema} result - The result of the analysis containing prompt, tool calls, and analysis data\n * @property {boolean} enableReasoningBox - Whether to group consecutive tool parts into a collapsible ReasoningBox\n * @property {Partial<Components>} customMarkdownComponents - Optional custom components for markdown rendering\n * @property {string[]} userTools - Array of tool names that should not be grouped and must be rendered separately\n */\ntype AnalysisResultProps = {\n analysisResult: AnalysisResultSchema;\n enableReasoningBox?: boolean;\n customMarkdownComponents?: Partial<Components>;\n excludeFromGrouping?: string[];\n ErrorMessageComponent?: React.ComponentType<ErrorMessageComponentProps>;\n};\n\n/**\n * Component that displays the results of an AI analysis.\n * Shows the original prompt, intermediate tool calls, final analysis text,\n * and any tool results.\n *\n * @component\n * @param props - Component props\n * @param props.result - The analysis result data to display\n * @returns A React component displaying the analysis results\n */\nexport const AnalysisResult: React.FC<AnalysisResultProps> = ({\n analysisResult,\n enableReasoningBox = false,\n customMarkdownComponents,\n excludeFromGrouping: userTools,\n ErrorMessageComponent,\n}) => {\n const uiMessages = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.uiMessages as UIMessage[] | undefined,\n );\n const toolAdditionalData = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.toolAdditionalData || {},\n );\n const [divWidth, setDivWidth] = useState<number>(0);\n const divRef = useRef<HTMLDivElement>(null);\n\n const uiMessageParts = useAssistantMessageParts(\n uiMessages,\n analysisResult.id,\n );\n\n // Measure div width using ResizeObserver\n useEffect(() => {\n const element = divRef.current;\n if (!element) return;\n\n // Set initial width immediately\n setDivWidth(element.getBoundingClientRect().width);\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n // Use borderBoxSize if available (modern API), fallback to contentRect\n const width =\n entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;\n setDivWidth(width);\n }\n });\n\n resizeObserver.observe(element);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n // Group consecutive tool parts together for rendering in ReasoningBox (only if enabled)\n const groupedParts = useToolGrouping(\n uiMessageParts,\n divWidth,\n userTools,\n toolAdditionalData,\n );\n\n return (\n <div className=\"group flex w-full flex-col gap-2 pb-2 text-sm\">\n <div className=\"mb-2 flex items-center gap-2 rounded-md text-gray-700 dark:text-gray-100\">\n <div className=\"bg-muted flex w-full items-center gap-2 rounded-md border p-2 text-sm\">\n <SquareTerminalIcon className=\"h-4 w-4\" />\n {/** render prompt */}\n <div className=\"flex-1\">{analysisResult.prompt}</div>\n <div className=\"flex gap-2 opacity-0 transition-opacity group-hover:opacity-100\">\n <CopyButton\n text={analysisResult.prompt}\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-6 w-6\"\n ariaLabel=\"Copy prompt\"\n />\n </div>\n </div>\n </div>\n <div ref={divRef} className=\"flex w-full flex-col gap-4\">\n {enableReasoningBox ? (\n <GroupedMessageParts\n groupedParts={groupedParts}\n totalPartsCount={uiMessageParts.length}\n toolAdditionalData={toolAdditionalData}\n customMarkdownComponents={customMarkdownComponents}\n />\n ) : (\n <MessagePartsList\n parts={uiMessageParts}\n toolAdditionalData={toolAdditionalData}\n customMarkdownComponents={customMarkdownComponents}\n />\n )}\n {analysisResult.errorMessage &&\n (ErrorMessageComponent ? (\n <ErrorMessageComponent\n errorMessage={analysisResult.errorMessage.error}\n />\n ) : (\n <ErrorMessage errorMessage={analysisResult.errorMessage.error} />\n ))}\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"AnalysisResult.js","sourceRoot":"","sources":["../../src/components/AnalysisResult.tsx"],"names":[],"mappings":"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","sourcesContent":["import {AnalysisResultSchema} from '@sqlrooms/ai-config';\nimport {CopyButton} from '@sqlrooms/ui';\nimport type {UIMessage} from 'ai';\nimport {SquareTerminalIcon} from 'lucide-react';\nimport {useEffect, useRef, useState} from 'react';\nimport {Components} from 'react-markdown';\nimport {useStoreWithAi} from '../AiSlice';\nimport {useAssistantMessageParts} from '../hooks/useAssistantMessageParts';\nimport {useToolGrouping} from '../hooks/useToolGrouping';\nimport {ErrorMessage, type ErrorMessageComponentProps} from './ErrorMessage';\nimport {GroupedMessageParts} from './GroupedMessageParts';\nimport {MessagePartsList} from './MessagePartsList';\n\n/**\n * Props for the AnalysisResult component\n * @property {AnalysisResultSchema} result - The result of the analysis containing prompt, tool calls, and analysis data\n * @property {boolean} enableReasoningBox - Whether to group consecutive tool parts into a collapsible ReasoningBox\n * @property {Partial<Components>} customMarkdownComponents - Optional custom components for markdown rendering\n * @property {string[]} userTools - Array of tool names that should not be grouped and must be rendered separately\n */\ntype AnalysisResultProps = {\n analysisResult: AnalysisResultSchema;\n enableReasoningBox?: boolean;\n customMarkdownComponents?: Partial<Components>;\n excludeFromGrouping?: string[];\n ErrorMessageComponent?: React.ComponentType<ErrorMessageComponentProps>;\n};\n\n/**\n * Component that displays the results of an AI analysis.\n * Shows the original prompt, intermediate tool calls, final analysis text,\n * and any tool results.\n *\n * @component\n * @param props - Component props\n * @param props.result - The analysis result data to display\n * @returns A React component displaying the analysis results\n */\nexport const AnalysisResult: React.FC<AnalysisResultProps> = ({\n analysisResult,\n enableReasoningBox = false,\n customMarkdownComponents,\n excludeFromGrouping: userTools,\n ErrorMessageComponent,\n}) => {\n const uiMessages = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.uiMessages as UIMessage[] | undefined,\n );\n const toolAdditionalData = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.toolAdditionalData || {},\n );\n const [divWidth, setDivWidth] = useState<number>(0);\n const divRef = useRef<HTMLDivElement>(null);\n\n const uiMessageParts = useAssistantMessageParts(\n uiMessages,\n analysisResult.id,\n );\n\n // Measure div width using ResizeObserver\n useEffect(() => {\n const element = divRef.current;\n if (!element) return;\n\n // Set initial width immediately\n setDivWidth(element.getBoundingClientRect().width);\n\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n // Use borderBoxSize if available (modern API), fallback to contentRect\n const width =\n entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;\n setDivWidth(width);\n }\n });\n\n resizeObserver.observe(element);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, []);\n\n // Group consecutive tool parts together for rendering in ReasoningBox (only if enabled)\n const groupedParts = useToolGrouping(\n uiMessageParts,\n divWidth,\n userTools,\n toolAdditionalData,\n );\n\n return (\n <div className=\"group flex w-full flex-col gap-2 pb-2 text-sm\">\n <div className=\"mb-2 flex items-center gap-2 rounded-md text-gray-700 dark:text-gray-100\">\n <div className=\"group/prompt bg-muted flex w-full items-center gap-2 rounded-md border p-2 text-sm\">\n <SquareTerminalIcon className=\"h-4 w-4\" />\n {/** render prompt */}\n <div className=\"flex-1\">{analysisResult.prompt}</div>\n <div className=\"flex gap-2 opacity-0 transition-opacity group-focus-within/prompt:opacity-100 group-hover/prompt:opacity-100\">\n <CopyButton\n text={analysisResult.prompt}\n tooltipLabel=\"Copy message\"\n />\n </div>\n </div>\n </div>\n <div ref={divRef} className=\"flex w-full flex-col gap-4\">\n {enableReasoningBox ? (\n <GroupedMessageParts\n groupedParts={groupedParts}\n totalPartsCount={uiMessageParts.length}\n toolAdditionalData={toolAdditionalData}\n customMarkdownComponents={customMarkdownComponents}\n />\n ) : (\n <MessagePartsList\n parts={uiMessageParts}\n toolAdditionalData={toolAdditionalData}\n customMarkdownComponents={customMarkdownComponents}\n />\n )}\n {analysisResult.errorMessage &&\n (ErrorMessageComponent ? (\n <ErrorMessageComponent\n errorMessage={analysisResult.errorMessage.error}\n />\n ) : (\n <ErrorMessage errorMessage={analysisResult.errorMessage.error} />\n ))}\n </div>\n </div>\n );\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResultsContainer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResultsContainer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAK1C,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,gBAAgB,CAAC;AAE/D,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;CACzE,CAyEA,CAAC"}
1
+ {"version":3,"file":"AnalysisResultsContainer.d.ts","sourceRoot":"","sources":["../../src/components/AnalysisResultsContainer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAK1C,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,gBAAgB,CAAC;AAE/D,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC;CACzE,CAuEA,CAAC"}
@@ -25,6 +25,6 @@ export const AnalysisResultsContainer = ({ className, enableReasoningBox = false
25
25
  scrollToBottom();
26
26
  }
27
27
  }, [isRunning, scrollToBottom]);
28
- return (_jsxs("div", { className: cn('relative flex h-full w-full flex-col', className), children: [_jsxs(ScrollArea, { viewportRef: containerRef, className: "flex w-full flex-grow flex-col gap-5", children: [currentAnalysisResults?.map((analysisResult) => (_jsx(AnalysisResult, { analysisResult: analysisResult, enableReasoningBox: enableReasoningBox, customMarkdownComponents: customMarkdownComponents, excludeFromGrouping: excludeFromGrouping, ErrorMessageComponent: ErrorMessageComponent }, analysisResult.id))), isRunning && (_jsx(AiThinkingDots, { className: "text-muted-foreground p-4" })), _jsx("div", { ref: endRef, className: "h-10 w-full shrink-0" }), _jsx(ScrollBar, { orientation: "vertical" }), _jsx(ScrollBar, { orientation: "horizontal" })] }), _jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 flex justify-center", children: _jsx("button", { onClick: scrollToBottom, className: cn('bg-primary hover:bg-primary/90 text-primary-foreground pointer-events-auto z-50', 'mb-6 translate-y-4 rounded-full p-2 opacity-0 shadow-md transition-all duration-200', showScrollButton && 'translate-y-0 opacity-100'), "aria-label": "Scroll to bottom", children: _jsx(ChevronDown, { className: "h-5 w-5" }) }) })] }));
28
+ return (_jsxs("div", { className: cn('relative flex h-full w-full flex-col', className), children: [_jsxs(ScrollArea, { viewportRef: containerRef, className: "flex w-full grow flex-col gap-5", children: [currentAnalysisResults?.map((analysisResult) => (_jsx(AnalysisResult, { analysisResult: analysisResult, enableReasoningBox: enableReasoningBox, customMarkdownComponents: customMarkdownComponents, excludeFromGrouping: excludeFromGrouping, ErrorMessageComponent: ErrorMessageComponent }, analysisResult.id))), isRunning && _jsx(AiThinkingDots, { className: "text-muted-foreground p-4" }), _jsx("div", { ref: endRef, className: "h-10 w-full shrink-0" }), _jsx(ScrollBar, { orientation: "vertical" }), _jsx(ScrollBar, { orientation: "horizontal" })] }), _jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 flex justify-center", children: _jsx("button", { onClick: scrollToBottom, className: cn('bg-primary hover:bg-primary/90 text-primary-foreground pointer-events-auto z-50', 'mb-6 translate-y-4 rounded-full p-2 opacity-0 shadow-md transition-all duration-200', showScrollButton && 'translate-y-0 opacity-100'), "aria-label": "Scroll to bottom", children: _jsx(ChevronDown, { className: "h-5 w-5" }) }) })] }));
29
29
  };
30
30
  //# sourceMappingURL=AnalysisResultsContainer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalysisResultsContainer.js","sourceRoot":"","sources":["../../src/components/AnalysisResultsContainer.tsx"],"names":[],"mappings":"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","sourcesContent":["import {cn, ScrollArea, ScrollBar} from '@sqlrooms/ui';\nimport {ChevronDown} from 'lucide-react';\nimport React, {useEffect, useRef} from 'react';\nimport {Components} from 'react-markdown';\nimport {useStoreWithAi} from '../AiSlice';\nimport {useScrollToBottom} from '../hooks/useScrollToBottom';\nimport {AnalysisResult} from './AnalysisResult';\nimport {AiThinkingDots} from './AiThinkingDots';\nimport type {ErrorMessageComponentProps} from './ErrorMessage';\n\nexport const AnalysisResultsContainer: React.FC<{\n className?: string;\n enableReasoningBox?: boolean;\n customMarkdownComponents?: Partial<Components>;\n excludeFromGrouping?: string[];\n ErrorMessageComponent?: React.ComponentType<ErrorMessageComponentProps>;\n}> = ({\n className,\n enableReasoningBox = false,\n customMarkdownComponents,\n excludeFromGrouping: excludeFromGrouping,\n ErrorMessageComponent,\n}) => {\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const isRunning = useStoreWithAi((s) =>\n sessionId ? s.ai.getIsRunning(sessionId) : false,\n );\n const currentAnalysisResults = useStoreWithAi((s) =>\n s.ai.getAnalysisResults(),\n );\n const uiMessages = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.uiMessages,\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const endRef = useRef<HTMLDivElement>(null);\n const {showScrollButton, scrollToBottom} = useScrollToBottom({\n containerRef,\n endRef,\n dataToObserve: uiMessages,\n });\n\n // Scroll to bottom when analysis starts\n useEffect(() => {\n if (isRunning) {\n scrollToBottom();\n }\n }, [isRunning, scrollToBottom]);\n\n return (\n <div className={cn('relative flex h-full w-full flex-col', className)}>\n <ScrollArea\n viewportRef={containerRef}\n className=\"flex w-full flex-grow flex-col gap-5\"\n >\n {/* Render analysis results */}\n {currentAnalysisResults?.map((analysisResult) => (\n <AnalysisResult\n key={analysisResult.id}\n analysisResult={analysisResult}\n enableReasoningBox={enableReasoningBox}\n customMarkdownComponents={customMarkdownComponents}\n excludeFromGrouping={excludeFromGrouping}\n ErrorMessageComponent={ErrorMessageComponent}\n />\n ))}\n {isRunning && (\n <AiThinkingDots className=\"text-muted-foreground p-4\" />\n )}\n <div ref={endRef} className=\"h-10 w-full shrink-0\" />\n <ScrollBar orientation=\"vertical\" />\n <ScrollBar orientation=\"horizontal\" />\n </ScrollArea>\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex justify-center\">\n <button\n onClick={scrollToBottom}\n className={cn(\n 'bg-primary hover:bg-primary/90 text-primary-foreground pointer-events-auto z-50',\n 'mb-6 translate-y-4 rounded-full p-2 opacity-0 shadow-md transition-all duration-200',\n showScrollButton && 'translate-y-0 opacity-100',\n )}\n aria-label=\"Scroll to bottom\"\n >\n <ChevronDown className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"AnalysisResultsContainer.js","sourceRoot":"","sources":["../../src/components/AnalysisResultsContainer.tsx"],"names":[],"mappings":"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","sourcesContent":["import {cn, ScrollArea, ScrollBar} from '@sqlrooms/ui';\nimport {ChevronDown} from 'lucide-react';\nimport React, {useEffect, useRef} from 'react';\nimport {Components} from 'react-markdown';\nimport {useStoreWithAi} from '../AiSlice';\nimport {useScrollToBottom} from '../hooks/useScrollToBottom';\nimport {AnalysisResult} from './AnalysisResult';\nimport {AiThinkingDots} from './AiThinkingDots';\nimport type {ErrorMessageComponentProps} from './ErrorMessage';\n\nexport const AnalysisResultsContainer: React.FC<{\n className?: string;\n enableReasoningBox?: boolean;\n customMarkdownComponents?: Partial<Components>;\n excludeFromGrouping?: string[];\n ErrorMessageComponent?: React.ComponentType<ErrorMessageComponentProps>;\n}> = ({\n className,\n enableReasoningBox = false,\n customMarkdownComponents,\n excludeFromGrouping: excludeFromGrouping,\n ErrorMessageComponent,\n}) => {\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const isRunning = useStoreWithAi((s) =>\n sessionId ? s.ai.getIsRunning(sessionId) : false,\n );\n const currentAnalysisResults = useStoreWithAi((s) =>\n s.ai.getAnalysisResults(),\n );\n const uiMessages = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.uiMessages,\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const endRef = useRef<HTMLDivElement>(null);\n const {showScrollButton, scrollToBottom} = useScrollToBottom({\n containerRef,\n endRef,\n dataToObserve: uiMessages,\n });\n\n // Scroll to bottom when analysis starts\n useEffect(() => {\n if (isRunning) {\n scrollToBottom();\n }\n }, [isRunning, scrollToBottom]);\n\n return (\n <div className={cn('relative flex h-full w-full flex-col', className)}>\n <ScrollArea\n viewportRef={containerRef}\n className=\"flex w-full grow flex-col gap-5\"\n >\n {/* Render analysis results */}\n {currentAnalysisResults?.map((analysisResult) => (\n <AnalysisResult\n key={analysisResult.id}\n analysisResult={analysisResult}\n enableReasoningBox={enableReasoningBox}\n customMarkdownComponents={customMarkdownComponents}\n excludeFromGrouping={excludeFromGrouping}\n ErrorMessageComponent={ErrorMessageComponent}\n />\n ))}\n {isRunning && <AiThinkingDots className=\"text-muted-foreground p-4\" />}\n <div ref={endRef} className=\"h-10 w-full shrink-0\" />\n <ScrollBar orientation=\"vertical\" />\n <ScrollBar orientation=\"horizontal\" />\n </ScrollArea>\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex justify-center\">\n <button\n onClick={scrollToBottom}\n className={cn(\n 'bg-primary hover:bg-primary/90 text-primary-foreground pointer-events-auto z-50',\n 'mb-6 translate-y-4 rounded-full p-2 opacity-0 shadow-md transition-all duration-200',\n showScrollButton && 'translate-y-0 opacity-100',\n )}\n aria-label=\"Scroll to bottom\"\n >\n <ChevronDown className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n );\n};\n"]}
@@ -4,6 +4,8 @@ type MessageContainerProps = {
4
4
  type: string;
5
5
  content: object;
6
6
  children: React.ReactNode;
7
+ footerActions?: React.ReactNode;
8
+ footerActionsClassName?: string;
7
9
  };
8
10
  export declare const MessageContainer: React.FC<MessageContainerProps>;
9
11
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"MessageContainer.d.ts","sourceRoot":"","sources":["../../src/components/MessageContainer.tsx"],"names":[],"mappings":"AAWA,KAAK,qBAAqB,GAAG;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IAEnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsC5D,CAAC"}
1
+ {"version":3,"file":"MessageContainer.d.ts","sourceRoot":"","sources":["../../src/components/MessageContainer.tsx"],"names":[],"mappings":"AAGA,KAAK,qBAAqB,GAAG;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IAEnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAkD5D,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Badge, cn, } from '@sqlrooms/ui';
2
+ import { Badge, cn } from '@sqlrooms/ui';
3
3
  import { XCircleIcon } from 'lucide-react';
4
4
  export const MessageContainer = ({ className, type,
5
5
  // borderColor,
6
- content, children, }) => {
7
- return (_jsxs("div", { className: cn('group relative px-5 py-2 text-xs', className, type === 'error' && 'border-destructive rounded-md border py-4'), children: [type === 'error' && (_jsxs(Badge, { variant: "secondary", className: cn('absolute top-[-12px] left-2 flex items-center gap-1 border text-xs', 'border-destructive bg-background'), children: [_jsx(XCircleIcon, { className: "h-3 w-3 text-red-500" }), type ? type.replace(/^\w/, (c) => c.toUpperCase()) : ''] })), _jsx("div", { className: "flex flex-col gap-5", children: children })] }));
6
+ content, children, footerActions, footerActionsClassName, }) => {
7
+ return (_jsxs("div", { className: cn('group relative px-5 py-2 text-xs', className, type === 'error' && 'border-destructive rounded-md border py-4'), children: [type === 'error' && (_jsxs(Badge, { variant: "secondary", className: cn('absolute left-2 top-[-12px] flex items-center gap-1 border text-xs', 'border-destructive bg-background'), children: [_jsx(XCircleIcon, { className: "h-3 w-3 text-red-500" }), type ? type.replace(/^\w/, (c) => c.toUpperCase()) : ''] })), _jsx("div", { className: "flex flex-col gap-5", children: children }), footerActions && (_jsx("div", { className: cn('mt-2 flex items-center justify-end gap-1 pt-1 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100', footerActionsClassName), children: footerActions }))] }));
8
8
  };
9
9
  //# sourceMappingURL=MessageContainer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageContainer.js","sourceRoot":"","sources":["../../src/components/MessageContainer.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,KAAK,EAEL,EAAE,GAIH,MAAM,cAAc,CAAC;AACtB,OAAO,EAAW,WAAW,EAAC,MAAM,cAAc,CAAC;AAWnD,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,SAAS,EACT,IAAI;AACJ,eAAe;AACf,OAAO,EACP,QAAQ,GACT,EAAE,EAAE;IACH,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,kCAAkC,EAClC,SAAS,EACT,IAAI,KAAK,OAAO,IAAI,2CAA2C,CAGhE,aAEA,IAAI,KAAK,OAAO,IAAI,CACnB,MAAC,KAAK,IACJ,OAAO,EAAC,WAAW,EACnB,SAAS,EAAE,EAAE,CACX,oEAAoE,EACpE,kCAAkC,CAEnC,aAKD,KAAC,WAAW,IAAC,SAAS,EAAC,sBAAsB,GAAG,EAE/C,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAClD,CACT,EAED,cAAK,SAAS,EAAC,qBAAqB,YAAE,QAAQ,GAAO,IACjD,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {JsonMonacoEditor} from '@sqlrooms/monaco-editor';\nimport {\n Badge,\n Button,\n cn,\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@sqlrooms/ui';\nimport {CodeIcon, XCircleIcon} from 'lucide-react';\n\ntype MessageContainerProps = {\n className?: string;\n isSuccess: boolean;\n // borderColor: string;\n type: string;\n content: object;\n children: React.ReactNode;\n};\n\nexport const MessageContainer: React.FC<MessageContainerProps> = ({\n className,\n type,\n // borderColor,\n content,\n children,\n}) => {\n return (\n <div\n className={cn(\n 'group relative px-5 py-2 text-xs',\n className,\n type === 'error' && 'border-destructive rounded-md border py-4',\n // borderColor,\n // isSuccess ? borderColor : 'border-red-500',\n )}\n >\n {type === 'error' && (\n <Badge\n variant=\"secondary\"\n className={cn(\n 'absolute top-[-12px] left-2 flex items-center gap-1 border text-xs',\n 'border-destructive bg-background',\n // isSuccess ? borderColor : 'border-red-500',\n )}\n >\n {/* {isSuccess ? ( */}\n {/* <CheckCircle2Icon className=\"h-3 w-3 text-green-500\" /> */}\n {/* ) : ( */}\n <XCircleIcon className=\"h-3 w-3 text-red-500\" />\n {/* )} */}\n {type ? type.replace(/^\\w/, (c) => c.toUpperCase()) : ''}\n </Badge>\n )}\n\n <div className=\"flex flex-col gap-5\">{children}</div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"MessageContainer.js","sourceRoot":"","sources":["../../src/components/MessageContainer.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,KAAK,EAAE,EAAE,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAazC,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,SAAS,EACT,IAAI;AACJ,eAAe;AACf,OAAO,EACP,QAAQ,EACR,aAAa,EACb,sBAAsB,GACvB,EAAE,EAAE;IACH,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,kCAAkC,EAClC,SAAS,EACT,IAAI,KAAK,OAAO,IAAI,2CAA2C,CAGhE,aAEA,IAAI,KAAK,OAAO,IAAI,CACnB,MAAC,KAAK,IACJ,OAAO,EAAC,WAAW,EACnB,SAAS,EAAE,EAAE,CACX,oEAAoE,EACpE,kCAAkC,CAEnC,aAKD,KAAC,WAAW,IAAC,SAAS,EAAC,sBAAsB,GAAG,EAE/C,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAClD,CACT,EAED,cAAK,SAAS,EAAC,qBAAqB,YAAE,QAAQ,GAAO,EACpD,aAAa,IAAI,CAChB,cACE,SAAS,EAAE,EAAE,CACX,mIAAmI,EACnI,sBAAsB,CACvB,YAEA,aAAa,GACV,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {Badge, cn} from '@sqlrooms/ui';\nimport {XCircleIcon} from 'lucide-react';\n\ntype MessageContainerProps = {\n className?: string;\n isSuccess: boolean;\n // borderColor: string;\n type: string;\n content: object;\n children: React.ReactNode;\n footerActions?: React.ReactNode;\n footerActionsClassName?: string;\n};\n\nexport const MessageContainer: React.FC<MessageContainerProps> = ({\n className,\n type,\n // borderColor,\n content,\n children,\n footerActions,\n footerActionsClassName,\n}) => {\n return (\n <div\n className={cn(\n 'group relative px-5 py-2 text-xs',\n className,\n type === 'error' && 'border-destructive rounded-md border py-4',\n // borderColor,\n // isSuccess ? borderColor : 'border-red-500',\n )}\n >\n {type === 'error' && (\n <Badge\n variant=\"secondary\"\n className={cn(\n 'absolute left-2 top-[-12px] flex items-center gap-1 border text-xs',\n 'border-destructive bg-background',\n // isSuccess ? borderColor : 'border-red-500',\n )}\n >\n {/* {isSuccess ? ( */}\n {/* <CheckCircle2Icon className=\"h-3 w-3 text-green-500\" /> */}\n {/* ) : ( */}\n <XCircleIcon className=\"h-3 w-3 text-red-500\" />\n {/* )} */}\n {type ? type.replace(/^\\w/, (c) => c.toUpperCase()) : ''}\n </Badge>\n )}\n\n <div className=\"flex flex-col gap-5\">{children}</div>\n {footerActions && (\n <div\n className={cn(\n 'mt-2 flex items-center justify-end gap-1 pt-1 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100',\n footerActionsClassName,\n )}\n >\n {footerActions}\n </div>\n )}\n </div>\n );\n};\n"]}
@@ -19,7 +19,7 @@ const Container = ({ isLoading = false, className, children, }) => {
19
19
  }
20
20
  return (_jsx(TooltipProvider, { children: _jsx("div", { className: cn('w-full py-1', className), children: _jsxs("div", { className: "flex h-full w-full gap-2", children: [_jsx(ScrollableRow, { className: "min-w-0 flex-1", scrollClassName: "flex flex-1 snap-x snap-mandatory scroll-pl-7 scroll-pr-7 gap-2 overflow-x-auto overflow-y-hidden px-1 py-1 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", arrowVisibility: "always", arrowIconClassName: "h-4 w-4 opacity-80", children: isLoading
21
21
  ? // Show placeholder buttons with spinners while loading
22
- Array.from({ length: 3 }).map((_, index) => (_jsx("div", { className: "shrink-0 snap-start", children: _jsx(Button, { disabled: true, className: cn('bg-muted/50 border-border border', 'rounded-lg', 'text-muted-foreground text-xs', 'relative', 'flex items-center justify-center', 'px-4 py-2', 'h-18 max-h-18 min-h-18 w-48 min-w-48 max-w-48'), type: "button", children: _jsx(Spinner, { className: "text-muted-foreground h-3.5 w-3.5" }) }) }, index)))
22
+ Array.from({ length: 3 }).map((_, index) => (_jsx("div", { className: "shrink-0 snap-start", children: _jsx(Button, { disabled: true, className: cn('bg-muted/50 border-border border', 'rounded-lg', 'text-muted-foreground text-xs', 'relative', 'flex items-center justify-center', 'px-4 py-2', 'h-18 max-h-18 min-h-18 w-48 max-w-48 min-w-48'), type: "button", children: _jsx(Spinner, { className: "text-muted-foreground h-3.5 w-3.5" }) }) }, index)))
23
23
  : children }), _jsx("div", { className: "flex shrink-0 items-center pr-1", children: _jsx(Button, { onClick: toggleVisibility, variant: "ghost", size: "icon", className: "text-muted-foreground hover:text-foreground h-6 w-6 shrink-0", title: "Hide prompt suggestions", children: _jsx(X, { className: "h-4 w-4" }) }) })] }) }) }));
24
24
  };
25
25
  /**
@@ -41,7 +41,7 @@ const Item = ({ text, className, icon, }) => {
41
41
  inline: 'nearest',
42
42
  });
43
43
  }, [text, setPrompt, currentSession]);
44
- return (_jsx("div", { className: "shrink-0 snap-start", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button, { ref: buttonRef, onClick: handleClick, className: cn('bg-muted/50 hover:bg-muted hover:shadow-lg', 'border-border hover:border-primary/50 border', 'rounded-lg', 'text-muted-foreground hover:text-foreground text-xs', 'transition-all duration-200 ease-in-out', 'hover:-translate-y-0.5 hover:scale-[1.02]', 'cursor-pointer', 'relative', 'flex items-start justify-start', 'text-left', 'overflow-hidden', 'py-2 pl-8 pr-4', 'h-18 max-h-18 min-h-18 w-48 min-w-48 max-w-48', className), type: "button", title: text, children: [_jsx("span", { className: "absolute left-2 top-3 opacity-60", children: icon ?? _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "line-clamp-2 text-wrap break-words", children: truncate(text, 40) })] }) }), _jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs", children: text }) })] }) }));
44
+ return (_jsx("div", { className: "shrink-0 snap-start", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button, { ref: buttonRef, onClick: handleClick, className: cn('bg-muted/50 hover:bg-muted hover:shadow-lg', 'border-border hover:border-primary/50 border', 'rounded-lg', 'text-muted-foreground hover:text-foreground text-xs', 'transition-all duration-200 ease-in-out', 'hover:-translate-y-0.5 hover:scale-[1.02]', 'cursor-pointer', 'relative', 'flex items-start justify-start', 'text-left', 'overflow-hidden', 'py-2 pr-4 pl-8', 'h-18 max-h-18 min-h-18 w-48 max-w-48 min-w-48', className), type: "button", title: text, children: [_jsx("span", { className: "absolute top-3 left-2 opacity-60", children: icon ?? _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "line-clamp-2 text-wrap wrap-break-word", children: truncate(text, 40) })] }) }), _jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs", children: text }) })] }) }));
45
45
  };
46
46
  /**
47
47
  * Toggle button for showing/hiding prompt suggestions
@@ -1 +1 @@
1
- {"version":3,"file":"PromptSuggestions.js","sourceRoot":"","sources":["../../src/components/PromptSuggestions.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n cn,\n Button,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n Spinner,\n ScrollableRow,\n} from '@sqlrooms/ui';\nimport {Lightbulb, X} from 'lucide-react';\nimport {PropsWithChildren, useCallback, useRef} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {truncate} from '@sqlrooms/utils';\n\ntype PromptSuggestionsContainerProps = PropsWithChildren<{\n isLoading?: boolean;\n className?: string;\n}>;\n\n/**\n * Container component for prompt suggestions\n * Shows suggestions when visible, returns null when not visible\n */\nconst Container: React.FC<PromptSuggestionsContainerProps> = ({\n isLoading = false,\n className,\n children,\n}) => {\n const isVisible = useStoreWithAi((s) => s.ai.promptSuggestionsVisible);\n const setIsVisible = useStoreWithAi((s) => s.ai.setPromptSuggestionsVisible);\n\n const toggleVisibility = useCallback(() => {\n setIsVisible(!isVisible);\n }, [isVisible, setIsVisible]);\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div className={cn('w-full py-1', className)}>\n {/* Container with scrollable suggestions and hide button */}\n <div className=\"flex h-full w-full gap-2\">\n <ScrollableRow\n className=\"min-w-0 flex-1\"\n scrollClassName=\"flex flex-1 snap-x snap-mandatory scroll-pl-7 scroll-pr-7 gap-2 overflow-x-auto overflow-y-hidden px-1 py-1 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n arrowVisibility=\"always\"\n arrowIconClassName=\"h-4 w-4 opacity-80\"\n >\n {isLoading\n ? // Show placeholder buttons with spinners while loading\n Array.from({length: 3}).map((_, index) => (\n <div key={index} className=\"shrink-0 snap-start\">\n <Button\n disabled\n className={cn(\n 'bg-muted/50 border-border border',\n 'rounded-lg',\n 'text-muted-foreground text-xs',\n 'relative',\n 'flex items-center justify-center',\n 'px-4 py-2',\n 'h-18 max-h-18 min-h-18 w-48 min-w-48 max-w-48',\n )}\n type=\"button\"\n >\n <Spinner className=\"text-muted-foreground h-3.5 w-3.5\" />\n </Button>\n </div>\n ))\n : children}\n </ScrollableRow>\n\n <div className=\"flex shrink-0 items-center pr-1\">\n <Button\n onClick={toggleVisibility}\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-muted-foreground hover:text-foreground h-6 w-6 shrink-0\"\n title=\"Hide prompt suggestions\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </TooltipProvider>\n );\n};\n\ntype PromptSuggestionsItemProps = {\n text: string;\n className?: string;\n icon?: React.ReactNode;\n};\n\n/**\n * Individual prompt suggestion item component\n * Displays a single prompt suggestion and handles click events\n */\nconst Item: React.FC<PromptSuggestionsItemProps> = ({\n text,\n className,\n icon,\n}) => {\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const setPrompt = useStoreWithAi((s) => s.ai.setPrompt);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleClick = useCallback(() => {\n if (currentSession?.id) {\n setPrompt(currentSession.id, text);\n }\n // Scroll the clicked item into view\n buttonRef.current?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'nearest',\n });\n }, [text, setPrompt, currentSession]);\n\n return (\n <div className=\"shrink-0 snap-start\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n ref={buttonRef}\n onClick={handleClick}\n className={cn(\n 'bg-muted/50 hover:bg-muted hover:shadow-lg',\n 'border-border hover:border-primary/50 border',\n 'rounded-lg',\n 'text-muted-foreground hover:text-foreground text-xs',\n 'transition-all duration-200 ease-in-out',\n 'hover:-translate-y-0.5 hover:scale-[1.02]',\n 'cursor-pointer',\n 'relative',\n 'flex items-start justify-start',\n 'text-left',\n 'overflow-hidden',\n 'py-2 pl-8 pr-4',\n 'h-18 max-h-18 min-h-18 w-48 min-w-48 max-w-48',\n className,\n )}\n type=\"button\"\n title={text}\n >\n <span className=\"absolute left-2 top-3 opacity-60\">\n {icon ?? <Lightbulb className=\"h-3.5 w-3.5\" />}\n </span>\n <span className=\"line-clamp-2 text-wrap break-words\">\n {truncate(text, 40)}\n </span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p className=\"max-w-xs\">{text}</p>\n </TooltipContent>\n </Tooltip>\n </div>\n );\n};\n\ntype PromptSuggestionsVisibilityToggleProps = {\n className?: string;\n icon?: React.ReactNode;\n};\n\n/**\n * Toggle button for showing/hiding prompt suggestions\n * Can be placed anywhere in the UI\n * Always shows a Lightbulb icon with styling that changes based on state\n */\nconst VisibilityToggle: React.FC<PromptSuggestionsVisibilityToggleProps> = ({\n className,\n icon,\n}) => {\n const isVisible = useStoreWithAi((s) => s.ai.promptSuggestionsVisible);\n const setIsVisible = useStoreWithAi((s) => s.ai.setPromptSuggestionsVisible);\n\n const toggleVisibility = useCallback(() => {\n setIsVisible(!isVisible);\n }, [isVisible, setIsVisible]);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n onClick={toggleVisibility}\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\n 'h-6 w-6 shrink-0',\n isVisible\n ? 'bg-secondary text-secondary-foreground hover:bg-secondary/80'\n : 'text-muted-foreground hover:text-foreground',\n className,\n )}\n title={\n isVisible ? 'Hide prompt suggestions' : 'Show prompt suggestions'\n }\n >\n {icon ?? <Lightbulb className=\"h-4 w-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>\n {isVisible ? 'Hide prompt suggestions' : 'Show prompt suggestions'}\n </p>\n </TooltipContent>\n </Tooltip>\n );\n};\n\n/**\n * Composable PromptSuggestions component with Container, Item, and VisibilityToggle subcomponents\n *\n * @example\n * ```tsx\n * <PromptSuggestions.Container isLoading={false}>\n * <PromptSuggestions.Item text=\"What are the top selling products?\" />\n * <PromptSuggestions.Item text=\"Show me the revenue trends\" />\n * </PromptSuggestions.Container>\n *\n * <PromptSuggestions.VisibilityToggle />\n * ```\n */\nexport const PromptSuggestions = {\n Container,\n Item,\n VisibilityToggle,\n};\n"]}
1
+ {"version":3,"file":"PromptSuggestions.js","sourceRoot":"","sources":["../../src/components/PromptSuggestions.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n cn,\n Button,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n Spinner,\n ScrollableRow,\n} from '@sqlrooms/ui';\nimport {Lightbulb, X} from 'lucide-react';\nimport {PropsWithChildren, useCallback, useRef} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {truncate} from '@sqlrooms/utils';\n\ntype PromptSuggestionsContainerProps = PropsWithChildren<{\n isLoading?: boolean;\n className?: string;\n}>;\n\n/**\n * Container component for prompt suggestions\n * Shows suggestions when visible, returns null when not visible\n */\nconst Container: React.FC<PromptSuggestionsContainerProps> = ({\n isLoading = false,\n className,\n children,\n}) => {\n const isVisible = useStoreWithAi((s) => s.ai.promptSuggestionsVisible);\n const setIsVisible = useStoreWithAi((s) => s.ai.setPromptSuggestionsVisible);\n\n const toggleVisibility = useCallback(() => {\n setIsVisible(!isVisible);\n }, [isVisible, setIsVisible]);\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div className={cn('w-full py-1', className)}>\n {/* Container with scrollable suggestions and hide button */}\n <div className=\"flex h-full w-full gap-2\">\n <ScrollableRow\n className=\"min-w-0 flex-1\"\n scrollClassName=\"flex flex-1 snap-x snap-mandatory scroll-pl-7 scroll-pr-7 gap-2 overflow-x-auto overflow-y-hidden px-1 py-1 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n arrowVisibility=\"always\"\n arrowIconClassName=\"h-4 w-4 opacity-80\"\n >\n {isLoading\n ? // Show placeholder buttons with spinners while loading\n Array.from({length: 3}).map((_, index) => (\n <div key={index} className=\"shrink-0 snap-start\">\n <Button\n disabled\n className={cn(\n 'bg-muted/50 border-border border',\n 'rounded-lg',\n 'text-muted-foreground text-xs',\n 'relative',\n 'flex items-center justify-center',\n 'px-4 py-2',\n 'h-18 max-h-18 min-h-18 w-48 max-w-48 min-w-48',\n )}\n type=\"button\"\n >\n <Spinner className=\"text-muted-foreground h-3.5 w-3.5\" />\n </Button>\n </div>\n ))\n : children}\n </ScrollableRow>\n\n <div className=\"flex shrink-0 items-center pr-1\">\n <Button\n onClick={toggleVisibility}\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-muted-foreground hover:text-foreground h-6 w-6 shrink-0\"\n title=\"Hide prompt suggestions\"\n >\n <X className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </TooltipProvider>\n );\n};\n\ntype PromptSuggestionsItemProps = {\n text: string;\n className?: string;\n icon?: React.ReactNode;\n};\n\n/**\n * Individual prompt suggestion item component\n * Displays a single prompt suggestion and handles click events\n */\nconst Item: React.FC<PromptSuggestionsItemProps> = ({\n text,\n className,\n icon,\n}) => {\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const setPrompt = useStoreWithAi((s) => s.ai.setPrompt);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleClick = useCallback(() => {\n if (currentSession?.id) {\n setPrompt(currentSession.id, text);\n }\n // Scroll the clicked item into view\n buttonRef.current?.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'nearest',\n });\n }, [text, setPrompt, currentSession]);\n\n return (\n <div className=\"shrink-0 snap-start\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n ref={buttonRef}\n onClick={handleClick}\n className={cn(\n 'bg-muted/50 hover:bg-muted hover:shadow-lg',\n 'border-border hover:border-primary/50 border',\n 'rounded-lg',\n 'text-muted-foreground hover:text-foreground text-xs',\n 'transition-all duration-200 ease-in-out',\n 'hover:-translate-y-0.5 hover:scale-[1.02]',\n 'cursor-pointer',\n 'relative',\n 'flex items-start justify-start',\n 'text-left',\n 'overflow-hidden',\n 'py-2 pr-4 pl-8',\n 'h-18 max-h-18 min-h-18 w-48 max-w-48 min-w-48',\n className,\n )}\n type=\"button\"\n title={text}\n >\n <span className=\"absolute top-3 left-2 opacity-60\">\n {icon ?? <Lightbulb className=\"h-3.5 w-3.5\" />}\n </span>\n <span className=\"line-clamp-2 text-wrap wrap-break-word\">\n {truncate(text, 40)}\n </span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p className=\"max-w-xs\">{text}</p>\n </TooltipContent>\n </Tooltip>\n </div>\n );\n};\n\ntype PromptSuggestionsVisibilityToggleProps = {\n className?: string;\n icon?: React.ReactNode;\n};\n\n/**\n * Toggle button for showing/hiding prompt suggestions\n * Can be placed anywhere in the UI\n * Always shows a Lightbulb icon with styling that changes based on state\n */\nconst VisibilityToggle: React.FC<PromptSuggestionsVisibilityToggleProps> = ({\n className,\n icon,\n}) => {\n const isVisible = useStoreWithAi((s) => s.ai.promptSuggestionsVisible);\n const setIsVisible = useStoreWithAi((s) => s.ai.setPromptSuggestionsVisible);\n\n const toggleVisibility = useCallback(() => {\n setIsVisible(!isVisible);\n }, [isVisible, setIsVisible]);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n onClick={toggleVisibility}\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\n 'h-6 w-6 shrink-0',\n isVisible\n ? 'bg-secondary text-secondary-foreground hover:bg-secondary/80'\n : 'text-muted-foreground hover:text-foreground',\n className,\n )}\n title={\n isVisible ? 'Hide prompt suggestions' : 'Show prompt suggestions'\n }\n >\n {icon ?? <Lightbulb className=\"h-4 w-4\" />}\n </Button>\n </TooltipTrigger>\n <TooltipContent>\n <p>\n {isVisible ? 'Hide prompt suggestions' : 'Show prompt suggestions'}\n </p>\n </TooltipContent>\n </Tooltip>\n );\n};\n\n/**\n * Composable PromptSuggestions component with Container, Item, and VisibilityToggle subcomponents\n *\n * @example\n * ```tsx\n * <PromptSuggestions.Container isLoading={false}>\n * <PromptSuggestions.Item text=\"What are the top selling products?\" />\n * <PromptSuggestions.Item text=\"Show me the revenue trends\" />\n * </PromptSuggestions.Container>\n *\n * <PromptSuggestions.VisibilityToggle />\n * ```\n */\nexport const PromptSuggestions = {\n Container,\n Item,\n VisibilityToggle,\n};\n"]}
@@ -83,7 +83,7 @@ export const QueryControls = ({ className, placeholder = 'What would you like to
83
83
  return (_jsx("div", { className: cn('flex w-full flex-col items-center justify-center gap-2', className), children: _jsx("div", { className: "bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border", children: _jsx("div", { className: "flex w-full flex-col gap-1 overflow-hidden", children: _jsx(InlineApiKeyInputRenderer, { inlineApiKeyInput: inlineApiKeyInput, children: otherChildren }) }) }) }));
84
84
  }
85
85
  // Render the normal prompt mode
86
- return (_jsx("div", { className: cn('flex w-full flex-col items-center justify-center gap-2', className), children: _jsx("div", { className: "bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border", children: _jsxs("div", { className: "flex w-full flex-col gap-1 overflow-hidden", children: [_jsx(Textarea, { ref: textareaRef, className: "max-h-[min(300px,40vh)] min-h-[30px] resize-none border-none p-2 text-sm outline-none focus-visible:ring-0", autoResize: true, value: prompt, onChange: (e) => {
86
+ return (_jsx("div", { className: cn('flex w-full flex-col items-center justify-center gap-2', className), children: _jsx("div", { className: "bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border", children: _jsxs("div", { className: "flex w-full flex-col gap-1 overflow-hidden", children: [_jsx(Textarea, { ref: textareaRef, className: "max-h-[min(300px,40vh)] min-h-[30px] resize-none border-none p-2 text-sm outline-hidden focus-visible:ring-0", autoResize: true, value: prompt, onChange: (e) => {
87
87
  if (sessionId) {
88
88
  setPrompt(sessionId, e.target.value);
89
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"QueryControls.js","sourceRoot":"","sources":["../../src/components/QueryControls.tsx"],"names":[],"mappings":"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","sourcesContent":["import {Button, cn, Textarea} from '@sqlrooms/ui';\nimport {ArrowUpIcon, OctagonXIcon} from 'lucide-react';\nimport {\n PropsWithChildren,\n useCallback,\n useRef,\n useEffect,\n useState,\n Children,\n isValidElement,\n ReactNode,\n} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {InlineApiKeyInput, InlineApiKeyInputButton} from './InlineApiKeyInput';\n\ntype QueryControlsProps = PropsWithChildren<{\n className?: string;\n placeholder?: string;\n onRun?: () => void;\n onCancel?: () => void;\n}>;\n\n/**\n * Checks if a child is an InlineApiKeyInput component\n */\nfunction isInlineApiKeyInput(\n child: ReactNode,\n): child is React.ReactElement<React.ComponentProps<typeof InlineApiKeyInput>> {\n return isValidElement(child) && child.type === InlineApiKeyInput;\n}\n\n/**\n * Extracts InlineApiKeyInput from children and returns the rest\n */\nfunction extractInlineApiKeyInput(children: ReactNode): {\n inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n > | null;\n otherChildren: ReactNode[];\n} {\n let inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n > | null = null;\n const otherChildren: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (isInlineApiKeyInput(child)) {\n inlineApiKeyInput = child;\n } else {\n otherChildren.push(child);\n }\n });\n\n return {inlineApiKeyInput, otherChildren};\n}\n\nexport const QueryControls: React.FC<QueryControlsProps> = ({\n className,\n placeholder = 'What would you like to learn about the data?',\n children,\n onRun,\n onCancel,\n}) => {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const model = currentSession?.model;\n\n const apiKey = useStoreWithAi((s) => s.ai.getApiKeyFromSettings());\n const hasApiKeyError = useStoreWithAi((s) => s.ai.hasApiKeyError());\n\n // Extract InlineApiKeyInput from children\n const {inlineApiKeyInput, otherChildren} = extractInlineApiKeyInput(children);\n\n // Show API key input if InlineApiKeyInput is provided and either:\n // - No API key is set, OR\n // - There's an API key error (invalid key)\n const showApiKeyInput =\n inlineApiKeyInput !== null &&\n (!apiKey || apiKey.trim().length === 0 || hasApiKeyError);\n\n const isRunning = useStoreWithAi((s) =>\n sessionId ? s.ai.getIsRunning(sessionId) : false,\n );\n const prompt = useStoreWithAi((s) =>\n sessionId ? s.ai.getPrompt(sessionId) : '',\n );\n const setPrompt = useStoreWithAi((s) => s.ai.setPrompt);\n const runAnalysis = useStoreWithAi((s) => s.ai.startAnalysis);\n const cancelAnalysis = useStoreWithAi((s) => s.ai.cancelAnalysis);\n\n useEffect(() => {\n if (showApiKeyInput) return;\n const timer = setTimeout(() => {\n textareaRef.current?.focus();\n }, 500);\n return () => clearTimeout(timer);\n }, [showApiKeyInput]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.ctrlKey &&\n !e.altKey &&\n !e.metaKey\n ) {\n e.preventDefault();\n if (!isRunning && sessionId && model && prompt.trim().length) {\n runAnalysis(sessionId);\n }\n }\n },\n [isRunning, sessionId, model, prompt, runAnalysis],\n );\n\n const canStart = Boolean(sessionId && model && prompt.trim().length);\n\n const handleClickRunOrCancel = useCallback(() => {\n if (!sessionId) return;\n if (isRunning) {\n cancelAnalysis(sessionId);\n onCancel?.();\n } else {\n runAnalysis(sessionId);\n onRun?.();\n }\n }, [sessionId, isRunning, cancelAnalysis, onCancel, runAnalysis, onRun]);\n\n // Render the API key input mode\n if (showApiKeyInput && inlineApiKeyInput) {\n return (\n <div\n className={cn(\n 'flex w-full flex-col items-center justify-center gap-2',\n className,\n )}\n >\n <div className=\"bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border\">\n <div className=\"flex w-full flex-col gap-1 overflow-hidden\">\n {/* Render the InlineApiKeyInput which provides Input + Button */}\n <InlineApiKeyInputRenderer inlineApiKeyInput={inlineApiKeyInput}>\n {otherChildren}\n </InlineApiKeyInputRenderer>\n </div>\n </div>\n </div>\n );\n }\n\n // Render the normal prompt mode\n return (\n <div\n className={cn(\n 'flex w-full flex-col items-center justify-center gap-2',\n className,\n )}\n >\n <div className=\"bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border\">\n <div className=\"flex w-full flex-col gap-1 overflow-hidden\">\n <Textarea\n ref={textareaRef}\n className=\"max-h-[min(300px,40vh)] min-h-[30px] resize-none border-none p-2 text-sm outline-none focus-visible:ring-0\"\n autoResize\n value={prompt}\n onChange={(e) => {\n if (sessionId) {\n setPrompt(sessionId, e.target.value);\n }\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n autoFocus\n />\n <div className=\"align-stretch flex w-full items-center gap-2 overflow-hidden\">\n <div className=\"flex h-full w-full min-w-0 items-center gap-2 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <div className=\"flex flex-nowrap items-center gap-2 overflow-x-auto py-1 pl-2\">\n {otherChildren}\n </div>\n </div>\n <div className=\"ml-auto shrink-0 gap-2 p-2\">\n <Button\n className=\"h-8 w-8 rounded-full\"\n variant=\"default\"\n size=\"icon\"\n onClick={handleClickRunOrCancel}\n disabled={!isRunning && !canStart}\n >\n {isRunning ? <OctagonXIcon /> : <ArrowUpIcon />}\n </Button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Internal component that renders the InlineApiKeyInput with proper layout\n */\nconst InlineApiKeyInputRenderer: React.FC<{\n inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n >;\n children: ReactNode;\n}> = ({inlineApiKeyInput, children}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n const [apiKeyInput, setApiKeyInput] = useState('');\n\n const modelProvider = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.modelProvider || 'openai',\n );\n const setApiKeyError = useStoreWithAi((s) => s.ai.setApiKeyError);\n\n const {onSaveApiKey} = inlineApiKeyInput.props;\n\n useEffect(() => {\n const timer = setTimeout(() => {\n inputRef.current?.focus();\n }, 500);\n return () => clearTimeout(timer);\n }, []);\n\n const handleSaveKey = useCallback(\n (provider: string, apiKey: string) => {\n // Clear the API key error for this provider when saving a new key\n setApiKeyError(provider, false);\n onSaveApiKey(provider, apiKey);\n },\n [onSaveApiKey, setApiKeyError],\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.ctrlKey &&\n !e.altKey &&\n !e.metaKey\n ) {\n e.preventDefault();\n if (apiKeyInput.trim() && modelProvider) {\n handleSaveKey(modelProvider, apiKeyInput.trim());\n setApiKeyInput('');\n }\n }\n },\n [apiKeyInput, modelProvider, handleSaveKey],\n );\n\n const handleSave = useCallback(() => {\n if (apiKeyInput.trim() && modelProvider) {\n handleSaveKey(modelProvider, apiKeyInput.trim());\n setApiKeyInput('');\n }\n }, [apiKeyInput, modelProvider, handleSaveKey]);\n\n const canSave = Boolean(apiKeyInput.trim().length && modelProvider);\n\n const formatProviderLabel = (provider: string) =>\n provider.charAt(0).toUpperCase() + provider.slice(1);\n\n return (\n <>\n <input\n ref={inputRef}\n type=\"password\"\n className=\"min-h-[30px] flex-1 border-none bg-transparent p-2 text-sm outline-none focus-visible:ring-0\"\n value={apiKeyInput}\n onChange={(e) => setApiKeyInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={`Enter your ${formatProviderLabel(modelProvider)} API key...`}\n autoFocus\n autoComplete=\"off\"\n />\n <div className=\"align-stretch flex w-full items-center gap-2 overflow-hidden\">\n <div className=\"flex h-full w-full min-w-0 items-center gap-2 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <div className=\"flex flex-nowrap items-center gap-2 overflow-x-auto py-1 pl-2\">\n {children}\n </div>\n </div>\n <div className=\"ml-auto shrink-0 gap-2 p-2\">\n <InlineApiKeyInputButton onSave={handleSave} disabled={!canSave} />\n </div>\n </div>\n </div>\n </>\n );\n};\n"]}
1
+ {"version":3,"file":"QueryControls.js","sourceRoot":"","sources":["../../src/components/QueryControls.tsx"],"names":[],"mappings":"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","sourcesContent":["import {Button, cn, Textarea} from '@sqlrooms/ui';\nimport {ArrowUpIcon, OctagonXIcon} from 'lucide-react';\nimport {\n PropsWithChildren,\n useCallback,\n useRef,\n useEffect,\n useState,\n Children,\n isValidElement,\n ReactNode,\n} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {InlineApiKeyInput, InlineApiKeyInputButton} from './InlineApiKeyInput';\n\ntype QueryControlsProps = PropsWithChildren<{\n className?: string;\n placeholder?: string;\n onRun?: () => void;\n onCancel?: () => void;\n}>;\n\n/**\n * Checks if a child is an InlineApiKeyInput component\n */\nfunction isInlineApiKeyInput(\n child: ReactNode,\n): child is React.ReactElement<React.ComponentProps<typeof InlineApiKeyInput>> {\n return isValidElement(child) && child.type === InlineApiKeyInput;\n}\n\n/**\n * Extracts InlineApiKeyInput from children and returns the rest\n */\nfunction extractInlineApiKeyInput(children: ReactNode): {\n inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n > | null;\n otherChildren: ReactNode[];\n} {\n let inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n > | null = null;\n const otherChildren: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (isInlineApiKeyInput(child)) {\n inlineApiKeyInput = child;\n } else {\n otherChildren.push(child);\n }\n });\n\n return {inlineApiKeyInput, otherChildren};\n}\n\nexport const QueryControls: React.FC<QueryControlsProps> = ({\n className,\n placeholder = 'What would you like to learn about the data?',\n children,\n onRun,\n onCancel,\n}) => {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const currentSession = useStoreWithAi((s) => s.ai.getCurrentSession());\n const sessionId = currentSession?.id;\n const model = currentSession?.model;\n\n const apiKey = useStoreWithAi((s) => s.ai.getApiKeyFromSettings());\n const hasApiKeyError = useStoreWithAi((s) => s.ai.hasApiKeyError());\n\n // Extract InlineApiKeyInput from children\n const {inlineApiKeyInput, otherChildren} = extractInlineApiKeyInput(children);\n\n // Show API key input if InlineApiKeyInput is provided and either:\n // - No API key is set, OR\n // - There's an API key error (invalid key)\n const showApiKeyInput =\n inlineApiKeyInput !== null &&\n (!apiKey || apiKey.trim().length === 0 || hasApiKeyError);\n\n const isRunning = useStoreWithAi((s) =>\n sessionId ? s.ai.getIsRunning(sessionId) : false,\n );\n const prompt = useStoreWithAi((s) =>\n sessionId ? s.ai.getPrompt(sessionId) : '',\n );\n const setPrompt = useStoreWithAi((s) => s.ai.setPrompt);\n const runAnalysis = useStoreWithAi((s) => s.ai.startAnalysis);\n const cancelAnalysis = useStoreWithAi((s) => s.ai.cancelAnalysis);\n\n useEffect(() => {\n if (showApiKeyInput) return;\n const timer = setTimeout(() => {\n textareaRef.current?.focus();\n }, 500);\n return () => clearTimeout(timer);\n }, [showApiKeyInput]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.ctrlKey &&\n !e.altKey &&\n !e.metaKey\n ) {\n e.preventDefault();\n if (!isRunning && sessionId && model && prompt.trim().length) {\n runAnalysis(sessionId);\n }\n }\n },\n [isRunning, sessionId, model, prompt, runAnalysis],\n );\n\n const canStart = Boolean(sessionId && model && prompt.trim().length);\n\n const handleClickRunOrCancel = useCallback(() => {\n if (!sessionId) return;\n if (isRunning) {\n cancelAnalysis(sessionId);\n onCancel?.();\n } else {\n runAnalysis(sessionId);\n onRun?.();\n }\n }, [sessionId, isRunning, cancelAnalysis, onCancel, runAnalysis, onRun]);\n\n // Render the API key input mode\n if (showApiKeyInput && inlineApiKeyInput) {\n return (\n <div\n className={cn(\n 'flex w-full flex-col items-center justify-center gap-2',\n className,\n )}\n >\n <div className=\"bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border\">\n <div className=\"flex w-full flex-col gap-1 overflow-hidden\">\n {/* Render the InlineApiKeyInput which provides Input + Button */}\n <InlineApiKeyInputRenderer inlineApiKeyInput={inlineApiKeyInput}>\n {otherChildren}\n </InlineApiKeyInputRenderer>\n </div>\n </div>\n </div>\n );\n }\n\n // Render the normal prompt mode\n return (\n <div\n className={cn(\n 'flex w-full flex-col items-center justify-center gap-2',\n className,\n )}\n >\n <div className=\"bg-muted/50 flex h-full w-full flex-row items-center gap-2 rounded-md border\">\n <div className=\"flex w-full flex-col gap-1 overflow-hidden\">\n <Textarea\n ref={textareaRef}\n className=\"max-h-[min(300px,40vh)] min-h-[30px] resize-none border-none p-2 text-sm outline-hidden focus-visible:ring-0\"\n autoResize\n value={prompt}\n onChange={(e) => {\n if (sessionId) {\n setPrompt(sessionId, e.target.value);\n }\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n autoFocus\n />\n <div className=\"align-stretch flex w-full items-center gap-2 overflow-hidden\">\n <div className=\"flex h-full w-full min-w-0 items-center gap-2 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <div className=\"flex flex-nowrap items-center gap-2 overflow-x-auto py-1 pl-2\">\n {otherChildren}\n </div>\n </div>\n <div className=\"ml-auto shrink-0 gap-2 p-2\">\n <Button\n className=\"h-8 w-8 rounded-full\"\n variant=\"default\"\n size=\"icon\"\n onClick={handleClickRunOrCancel}\n disabled={!isRunning && !canStart}\n >\n {isRunning ? <OctagonXIcon /> : <ArrowUpIcon />}\n </Button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Internal component that renders the InlineApiKeyInput with proper layout\n */\nconst InlineApiKeyInputRenderer: React.FC<{\n inlineApiKeyInput: React.ReactElement<\n React.ComponentProps<typeof InlineApiKeyInput>\n >;\n children: ReactNode;\n}> = ({inlineApiKeyInput, children}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n const [apiKeyInput, setApiKeyInput] = useState('');\n\n const modelProvider = useStoreWithAi(\n (s) => s.ai.getCurrentSession()?.modelProvider || 'openai',\n );\n const setApiKeyError = useStoreWithAi((s) => s.ai.setApiKeyError);\n\n const {onSaveApiKey} = inlineApiKeyInput.props;\n\n useEffect(() => {\n const timer = setTimeout(() => {\n inputRef.current?.focus();\n }, 500);\n return () => clearTimeout(timer);\n }, []);\n\n const handleSaveKey = useCallback(\n (provider: string, apiKey: string) => {\n // Clear the API key error for this provider when saving a new key\n setApiKeyError(provider, false);\n onSaveApiKey(provider, apiKey);\n },\n [onSaveApiKey, setApiKeyError],\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (\n e.key === 'Enter' &&\n !e.shiftKey &&\n !e.ctrlKey &&\n !e.altKey &&\n !e.metaKey\n ) {\n e.preventDefault();\n if (apiKeyInput.trim() && modelProvider) {\n handleSaveKey(modelProvider, apiKeyInput.trim());\n setApiKeyInput('');\n }\n }\n },\n [apiKeyInput, modelProvider, handleSaveKey],\n );\n\n const handleSave = useCallback(() => {\n if (apiKeyInput.trim() && modelProvider) {\n handleSaveKey(modelProvider, apiKeyInput.trim());\n setApiKeyInput('');\n }\n }, [apiKeyInput, modelProvider, handleSaveKey]);\n\n const canSave = Boolean(apiKeyInput.trim().length && modelProvider);\n\n const formatProviderLabel = (provider: string) =>\n provider.charAt(0).toUpperCase() + provider.slice(1);\n\n return (\n <>\n <input\n ref={inputRef}\n type=\"password\"\n className=\"min-h-[30px] flex-1 border-none bg-transparent p-2 text-sm outline-none focus-visible:ring-0\"\n value={apiKeyInput}\n onChange={(e) => setApiKeyInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={`Enter your ${formatProviderLabel(modelProvider)} API key...`}\n autoFocus\n autoComplete=\"off\"\n />\n <div className=\"align-stretch flex w-full items-center gap-2 overflow-hidden\">\n <div className=\"flex h-full w-full min-w-0 items-center gap-2 overflow-hidden\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <div className=\"flex flex-nowrap items-center gap-2 overflow-x-auto py-1 pl-2\">\n {children}\n </div>\n </div>\n <div className=\"ml-auto shrink-0 gap-2 p-2\">\n <InlineApiKeyInputButton onSave={handleSave} disabled={!canSave} />\n </div>\n </div>\n </div>\n </>\n );\n};\n"]}
@@ -76,7 +76,7 @@ export const SessionControls = ({ className, children }) => {
76
76
  setSessionToRename(null);
77
77
  }, [sessionToRename, renameSession]);
78
78
  // Memoize render functions to prevent unnecessary re-renders
79
- const renderTabLabel = useCallback((tab) => (_jsxs("div", { className: "flex items-center gap-2", children: [getIsSessionRunning(tab.id) && (_jsx(Spinner, { className: "text-muted-foreground h-4 w-4 flex-shrink-0 animate-spin" })), _jsx("span", { className: "truncate", children: tab.name })] })), [getIsSessionRunning]);
79
+ const renderTabLabel = useCallback((tab) => (_jsxs("div", { className: "flex items-center gap-2", children: [getIsSessionRunning(tab.id) && (_jsx(Spinner, { className: "text-muted-foreground h-4 w-4 shrink-0 animate-spin" })), _jsx("span", { className: "truncate", children: tab.name })] })), [getIsSessionRunning]);
80
80
  const renderTabMenu = useCallback((tab) => (_jsxs(_Fragment, { children: [_jsxs(TabStrip.MenuItem, { onClick: () => handleRenameRequest(tab.id), children: [_jsx(PencilIcon, { className: "mr-2 h-4 w-4" }), "Rename"] }), sessions.length > 1 && (_jsxs(_Fragment, { children: [_jsx(TabStrip.MenuSeparator, {}), _jsxs(TabStrip.MenuItem, { variant: "destructive", onClick: () => handleDelete(tab.id), children: [_jsx(TrashIcon, { className: "mr-2 h-4 w-4" }), "Delete"] })] }))] })), [handleRenameRequest, handleDelete, sessions.length]);
81
81
  const renderSearchItemActions = useCallback((tab) => (_jsxs(_Fragment, { children: [_jsx(TabStrip.SearchItemAction, { icon: _jsx(PencilIcon, { className: "h-3 w-3" }), "aria-label": `Rename ${tab.name}`, onClick: () => handleRenameRequest(tab.id) }), sessions.length > 1 && (_jsx(TabStrip.SearchItemAction, { icon: _jsx(TrashIcon, { className: "h-3 w-3" }), "aria-label": `Delete ${tab.name}`, onClick: () => handleDelete(tab.id) }))] })), [handleRenameRequest, handleDelete, sessions.length]);
82
82
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: cn('flex w-full flex-wrap items-center justify-between gap-2 overflow-visible', className), children: [_jsx("div", { className: "flex w-full min-w-0 items-center gap-3", children: _jsxs(TabStrip, { className: "bg-background", tabs: tabs, preventCloseLastTab: true, openTabs: openTabs, selectedTabId: currentSessionId, onClose: handleClose, onOpenTabsChange: setOpenTabs, onSelect: switchSession, onCreate: () => createSession(), onRename: renameSession, renderTabLabel: renderTabLabel, renderTabMenu: renderTabMenu, renderSearchItemActions: renderSearchItemActions, children: [_jsx(TabStrip.SearchDropdown, { triggerIcon: _jsx(HistoryIcon, { className: "h-4 w-4" }), tooltip: "Session history", sortSearchItems: "recent", getTabLastOpenedAt: (tab) => tab.lastOpenedAt }), _jsx(TabStrip.Tabs, { tabClassName: "rounded-md data-[state=active]:bg-muted" }), _jsx(TabStrip.NewButton, { tooltip: "New session" })] }) }), children] }), _jsx(DeleteSessionDialog, { isOpen: sessionToDelete !== null, onClose: handleCloseDeleteDialog, onDelete: handleConfirmDelete }), _jsx(RenameSessionDialog, { isOpen: sessionToRename !== null, onClose: () => setSessionToRename(null), initialName: sessionToRename?.name ?? '', onRename: handleFinishRename })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"SessionControls.js","sourceRoot":"","sources":["../../src/components/SessionControls.tsx"],"names":[],"mappings":"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","sourcesContent":["import {cn, Spinner, TabStrip} from '@sqlrooms/ui';\nimport {HistoryIcon, PencilIcon, TrashIcon} from 'lucide-react';\nimport {useCallback, useMemo, useState} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {DeleteSessionDialog, RenameSessionDialog} from './session';\n\n/**\n * Main component for managing AI sessions.\n * Uses TabStrip for session management with dropdown for switching sessions.\n *\n * @example\n * ```tsx\n * <SessionControls className=\"p-4 border-b\">\n * <Button>Custom Button</Button>\n * </SessionControls>\n * ```\n */\nexport const SessionControls: React.FC<{\n className?: string;\n children?: React.ReactNode;\n}> = ({className, children}) => {\n const sessions = useStoreWithAi((s) => s.ai.config.sessions);\n const currentSessionId = useStoreWithAi((s) => s.ai.config.currentSessionId);\n const getIsSessionRunning = useStoreWithAi((s) => s.ai.getIsRunning);\n const switchSession = useStoreWithAi((s) => s.ai.switchSession);\n const createSession = useStoreWithAi((s) => s.ai.createSession);\n const renameSession = useStoreWithAi((s) => s.ai.renameSession);\n const deleteSession = useStoreWithAi((s) => s.ai.deleteSession);\n const openTabs = useStoreWithAi((s) => s.ai.config.openSessionTabs);\n const setOpenTabs = useStoreWithAi((s) => s.ai.setOpenSessionTabs);\n\n const [sessionToDelete, setSessionToDelete] = useState<string | null>(null);\n const [sessionToRename, setSessionToRename] = useState<{\n id: string;\n name: string;\n } | null>(null);\n\n // Convert sessions to TabDescriptor format\n const tabs = useMemo(\n () =>\n sessions.map((s) => ({\n id: s.id,\n name: s.name,\n lastOpenedAt: s.lastOpenedAt,\n })),\n [sessions],\n );\n\n const handleClose = useCallback(\n (sessionId: string) => {\n // Don't close if it's the only open tab\n if (!openTabs || openTabs.length <= 1) return;\n\n const remaining = openTabs.filter((id) => id !== sessionId);\n setOpenTabs(remaining);\n\n // If closing the current session, switch to another open one\n if (sessionId === currentSessionId && remaining.length > 0) {\n switchSession(remaining[0]!);\n }\n },\n [openTabs, currentSessionId, switchSession, setOpenTabs],\n );\n\n const handleDelete = useCallback(\n (sessionId: string) => {\n const session = sessions.find((s) => s.id === sessionId);\n // If session is empty (no messages), delete without confirmation\n if (!session?.uiMessages?.length) {\n deleteSession(sessionId);\n } else {\n setSessionToDelete(sessionId);\n }\n },\n [sessions, deleteSession],\n );\n\n const handleConfirmDelete = useCallback(() => {\n if (sessionToDelete) {\n deleteSession(sessionToDelete);\n setSessionToDelete(null);\n }\n }, [sessionToDelete, deleteSession]);\n\n const handleCloseDeleteDialog = useCallback(() => {\n setSessionToDelete(null);\n }, []);\n\n const handleRenameRequest = useCallback(\n (sessionId: string) => {\n const session = sessions.find((s) => s.id === sessionId);\n if (session) {\n setSessionToRename({id: sessionId, name: session.name});\n }\n },\n [sessions],\n );\n\n const handleFinishRename = useCallback(\n (newName: string) => {\n if (sessionToRename) {\n renameSession(sessionToRename.id, newName);\n }\n setSessionToRename(null);\n },\n [sessionToRename, renameSession],\n );\n\n // Memoize render functions to prevent unnecessary re-renders\n const renderTabLabel = useCallback(\n (tab: {id: string; name: string}) => (\n <div className=\"flex items-center gap-2\">\n {getIsSessionRunning(tab.id) && (\n <Spinner className=\"text-muted-foreground h-4 w-4 flex-shrink-0 animate-spin\" />\n )}\n <span className=\"truncate\">{tab.name}</span>\n </div>\n ),\n [getIsSessionRunning],\n );\n\n const renderTabMenu = useCallback(\n (tab: {id: string; name: string}) => (\n <>\n <TabStrip.MenuItem onClick={() => handleRenameRequest(tab.id)}>\n <PencilIcon className=\"mr-2 h-4 w-4\" />\n Rename\n </TabStrip.MenuItem>\n {sessions.length > 1 && (\n <>\n <TabStrip.MenuSeparator />\n <TabStrip.MenuItem\n variant=\"destructive\"\n onClick={() => handleDelete(tab.id)}\n >\n <TrashIcon className=\"mr-2 h-4 w-4\" />\n Delete\n </TabStrip.MenuItem>\n </>\n )}\n </>\n ),\n [handleRenameRequest, handleDelete, sessions.length],\n );\n\n const renderSearchItemActions = useCallback(\n (tab: {id: string; name: string}) => (\n <>\n <TabStrip.SearchItemAction\n icon={<PencilIcon className=\"h-3 w-3\" />}\n aria-label={`Rename ${tab.name}`}\n onClick={() => handleRenameRequest(tab.id)}\n />\n {sessions.length > 1 && (\n <TabStrip.SearchItemAction\n icon={<TrashIcon className=\"h-3 w-3\" />}\n aria-label={`Delete ${tab.name}`}\n onClick={() => handleDelete(tab.id)}\n />\n )}\n </>\n ),\n [handleRenameRequest, handleDelete, sessions.length],\n );\n\n return (\n <>\n <div\n className={cn(\n 'flex w-full flex-wrap items-center justify-between gap-2 overflow-visible',\n className,\n )}\n >\n <div className=\"flex w-full min-w-0 items-center gap-3\">\n <TabStrip\n className=\"bg-background\"\n tabs={tabs}\n preventCloseLastTab\n openTabs={openTabs}\n selectedTabId={currentSessionId}\n onClose={handleClose}\n onOpenTabsChange={setOpenTabs}\n onSelect={switchSession}\n onCreate={() => createSession()}\n onRename={renameSession}\n renderTabLabel={renderTabLabel}\n renderTabMenu={renderTabMenu}\n renderSearchItemActions={renderSearchItemActions}\n >\n <TabStrip.SearchDropdown\n triggerIcon={<HistoryIcon className=\"h-4 w-4\" />}\n tooltip=\"Session history\"\n sortSearchItems=\"recent\"\n getTabLastOpenedAt={(tab) =>\n tab.lastOpenedAt as number | undefined\n }\n />\n <TabStrip.Tabs tabClassName=\"rounded-md data-[state=active]:bg-muted\" />\n <TabStrip.NewButton tooltip=\"New session\" />\n </TabStrip>\n </div>\n\n {children}\n </div>\n\n <DeleteSessionDialog\n isOpen={sessionToDelete !== null}\n onClose={handleCloseDeleteDialog}\n onDelete={handleConfirmDelete}\n />\n <RenameSessionDialog\n isOpen={sessionToRename !== null}\n onClose={() => setSessionToRename(null)}\n initialName={sessionToRename?.name ?? ''}\n onRename={handleFinishRename}\n />\n </>\n );\n};\n"]}
1
+ {"version":3,"file":"SessionControls.js","sourceRoot":"","sources":["../../src/components/SessionControls.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AACnD,OAAO,EAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,cAAc,CAAC;AAChE,OAAO,EAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AACrD,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAC,mBAAmB,EAAE,mBAAmB,EAAC,MAAM,WAAW,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAGvB,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,EAAE,EAAE;IAC7B,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC7E,MAAM,mBAAmB,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;IACrE,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC;IAEnE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAG5C,IAAI,CAAC,CAAC;IAEhB,2CAA2C;IAC3C,MAAM,IAAI,GAAG,OAAO,CAClB,GAAG,EAAE,CACH,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,YAAY,EAAE,CAAC,CAAC,YAAY;KAC7B,CAAC,CAAC,EACL,CAAC,QAAQ,CAAC,CACX,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,SAAiB,EAAE,EAAE;QACpB,wCAAwC;QACxC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO;QAE9C,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAC5D,WAAW,CAAC,SAAS,CAAC,CAAC;QAEvB,6DAA6D;QAC7D,IAAI,SAAS,KAAK,gBAAgB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3D,aAAa,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,CAAC,CACzD,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,SAAiB,EAAE,EAAE;QACpB,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QACzD,iEAAiE;QACjE,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;YACjC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,EAAE,aAAa,CAAC,CAC1B,CAAC;IAEF,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC3C,IAAI,eAAe,EAAE,CAAC;YACpB,aAAa,CAAC,eAAe,CAAC,CAAC;YAC/B,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,EAAE,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC;IAErC,MAAM,uBAAuB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/C,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,SAAiB,EAAE,EAAE;QACpB,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QACzD,IAAI,OAAO,EAAE,CAAC;YACZ,kBAAkB,CAAC,EAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAC;IAEF,MAAM,kBAAkB,GAAG,WAAW,CACpC,CAAC,OAAe,EAAE,EAAE;QAClB,IAAI,eAAe,EAAE,CAAC;YACpB,aAAa,CAAC,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC;QACD,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC,EACD,CAAC,eAAe,EAAE,aAAa,CAAC,CACjC,CAAC;IAEF,6DAA6D;IAC7D,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,GAA+B,EAAE,EAAE,CAAC,CACnC,eAAK,SAAS,EAAC,yBAAyB,aACrC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAC9B,KAAC,OAAO,IAAC,SAAS,EAAC,qDAAqD,GAAG,CAC5E,EACD,eAAM,SAAS,EAAC,UAAU,YAAE,GAAG,CAAC,IAAI,GAAQ,IACxC,CACP,EACD,CAAC,mBAAmB,CAAC,CACtB,CAAC;IAEF,MAAM,aAAa,GAAG,WAAW,CAC/B,CAAC,GAA+B,EAAE,EAAE,CAAC,CACnC,8BACE,MAAC,QAAQ,CAAC,QAAQ,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,aAC3D,KAAC,UAAU,IAAC,SAAS,EAAC,cAAc,GAAG,cAErB,EACnB,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,8BACE,KAAC,QAAQ,CAAC,aAAa,KAAG,EAC1B,MAAC,QAAQ,CAAC,QAAQ,IAChB,OAAO,EAAC,aAAa,EACrB,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,aAEnC,KAAC,SAAS,IAAC,SAAS,EAAC,cAAc,GAAG,cAEpB,IACnB,CACJ,IACA,CACJ,EACD,CAAC,mBAAmB,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CACrD,CAAC;IAEF,MAAM,uBAAuB,GAAG,WAAW,CACzC,CAAC,GAA+B,EAAE,EAAE,CAAC,CACnC,8BACE,KAAC,QAAQ,CAAC,gBAAgB,IACxB,IAAI,EAAE,KAAC,UAAU,IAAC,SAAS,EAAC,SAAS,GAAG,gBAC5B,UAAU,GAAG,CAAC,IAAI,EAAE,EAChC,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,GAC1C,EACD,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,KAAC,QAAQ,CAAC,gBAAgB,IACxB,IAAI,EAAE,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,GAAG,gBAC3B,UAAU,GAAG,CAAC,IAAI,EAAE,EAChC,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,GACnC,CACH,IACA,CACJ,EACD,CAAC,mBAAmB,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CACrD,CAAC;IAEF,OAAO,CACL,8BACE,eACE,SAAS,EAAE,EAAE,CACX,2EAA2E,EAC3E,SAAS,CACV,aAED,cAAK,SAAS,EAAC,wCAAwC,YACrD,MAAC,QAAQ,IACP,SAAS,EAAC,eAAe,EACzB,IAAI,EAAE,IAAI,EACV,mBAAmB,QACnB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,gBAAgB,EAC/B,OAAO,EAAE,WAAW,EACpB,gBAAgB,EAAE,WAAW,EAC7B,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE,EAC/B,QAAQ,EAAE,aAAa,EACvB,cAAc,EAAE,cAAc,EAC9B,aAAa,EAAE,aAAa,EAC5B,uBAAuB,EAAE,uBAAuB,aAEhD,KAAC,QAAQ,CAAC,cAAc,IACtB,WAAW,EAAE,KAAC,WAAW,IAAC,SAAS,EAAC,SAAS,GAAG,EAChD,OAAO,EAAC,iBAAiB,EACzB,eAAe,EAAC,QAAQ,EACxB,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE,CAC1B,GAAG,CAAC,YAAkC,GAExC,EACF,KAAC,QAAQ,CAAC,IAAI,IAAC,YAAY,EAAC,yCAAyC,GAAG,EACxE,KAAC,QAAQ,CAAC,SAAS,IAAC,OAAO,EAAC,aAAa,GAAG,IACnC,GACP,EAEL,QAAQ,IACL,EAEN,KAAC,mBAAmB,IAClB,MAAM,EAAE,eAAe,KAAK,IAAI,EAChC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,mBAAmB,GAC7B,EACF,KAAC,mBAAmB,IAClB,MAAM,EAAE,eAAe,KAAK,IAAI,EAChC,OAAO,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,EACvC,WAAW,EAAE,eAAe,EAAE,IAAI,IAAI,EAAE,EACxC,QAAQ,EAAE,kBAAkB,GAC5B,IACD,CACJ,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {cn, Spinner, TabStrip} from '@sqlrooms/ui';\nimport {HistoryIcon, PencilIcon, TrashIcon} from 'lucide-react';\nimport {useCallback, useMemo, useState} from 'react';\nimport {useStoreWithAi} from '../AiSlice';\nimport {DeleteSessionDialog, RenameSessionDialog} from './session';\n\n/**\n * Main component for managing AI sessions.\n * Uses TabStrip for session management with dropdown for switching sessions.\n *\n * @example\n * ```tsx\n * <SessionControls className=\"p-4 border-b\">\n * <Button>Custom Button</Button>\n * </SessionControls>\n * ```\n */\nexport const SessionControls: React.FC<{\n className?: string;\n children?: React.ReactNode;\n}> = ({className, children}) => {\n const sessions = useStoreWithAi((s) => s.ai.config.sessions);\n const currentSessionId = useStoreWithAi((s) => s.ai.config.currentSessionId);\n const getIsSessionRunning = useStoreWithAi((s) => s.ai.getIsRunning);\n const switchSession = useStoreWithAi((s) => s.ai.switchSession);\n const createSession = useStoreWithAi((s) => s.ai.createSession);\n const renameSession = useStoreWithAi((s) => s.ai.renameSession);\n const deleteSession = useStoreWithAi((s) => s.ai.deleteSession);\n const openTabs = useStoreWithAi((s) => s.ai.config.openSessionTabs);\n const setOpenTabs = useStoreWithAi((s) => s.ai.setOpenSessionTabs);\n\n const [sessionToDelete, setSessionToDelete] = useState<string | null>(null);\n const [sessionToRename, setSessionToRename] = useState<{\n id: string;\n name: string;\n } | null>(null);\n\n // Convert sessions to TabDescriptor format\n const tabs = useMemo(\n () =>\n sessions.map((s) => ({\n id: s.id,\n name: s.name,\n lastOpenedAt: s.lastOpenedAt,\n })),\n [sessions],\n );\n\n const handleClose = useCallback(\n (sessionId: string) => {\n // Don't close if it's the only open tab\n if (!openTabs || openTabs.length <= 1) return;\n\n const remaining = openTabs.filter((id) => id !== sessionId);\n setOpenTabs(remaining);\n\n // If closing the current session, switch to another open one\n if (sessionId === currentSessionId && remaining.length > 0) {\n switchSession(remaining[0]!);\n }\n },\n [openTabs, currentSessionId, switchSession, setOpenTabs],\n );\n\n const handleDelete = useCallback(\n (sessionId: string) => {\n const session = sessions.find((s) => s.id === sessionId);\n // If session is empty (no messages), delete without confirmation\n if (!session?.uiMessages?.length) {\n deleteSession(sessionId);\n } else {\n setSessionToDelete(sessionId);\n }\n },\n [sessions, deleteSession],\n );\n\n const handleConfirmDelete = useCallback(() => {\n if (sessionToDelete) {\n deleteSession(sessionToDelete);\n setSessionToDelete(null);\n }\n }, [sessionToDelete, deleteSession]);\n\n const handleCloseDeleteDialog = useCallback(() => {\n setSessionToDelete(null);\n }, []);\n\n const handleRenameRequest = useCallback(\n (sessionId: string) => {\n const session = sessions.find((s) => s.id === sessionId);\n if (session) {\n setSessionToRename({id: sessionId, name: session.name});\n }\n },\n [sessions],\n );\n\n const handleFinishRename = useCallback(\n (newName: string) => {\n if (sessionToRename) {\n renameSession(sessionToRename.id, newName);\n }\n setSessionToRename(null);\n },\n [sessionToRename, renameSession],\n );\n\n // Memoize render functions to prevent unnecessary re-renders\n const renderTabLabel = useCallback(\n (tab: {id: string; name: string}) => (\n <div className=\"flex items-center gap-2\">\n {getIsSessionRunning(tab.id) && (\n <Spinner className=\"text-muted-foreground h-4 w-4 shrink-0 animate-spin\" />\n )}\n <span className=\"truncate\">{tab.name}</span>\n </div>\n ),\n [getIsSessionRunning],\n );\n\n const renderTabMenu = useCallback(\n (tab: {id: string; name: string}) => (\n <>\n <TabStrip.MenuItem onClick={() => handleRenameRequest(tab.id)}>\n <PencilIcon className=\"mr-2 h-4 w-4\" />\n Rename\n </TabStrip.MenuItem>\n {sessions.length > 1 && (\n <>\n <TabStrip.MenuSeparator />\n <TabStrip.MenuItem\n variant=\"destructive\"\n onClick={() => handleDelete(tab.id)}\n >\n <TrashIcon className=\"mr-2 h-4 w-4\" />\n Delete\n </TabStrip.MenuItem>\n </>\n )}\n </>\n ),\n [handleRenameRequest, handleDelete, sessions.length],\n );\n\n const renderSearchItemActions = useCallback(\n (tab: {id: string; name: string}) => (\n <>\n <TabStrip.SearchItemAction\n icon={<PencilIcon className=\"h-3 w-3\" />}\n aria-label={`Rename ${tab.name}`}\n onClick={() => handleRenameRequest(tab.id)}\n />\n {sessions.length > 1 && (\n <TabStrip.SearchItemAction\n icon={<TrashIcon className=\"h-3 w-3\" />}\n aria-label={`Delete ${tab.name}`}\n onClick={() => handleDelete(tab.id)}\n />\n )}\n </>\n ),\n [handleRenameRequest, handleDelete, sessions.length],\n );\n\n return (\n <>\n <div\n className={cn(\n 'flex w-full flex-wrap items-center justify-between gap-2 overflow-visible',\n className,\n )}\n >\n <div className=\"flex w-full min-w-0 items-center gap-3\">\n <TabStrip\n className=\"bg-background\"\n tabs={tabs}\n preventCloseLastTab\n openTabs={openTabs}\n selectedTabId={currentSessionId}\n onClose={handleClose}\n onOpenTabsChange={setOpenTabs}\n onSelect={switchSession}\n onCreate={() => createSession()}\n onRename={renameSession}\n renderTabLabel={renderTabLabel}\n renderTabMenu={renderTabMenu}\n renderSearchItemActions={renderSearchItemActions}\n >\n <TabStrip.SearchDropdown\n triggerIcon={<HistoryIcon className=\"h-4 w-4\" />}\n tooltip=\"Session history\"\n sortSearchItems=\"recent\"\n getTabLastOpenedAt={(tab) =>\n tab.lastOpenedAt as number | undefined\n }\n />\n <TabStrip.Tabs tabClassName=\"rounded-md data-[state=active]:bg-muted\" />\n <TabStrip.NewButton tooltip=\"New session\" />\n </TabStrip>\n </div>\n\n {children}\n </div>\n\n <DeleteSessionDialog\n isOpen={sessionToDelete !== null}\n onClose={handleCloseDeleteDialog}\n onDelete={handleConfirmDelete}\n />\n <RenameSessionDialog\n isOpen={sessionToRename !== null}\n onClose={() => setSessionToRename(null)}\n initialName={sessionToRename?.name ?? ''}\n onRename={handleFinishRename}\n />\n </>\n );\n};\n"]}
@@ -17,6 +17,6 @@ import { cn } from '@sqlrooms/ui';
17
17
  */
18
18
  export const ToolCallInfo = ({ toolName, input, isCompleted, state, }) => {
19
19
  const [isExpanded, setIsExpanded] = useState(false);
20
- return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2 pl-2 text-xs text-gray-700 dark:text-gray-300", children: [state !== 'output-available' && state !== 'output-error' ? (_jsx(Loader2, { className: "h-4 w-4 shrink-0 animate-spin text-gray-400 dark:text-gray-500" })) : (_jsx(CircleArrowRightIcon, { className: "h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500" })), _jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: cn('flex items-center gap-1 rounded px-1 py-0.5 font-medium transition-colors', 'text-gray-700'), children: [isExpanded ? (_jsx(ChevronDownIcon, { className: "h-3 w-3" })) : (_jsx(ChevronRightIcon, { className: "h-3 w-3" })), _jsx("span", { children: toolName })] })] }), isExpanded && (_jsx("div", { className: "px-5 py-2", children: _jsx("pre", { className: "text-muted-foreground bg-muted m-0 max-h-24 overflow-auto whitespace-pre-wrap break-words rounded-md p-2 font-mono text-xs", children: JSON.stringify(input, null, 2) }) }))] }));
20
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-2 pl-2 text-xs text-gray-700 dark:text-gray-300", children: [state !== 'output-available' && state !== 'output-error' ? (_jsx(Loader2, { className: "h-4 w-4 shrink-0 animate-spin text-gray-400 dark:text-gray-500" })) : (_jsx(CircleArrowRightIcon, { className: "h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500" })), _jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: cn('flex items-center gap-1 rounded px-1 py-0.5 font-medium transition-colors', 'text-gray-700'), children: [isExpanded ? (_jsx(ChevronDownIcon, { className: "h-3 w-3" })) : (_jsx(ChevronRightIcon, { className: "h-3 w-3" })), _jsx("span", { children: toolName })] })] }), isExpanded && (_jsx("div", { className: "px-5 py-2", children: _jsx("pre", { className: "text-muted-foreground bg-muted m-0 max-h-24 overflow-auto rounded-md p-2 font-mono text-xs wrap-break-word whitespace-pre-wrap", children: JSON.stringify(input, null, 2) }) }))] }));
21
21
  };
22
22
  //# sourceMappingURL=ToolCallInfo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCallInfo.js","sourceRoot":"","sources":["../../src/components/ToolCallInfo.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAgBhC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACxD,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,GACN,EAAE,EAAE;IACH,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpD,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,eAAK,SAAS,EAAC,uEAAuE,aAEnF,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,cAAc,CAAC,CAAC,CAAC,CAC1D,KAAC,OAAO,IAAC,SAAS,EAAC,gEAAgE,GAAG,CACvF,CAAC,CAAC,CAAC,CACF,KAAC,oBAAoB,IAAC,SAAS,EAAC,mDAAmD,GAAG,CACvF,EAGD,kBACE,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,UAAU,CAAC,EACzC,SAAS,EAAE,EAAE,CACX,2EAA2E,EAC3E,eAAe,CAChB,aAEA,UAAU,CAAC,CAAC,CAAC,CACZ,KAAC,eAAe,IAAC,SAAS,EAAC,SAAS,GAAG,CACxC,CAAC,CAAC,CAAC,CACF,KAAC,gBAAgB,IAAC,SAAS,EAAC,SAAS,GAAG,CACzC,EACD,yBAAO,QAAQ,GAAQ,IAChB,IACL,EAGL,UAAU,IAAI,CACb,cAAK,SAAS,EAAC,WAAW,YACxB,cAAK,SAAS,EAAC,4HAA4H,YACxI,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAC3B,GACF,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {\n CircleArrowRightIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n Loader2,\n} from 'lucide-react';\nimport {useState} from 'react';\nimport {cn} from '@sqlrooms/ui';\n\n/**\n * Props for the ToolCallInfoBox component\n */\ntype ToolCallInfoProps = {\n toolName: string;\n input: unknown;\n isCompleted: boolean;\n state:\n | 'input-streaming'\n | 'input-available'\n | 'output-available'\n | 'output-error';\n};\n\n/**\n * Component that renders a tool call is running\n * Shows the tool name and truncated arguments by default.\n * Click on the tool name to expand and see full arguments.\n *\n * @component\n * @param props - Component props\n * @param props.toolName - Name of the tool being called\n * @param props.input - Input arguments passed to the tool\n * @param props.isCompleted - Whether the tool call has completed\n * @param props.state - Current state of the tool call\n * @returns A React component displaying the tool call details\n */\nexport const ToolCallInfo: React.FC<ToolCallInfoProps> = ({\n toolName,\n input,\n isCompleted,\n state,\n}) => {\n const [isExpanded, setIsExpanded] = useState(false);\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-2 pl-2 text-xs text-gray-700 dark:text-gray-300\">\n {/* Loading/Completed Indicator */}\n {state !== 'output-available' && state !== 'output-error' ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin text-gray-400 dark:text-gray-500\" />\n ) : (\n <CircleArrowRightIcon className=\"h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500\" />\n )}\n\n {/* Clickable Tool Name */}\n <button\n onClick={() => setIsExpanded(!isExpanded)}\n className={cn(\n 'flex items-center gap-1 rounded px-1 py-0.5 font-medium transition-colors',\n 'text-gray-700',\n )}\n >\n {isExpanded ? (\n <ChevronDownIcon className=\"h-3 w-3\" />\n ) : (\n <ChevronRightIcon className=\"h-3 w-3\" />\n )}\n <span>{toolName}</span>\n </button>\n </div>\n\n {/* Expanded Arguments */}\n {isExpanded && (\n <div className=\"px-5 py-2\">\n <pre className=\"text-muted-foreground bg-muted m-0 max-h-24 overflow-auto whitespace-pre-wrap break-words rounded-md p-2 font-mono text-xs\">\n {JSON.stringify(input, null, 2)}\n </pre>\n </div>\n )}\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"ToolCallInfo.js","sourceRoot":"","sources":["../../src/components/ToolCallInfo.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAgBhC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACxD,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,GACN,EAAE,EAAE;IACH,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpD,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,eAAK,SAAS,EAAC,uEAAuE,aAEnF,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,cAAc,CAAC,CAAC,CAAC,CAC1D,KAAC,OAAO,IAAC,SAAS,EAAC,gEAAgE,GAAG,CACvF,CAAC,CAAC,CAAC,CACF,KAAC,oBAAoB,IAAC,SAAS,EAAC,mDAAmD,GAAG,CACvF,EAGD,kBACE,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,UAAU,CAAC,EACzC,SAAS,EAAE,EAAE,CACX,2EAA2E,EAC3E,eAAe,CAChB,aAEA,UAAU,CAAC,CAAC,CAAC,CACZ,KAAC,eAAe,IAAC,SAAS,EAAC,SAAS,GAAG,CACxC,CAAC,CAAC,CAAC,CACF,KAAC,gBAAgB,IAAC,SAAS,EAAC,SAAS,GAAG,CACzC,EACD,yBAAO,QAAQ,GAAQ,IAChB,IACL,EAGL,UAAU,IAAI,CACb,cAAK,SAAS,EAAC,WAAW,YACxB,cAAK,SAAS,EAAC,gIAAgI,YAC5I,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAC3B,GACF,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {\n CircleArrowRightIcon,\n ChevronDownIcon,\n ChevronRightIcon,\n Loader2,\n} from 'lucide-react';\nimport {useState} from 'react';\nimport {cn} from '@sqlrooms/ui';\n\n/**\n * Props for the ToolCallInfoBox component\n */\ntype ToolCallInfoProps = {\n toolName: string;\n input: unknown;\n isCompleted: boolean;\n state:\n | 'input-streaming'\n | 'input-available'\n | 'output-available'\n | 'output-error';\n};\n\n/**\n * Component that renders a tool call is running\n * Shows the tool name and truncated arguments by default.\n * Click on the tool name to expand and see full arguments.\n *\n * @component\n * @param props - Component props\n * @param props.toolName - Name of the tool being called\n * @param props.input - Input arguments passed to the tool\n * @param props.isCompleted - Whether the tool call has completed\n * @param props.state - Current state of the tool call\n * @returns A React component displaying the tool call details\n */\nexport const ToolCallInfo: React.FC<ToolCallInfoProps> = ({\n toolName,\n input,\n isCompleted,\n state,\n}) => {\n const [isExpanded, setIsExpanded] = useState(false);\n\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-2 pl-2 text-xs text-gray-700 dark:text-gray-300\">\n {/* Loading/Completed Indicator */}\n {state !== 'output-available' && state !== 'output-error' ? (\n <Loader2 className=\"h-4 w-4 shrink-0 animate-spin text-gray-400 dark:text-gray-500\" />\n ) : (\n <CircleArrowRightIcon className=\"h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500\" />\n )}\n\n {/* Clickable Tool Name */}\n <button\n onClick={() => setIsExpanded(!isExpanded)}\n className={cn(\n 'flex items-center gap-1 rounded px-1 py-0.5 font-medium transition-colors',\n 'text-gray-700',\n )}\n >\n {isExpanded ? (\n <ChevronDownIcon className=\"h-3 w-3\" />\n ) : (\n <ChevronRightIcon className=\"h-3 w-3\" />\n )}\n <span>{toolName}</span>\n </button>\n </div>\n\n {/* Expanded Arguments */}\n {isExpanded && (\n <div className=\"px-5 py-2\">\n <pre className=\"text-muted-foreground bg-muted m-0 max-h-24 overflow-auto rounded-md p-2 font-mono text-xs wrap-break-word whitespace-pre-wrap\">\n {JSON.stringify(input, null, 2)}\n </pre>\n </div>\n )}\n </div>\n );\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/ai-core",
3
- "version": "0.27.0-rc.5",
3
+ "version": "0.28.0-rc.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/index.js",
@@ -23,11 +23,11 @@
23
23
  "@ai-sdk/react": "^2.0.44",
24
24
  "@openassistant/utils": "1.0.0-alpha.0",
25
25
  "@paralleldrive/cuid2": "^3.0.0",
26
- "@sqlrooms/ai-config": "0.27.0-rc.5",
27
- "@sqlrooms/monaco-editor": "0.27.0-rc.5",
28
- "@sqlrooms/room-store": "0.27.0-rc.5",
29
- "@sqlrooms/ui": "0.27.0-rc.5",
30
- "@sqlrooms/utils": "0.27.0-rc.5",
26
+ "@sqlrooms/ai-config": "0.28.0-rc.0",
27
+ "@sqlrooms/monaco-editor": "0.28.0-rc.0",
28
+ "@sqlrooms/room-store": "0.28.0-rc.0",
29
+ "@sqlrooms/ui": "0.28.0-rc.0",
30
+ "@sqlrooms/utils": "0.28.0-rc.0",
31
31
  "ai": "^5.0.44",
32
32
  "immer": "^11.0.1",
33
33
  "lucide-react": "^0.556.0",
@@ -47,5 +47,5 @@
47
47
  "typecheck": "tsc --noEmit",
48
48
  "typedoc": "typedoc"
49
49
  },
50
- "gitHead": "d860b9100d63e0d4029712872741abfa3a9c31a1"
50
+ "gitHead": "87a478edbff690e04c38cc717db8e11e844565c8"
51
51
  }