@sqlrooms/ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/dist/AiSlice.d.ts +366 -0
- package/dist/AiSlice.d.ts.map +1 -0
- package/dist/AiSlice.js +191 -0
- package/dist/AnalysisResult.d.ts +7 -0
- package/dist/AnalysisResult.d.ts.map +1 -0
- package/dist/AnalysisResult.js +10 -0
- package/dist/AnalysisResultsContainer.d.ts +2 -0
- package/dist/AnalysisResultsContainer.d.ts.map +1 -0
- package/dist/AnalysisResultsContainer.js +14 -0
- package/dist/QueryControls.d.ts +6 -0
- package/dist/QueryControls.d.ts.map +1 -0
- package/dist/QueryControls.js +36 -0
- package/dist/ToolCall.d.ts +7 -0
- package/dist/ToolCall.d.ts.map +1 -0
- package/dist/ToolCall.js +13 -0
- package/dist/ToolResult.d.ts +7 -0
- package/dist/ToolResult.d.ts.map +1 -0
- package/dist/ToolResult.js +8 -0
- package/dist/analysis.d.ts +143 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +87 -0
- package/dist/hooks/use-scroll-to-bottom.d.ts +7 -0
- package/dist/hooks/use-scroll-to-bottom.d.ts.map +1 -0
- package/dist/hooks/use-scroll-to-bottom.js +50 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/schemas.d.ts +354 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +43 -0
- package/package.json +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolCall.d.ts","sourceRoot":"","sources":["../src/ToolCall.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;AAezC,UAAU,aAAa;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAC,QAAQ,EAAC,EAAE,aAAa,2CA6EjD"}
|
package/dist/ToolCall.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Button, Popover, PopoverContent, PopoverTrigger, Spinner, cn, } from '@sqlrooms/ui';
|
|
3
|
+
import { CodeIcon } from 'lucide-react';
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import Markdown from 'react-markdown';
|
|
6
|
+
import { VegaLiteChart } from '@sqlrooms/vega';
|
|
7
|
+
export function ToolCall({ toolCall }) {
|
|
8
|
+
const { args, toolName, toolCallId } = toolCall;
|
|
9
|
+
const { type } = args;
|
|
10
|
+
return (_jsxs("div", { className: cn('border-2 relative bg-gray-900 px-5 py-6 rounded-md text-gray-300 text-xs', {
|
|
11
|
+
' border-blue-500': toolName === 'answer',
|
|
12
|
+
}), children: [_jsx(Badge, { variant: "secondary", className: cn('text-xs absolute top-[-12px] left-2 text-gray-100', toolName === 'answer' && 'bg-blue-500'), children: toolName }), _jsx("div", { className: "absolute top-2 right-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[400px] overflow-auto", align: "start", side: "right", children: _jsx("pre", { className: "text-xs", children: JSON.stringify(toolCall, null, 2) }) })] }) }), type === 'query' ? (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-xs text-gray-400", children: args.reasoning }), _jsx("div", { className: "font-mono", children: args.sqlQuery })] })) : type === 'answer' ? (_jsxs("div", { className: "flex flex-col gap-5", children: [_jsx("div", { className: "text-sm", children: _jsx(Markdown, { children: args.answer }) }), args.chart && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "text-xs text-muted-foreground font-mono", children: args.chart.sqlQuery }), _jsx(Suspense, { fallback: _jsx("div", { className: "w-full h-full flex items-center justify-center", children: _jsx(Spinner, { className: "w-4 h-4" }) }), children: _jsx(VegaLiteChart, { width: 400, height: 250, sqlQuery: args.chart.sqlQuery, spec: args.chart.vegaLiteSpec }) })] }))] })) : (_jsx("pre", { children: JSON.stringify(args, null, 2) }))] }, toolCallId));
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolResult.d.ts","sourceRoot":"","sources":["../src/ToolResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAW3C,UAAU,eAAe;IACvB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2DhD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Button, Popover, PopoverContent, PopoverTrigger, cn, } from '@sqlrooms/ui';
|
|
3
|
+
import { CheckCircle2Icon, CodeIcon, XCircleIcon } from 'lucide-react';
|
|
4
|
+
export const ToolResult = ({ toolResult }) => {
|
|
5
|
+
const { toolName, args, result } = toolResult;
|
|
6
|
+
const isSuccess = result.success;
|
|
7
|
+
return (_jsxs("div", { className: cn('border-2 relative bg-gray-900 px-5 py-6 rounded-md text-gray-300 text-xs', isSuccess ? 'border-green-500' : 'border-red-500'), children: [_jsxs(Badge, { variant: "secondary", className: cn('text-xs absolute top-[-12px] left-2 text-gray-100 flex items-center gap-1 border', isSuccess ? 'border-green-500' : 'border-red-500'), children: [isSuccess ? (_jsx(CheckCircle2Icon, { className: "w-3 h-3 text-green-500" })) : (_jsx(XCircleIcon, { className: "w-3 h-3 text-red-500" })), toolName] }), _jsx("div", { className: "absolute top-2 right-2", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", size: "icon", className: "w-6 h-6", children: _jsx(CodeIcon, { className: "w-4 h-4" }) }) }), _jsx(PopoverContent, { className: "w-[400px] max-h-[300px] overflow-auto p-4", side: "right", align: "start", children: _jsx("pre", { className: "whitespace-no-wrap text-xs", children: JSON.stringify(toolResult, null, 2) }) })] }) }), _jsxs("div", { className: "flex flex-col gap-5", children: [args.reasoning && (_jsx("div", { className: "text-xs text-gray-400", children: args.reasoning })), args.sqlQuery && _jsx("div", { className: "font-mono", children: args.sqlQuery }), !result.success && (_jsxs("div", { className: "text-red-500 gap-2 flex flex-col", children: [_jsx("p", { className: "text-sm font-bold", children: "Oops! Something went wrong..." }), _jsx("p", { className: "text-xs", children: result.error })] }))] })] }));
|
|
8
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { LanguageModelV1 } from '@ai-sdk/provider';
|
|
2
|
+
import { StepResult, ToolExecutionOptions } from 'ai';
|
|
3
|
+
/**
|
|
4
|
+
* Run analysis on the project data
|
|
5
|
+
* @param prompt - The prompt for the analysis
|
|
6
|
+
* @param abortSignal - An optional abort signal to cancel the analysis
|
|
7
|
+
* @returns The tool calls and the final answer
|
|
8
|
+
*/
|
|
9
|
+
export declare function runAnalysis({ model, abortSignal, onStepFinish, maxSteps, messages, }: {
|
|
10
|
+
abortSignal?: AbortSignal;
|
|
11
|
+
onStepFinish?: (event: StepResult<typeof TOOLS>) => Promise<void> | void;
|
|
12
|
+
apiKey: string;
|
|
13
|
+
model: LanguageModelV1;
|
|
14
|
+
maxSteps?: number;
|
|
15
|
+
messages?: any[];
|
|
16
|
+
}): Promise<import("ai").GenerateTextResult<{
|
|
17
|
+
query: import("ai").Tool<import("zod").ZodObject<{
|
|
18
|
+
type: import("zod").ZodLiteral<"query">;
|
|
19
|
+
sqlQuery: import("zod").ZodString;
|
|
20
|
+
reasoning: import("zod").ZodString;
|
|
21
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
22
|
+
type: "query";
|
|
23
|
+
sqlQuery: string;
|
|
24
|
+
reasoning: string;
|
|
25
|
+
}, {
|
|
26
|
+
type: "query";
|
|
27
|
+
sqlQuery: string;
|
|
28
|
+
reasoning: string;
|
|
29
|
+
}>, {
|
|
30
|
+
success: false;
|
|
31
|
+
error: string;
|
|
32
|
+
} | {
|
|
33
|
+
success: true;
|
|
34
|
+
data: Record<string, any>;
|
|
35
|
+
}> & {
|
|
36
|
+
execute: (args: {
|
|
37
|
+
type: "query";
|
|
38
|
+
sqlQuery: string;
|
|
39
|
+
reasoning: string;
|
|
40
|
+
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
41
|
+
success: false;
|
|
42
|
+
error: string;
|
|
43
|
+
} | {
|
|
44
|
+
success: true;
|
|
45
|
+
data: Record<string, any>;
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
48
|
+
answer: import("ai").Tool<import("zod").ZodObject<{
|
|
49
|
+
type: import("zod").ZodLiteral<"answer">;
|
|
50
|
+
answer: import("zod").ZodString;
|
|
51
|
+
chart: import("zod").ZodUnion<[import("zod").ZodObject<{
|
|
52
|
+
sqlQuery: import("zod").ZodString;
|
|
53
|
+
vegaLiteSpec: import("zod").ZodString;
|
|
54
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
55
|
+
sqlQuery: string;
|
|
56
|
+
vegaLiteSpec: string;
|
|
57
|
+
}, {
|
|
58
|
+
sqlQuery: string;
|
|
59
|
+
vegaLiteSpec: string;
|
|
60
|
+
}>, import("zod").ZodNull]>;
|
|
61
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
62
|
+
type: "answer";
|
|
63
|
+
answer: string;
|
|
64
|
+
chart: {
|
|
65
|
+
sqlQuery: string;
|
|
66
|
+
vegaLiteSpec: string;
|
|
67
|
+
} | null;
|
|
68
|
+
}, {
|
|
69
|
+
type: "answer";
|
|
70
|
+
answer: string;
|
|
71
|
+
chart: {
|
|
72
|
+
sqlQuery: string;
|
|
73
|
+
vegaLiteSpec: string;
|
|
74
|
+
} | null;
|
|
75
|
+
}>, unknown> & {
|
|
76
|
+
execute: undefined;
|
|
77
|
+
};
|
|
78
|
+
}, never>>;
|
|
79
|
+
declare const TOOLS: {
|
|
80
|
+
query: import("ai").Tool<import("zod").ZodObject<{
|
|
81
|
+
type: import("zod").ZodLiteral<"query">;
|
|
82
|
+
sqlQuery: import("zod").ZodString;
|
|
83
|
+
reasoning: import("zod").ZodString;
|
|
84
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
85
|
+
type: "query";
|
|
86
|
+
sqlQuery: string;
|
|
87
|
+
reasoning: string;
|
|
88
|
+
}, {
|
|
89
|
+
type: "query";
|
|
90
|
+
sqlQuery: string;
|
|
91
|
+
reasoning: string;
|
|
92
|
+
}>, {
|
|
93
|
+
success: false;
|
|
94
|
+
error: string;
|
|
95
|
+
} | {
|
|
96
|
+
success: true;
|
|
97
|
+
data: Record<string, any>;
|
|
98
|
+
}> & {
|
|
99
|
+
execute: (args: {
|
|
100
|
+
type: "query";
|
|
101
|
+
sqlQuery: string;
|
|
102
|
+
reasoning: string;
|
|
103
|
+
}, options: ToolExecutionOptions) => PromiseLike<{
|
|
104
|
+
success: false;
|
|
105
|
+
error: string;
|
|
106
|
+
} | {
|
|
107
|
+
success: true;
|
|
108
|
+
data: Record<string, any>;
|
|
109
|
+
}>;
|
|
110
|
+
};
|
|
111
|
+
answer: import("ai").Tool<import("zod").ZodObject<{
|
|
112
|
+
type: import("zod").ZodLiteral<"answer">;
|
|
113
|
+
answer: import("zod").ZodString;
|
|
114
|
+
chart: import("zod").ZodUnion<[import("zod").ZodObject<{
|
|
115
|
+
sqlQuery: import("zod").ZodString;
|
|
116
|
+
vegaLiteSpec: import("zod").ZodString;
|
|
117
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
118
|
+
sqlQuery: string;
|
|
119
|
+
vegaLiteSpec: string;
|
|
120
|
+
}, {
|
|
121
|
+
sqlQuery: string;
|
|
122
|
+
vegaLiteSpec: string;
|
|
123
|
+
}>, import("zod").ZodNull]>;
|
|
124
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
125
|
+
type: "answer";
|
|
126
|
+
answer: string;
|
|
127
|
+
chart: {
|
|
128
|
+
sqlQuery: string;
|
|
129
|
+
vegaLiteSpec: string;
|
|
130
|
+
} | null;
|
|
131
|
+
}, {
|
|
132
|
+
type: "answer";
|
|
133
|
+
answer: string;
|
|
134
|
+
chart: {
|
|
135
|
+
sqlQuery: string;
|
|
136
|
+
vegaLiteSpec: string;
|
|
137
|
+
} | null;
|
|
138
|
+
}>, unknown> & {
|
|
139
|
+
execute: undefined;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
export {};
|
|
143
|
+
//# sourceMappingURL=analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAe,UAAU,EAAQ,oBAAoB,EAAC,MAAM,IAAI,CAAC;AAOxE;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,EAChC,KAAK,EAEL,WAAW,EACX,YAAY,EACZ,QAAc,EACd,QAAQ,GACT,EAAE;IAED,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;CAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA+BA;AAED,QAAA,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DV,CAAC"}
|
package/dist/analysis.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { arrowTableToJson, getDuckDb } from '@sqlrooms/duckdb';
|
|
2
|
+
import { generateText, tool } from 'ai';
|
|
3
|
+
import { AnswerToolParameters, QueryToolParameters, } from './schemas';
|
|
4
|
+
/**
|
|
5
|
+
* Run analysis on the project data
|
|
6
|
+
* @param prompt - The prompt for the analysis
|
|
7
|
+
* @param abortSignal - An optional abort signal to cancel the analysis
|
|
8
|
+
* @returns The tool calls and the final answer
|
|
9
|
+
*/
|
|
10
|
+
export async function runAnalysis({ model,
|
|
11
|
+
// prompt,
|
|
12
|
+
abortSignal, onStepFinish, maxSteps = 100, messages, }) {
|
|
13
|
+
const result = await generateText({
|
|
14
|
+
model,
|
|
15
|
+
abortSignal,
|
|
16
|
+
// prompt,
|
|
17
|
+
messages,
|
|
18
|
+
tools: TOOLS,
|
|
19
|
+
toolChoice: 'required',
|
|
20
|
+
maxSteps,
|
|
21
|
+
maxRetries: 1,
|
|
22
|
+
system: 'You are analyzing tables in DuckDB database in the context of a project. ' +
|
|
23
|
+
'You can run SQL queries to perform analysis and answer questions. ' +
|
|
24
|
+
'Reason step by step. ' +
|
|
25
|
+
'When you give the final answer, provide an explanation for how you got it.',
|
|
26
|
+
onStepFinish,
|
|
27
|
+
});
|
|
28
|
+
// const answer = result.toolCalls.find((t) => t.toolName === 'answer');
|
|
29
|
+
// if (!answer) {
|
|
30
|
+
// console.error('No answer tool call found', {result});
|
|
31
|
+
// throw new Error('No answer tool call found');
|
|
32
|
+
// }
|
|
33
|
+
// return answer.args;
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
const TOOLS = {
|
|
37
|
+
query: tool({
|
|
38
|
+
description: 'A tool for executing SQL queries in DuckDB that is embedded in browser using duckdb-wasm. ' +
|
|
39
|
+
'You can obtain the structures of all tables and their column types by running `DESCRIBE`. ' +
|
|
40
|
+
'Query results are returned as a json object `{success: boolean, data: object[], error?: string}`. ' +
|
|
41
|
+
'You should only analyze tables which are in the main schema. ' +
|
|
42
|
+
'Avoid queries returning too much data to prevent the browser from crashing. ' +
|
|
43
|
+
'Include VegaLite charts in your response if the data is suitable for it. ' +
|
|
44
|
+
'Omit the data from the chart.vegaLiteSpec in the response, provide an sql query in chart.sqlQuery instead. ' +
|
|
45
|
+
'To obtain stats, use the `SUMMARIZE table_name` query. ' +
|
|
46
|
+
"Don't execute queries that modify data unless explicitly asked. ",
|
|
47
|
+
parameters: QueryToolParameters,
|
|
48
|
+
execute: async ({ sqlQuery }, options) => {
|
|
49
|
+
try {
|
|
50
|
+
const { conn } = await getDuckDb();
|
|
51
|
+
// TODO use options.abortSignal: maybe call db.cancelPendingQuery
|
|
52
|
+
const result = await conn.query(sqlQuery);
|
|
53
|
+
// if (options.abortSignal?.aborted) {
|
|
54
|
+
// throw new Error('Query aborted');
|
|
55
|
+
// }
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
data: arrowTableToJson(result),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('SQL query error:', error);
|
|
63
|
+
const errorMessage = error instanceof Error
|
|
64
|
+
? error.cause instanceof Error
|
|
65
|
+
? error.cause.message
|
|
66
|
+
: error.message
|
|
67
|
+
: String(error);
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: errorMessage,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
// TODO: consider experimental_toToolResultContent() for returning Arrow
|
|
75
|
+
}),
|
|
76
|
+
// answer tool: the LLM will provide a structured answer
|
|
77
|
+
answer: tool({
|
|
78
|
+
description: 'A tool for providing the final answer.',
|
|
79
|
+
parameters: AnswerToolParameters,
|
|
80
|
+
// execute: async ({answer}): Promise<ToolResultSchema['result']> => {
|
|
81
|
+
// return {
|
|
82
|
+
// success: true,
|
|
83
|
+
// data: answer,
|
|
84
|
+
// };
|
|
85
|
+
// },
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
export declare function useScrollToBottom<T extends HTMLElement>(options?: MutationObserverInit): [RefObject<T>, RefObject<T>];
|
|
3
|
+
export declare function useScrollToBottomButton(containerRef: RefObject<HTMLElement>): {
|
|
4
|
+
showButton: boolean;
|
|
5
|
+
scrollToBottom: () => void;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=use-scroll-to-bottom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-scroll-to-bottom.d.ts","sourceRoot":"","sources":["../../src/hooks/use-scroll-to-bottom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAElE,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,OAAO,GAAE,oBAKR,GACA,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAoB9B;AAED,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,SAAS,CAAC,WAAW,CAAC;;;EAiC3E"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
export function useScrollToBottom(options = {
|
|
3
|
+
childList: true,
|
|
4
|
+
subtree: true,
|
|
5
|
+
characterData: true,
|
|
6
|
+
// attributes: true,
|
|
7
|
+
}) {
|
|
8
|
+
const containerRef = useRef(null);
|
|
9
|
+
const endRef = useRef(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const container = containerRef.current;
|
|
12
|
+
const end = endRef.current;
|
|
13
|
+
if (container && end) {
|
|
14
|
+
const observer = new MutationObserver(() => {
|
|
15
|
+
end.scrollIntoView({ behavior: 'instant', block: 'end' });
|
|
16
|
+
});
|
|
17
|
+
observer.observe(container, options);
|
|
18
|
+
return () => observer.disconnect();
|
|
19
|
+
}
|
|
20
|
+
}, [options]);
|
|
21
|
+
return [containerRef, endRef];
|
|
22
|
+
}
|
|
23
|
+
export function useScrollToBottomButton(containerRef) {
|
|
24
|
+
const [showButton, setShowButton] = useState(false);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const container = containerRef.current;
|
|
27
|
+
if (!container)
|
|
28
|
+
return;
|
|
29
|
+
const handleScroll = () => {
|
|
30
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
31
|
+
// Only show if we're scrolled up more than 200px from the bottom
|
|
32
|
+
const isNotAtBottom = scrollHeight - scrollTop - clientHeight > 200;
|
|
33
|
+
setShowButton(isNotAtBottom);
|
|
34
|
+
};
|
|
35
|
+
container.addEventListener('scroll', handleScroll);
|
|
36
|
+
// Initial check with a slight delay to ensure proper measurement
|
|
37
|
+
const timeoutId = setTimeout(handleScroll, 100);
|
|
38
|
+
return () => {
|
|
39
|
+
container.removeEventListener('scroll', handleScroll);
|
|
40
|
+
clearTimeout(timeoutId);
|
|
41
|
+
};
|
|
42
|
+
}, [containerRef]);
|
|
43
|
+
const scrollToBottom = () => {
|
|
44
|
+
containerRef.current?.scrollTo({
|
|
45
|
+
top: containerRef.current.scrollHeight,
|
|
46
|
+
behavior: 'smooth',
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
return { showButton, scrollToBottom };
|
|
50
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AiSliceConfig, createAiSlice, useStoreWithAi as useAiStore, createDefaultAiConfig, } from './AiSlice';
|
|
2
|
+
export type { AiSliceState } from './AiSlice';
|
|
3
|
+
export { QueryControls } from './QueryControls';
|
|
4
|
+
export { AnalysisResultsContainer } from './AnalysisResultsContainer';
|
|
5
|
+
export { AnalysisResult } from './AnalysisResult';
|
|
6
|
+
export { useScrollToBottom, useScrollToBottomButton, } from './hooks/use-scroll-to-bottom';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,cAAc,IAAI,UAAU,EAC5B,qBAAqB,GACtB,MAAM,WAAW,CAAC;AAEnB,YAAY,EAAC,YAAY,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,wBAAwB,EAAC,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AiSliceConfig, createAiSlice, useStoreWithAi as useAiStore, createDefaultAiConfig, } from './AiSlice';
|
|
2
|
+
export { QueryControls } from './QueryControls';
|
|
3
|
+
export { AnalysisResultsContainer } from './AnalysisResultsContainer';
|
|
4
|
+
export { AnalysisResult } from './AnalysisResult';
|
|
5
|
+
export { useScrollToBottom, useScrollToBottomButton, } from './hooks/use-scroll-to-bottom';
|