@databricks/appkit-ui 0.21.0 → 0.22.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/CLAUDE.md +1 -0
- package/README.md +3 -20
- package/dist/cli/commands/setup.js +2 -2
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/js/config.d.ts +24 -0
- package/dist/js/config.d.ts.map +1 -0
- package/dist/js/config.js +49 -0
- package/dist/js/config.js.map +1 -0
- package/dist/js/index.d.ts +2 -1
- package/dist/js/index.js +2 -1
- package/dist/react/genie/genie-chat-message-list.d.ts.map +1 -1
- package/dist/react/genie/genie-chat-message-list.js +5 -4
- package/dist/react/genie/genie-chat-message-list.js.map +1 -1
- package/dist/react/genie/genie-chat-message.d.ts.map +1 -1
- package/dist/react/genie/genie-chat-message.js +7 -6
- package/dist/react/genie/genie-chat-message.js.map +1 -1
- package/dist/react/genie/genie-query-visualization.d.ts.map +1 -1
- package/dist/react/genie/genie-query-visualization.js +4 -3
- package/dist/react/genie/genie-query-visualization.js.map +1 -1
- package/dist/react/genie/index.d.ts +1 -1
- package/dist/react/genie/types.d.ts +2 -1
- package/dist/react/genie/types.d.ts.map +1 -1
- package/dist/react/genie/types.js +6 -0
- package/dist/react/genie/types.js.map +1 -0
- package/dist/react/genie/use-genie-chat.d.ts.map +1 -1
- package/dist/react/genie/use-genie-chat.js +60 -23
- package/dist/react/genie/use-genie-chat.js.map +1 -1
- package/dist/react/hooks/index.d.ts +2 -1
- package/dist/react/hooks/index.js +1 -0
- package/dist/react/hooks/use-plugin-config.d.ts +25 -0
- package/dist/react/hooks/use-plugin-config.d.ts.map +1 -0
- package/dist/react/hooks/use-plugin-config.js +32 -0
- package/dist/react/hooks/use-plugin-config.js.map +1 -0
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +2 -1
- package/dist/shared/src/index.d.ts +1 -1
- package/dist/shared/src/plugin.d.ts +12 -1
- package/dist/shared/src/plugin.d.ts.map +1 -0
- package/docs/api/appkit/Class.Plugin.md +75 -17
- package/docs/app-management.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/development/ai-assisted-development.md +2 -2
- package/docs/development/local-development.md +1 -1
- package/docs/development/remote-bridge.md +1 -1
- package/docs/development/templates.md +93 -0
- package/docs/development.md +1 -1
- package/docs/plugins/caching.md +3 -1
- package/docs/plugins/execution-context.md +1 -1
- package/docs/plugins/lakebase.md +1 -1
- package/docs.md +2 -2
- package/llms.txt +1 -0
- package/package.json +60 -58
- package/sbom.cdx.json +1 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { connectSSE } from "../../js/sse/connect-sse.js";
|
|
2
2
|
import "../../js/index.js";
|
|
3
|
+
import { TERMINAL_STATUSES } from "./types.js";
|
|
3
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
5
|
|
|
5
6
|
//#region src/react/genie/use-genie-chat.ts
|
|
@@ -49,9 +50,22 @@ function makeAssistantItem(msg) {
|
|
|
49
50
|
/**
|
|
50
51
|
* The API bundles user question (content) and AI answer (attachments) in one message.
|
|
51
52
|
* Split into separate user + assistant items for display.
|
|
53
|
+
*
|
|
54
|
+
* When a message is still in-progress (non-terminal status) and has no
|
|
55
|
+
* attachments yet, we emit an empty assistant placeholder so the UI can
|
|
56
|
+
* show a loading indicator and later poll for the completed response.
|
|
52
57
|
*/
|
|
53
58
|
function messageResultToItems(msg) {
|
|
54
|
-
|
|
59
|
+
const hasAttachments = (msg.attachments?.length ?? 0) > 0;
|
|
60
|
+
if (!hasAttachments && TERMINAL_STATUSES.has(msg.status)) return [makeUserItem(msg)];
|
|
61
|
+
if (!hasAttachments) return [makeUserItem(msg, "-user"), {
|
|
62
|
+
id: msg.messageId,
|
|
63
|
+
role: "assistant",
|
|
64
|
+
content: "",
|
|
65
|
+
status: msg.status,
|
|
66
|
+
attachments: [],
|
|
67
|
+
queryResults: /* @__PURE__ */ new Map()
|
|
68
|
+
}];
|
|
55
69
|
return [makeUserItem(msg, "-user"), makeAssistantItem(msg)];
|
|
56
70
|
}
|
|
57
71
|
/**
|
|
@@ -118,12 +132,12 @@ function useGenieChat(options) {
|
|
|
118
132
|
const conversationIdRef = useRef(null);
|
|
119
133
|
const nextPageTokenRef = useRef(null);
|
|
120
134
|
const isLoadingOlderRef = useRef(false);
|
|
135
|
+
const processStreamEventRef = useRef(() => {});
|
|
121
136
|
useEffect(() => {
|
|
122
137
|
conversationIdRef.current = conversationId;
|
|
123
138
|
nextPageTokenRef.current = nextPageToken;
|
|
124
139
|
}, [conversationId, nextPageToken]);
|
|
125
|
-
|
|
126
|
-
const processStreamEvent = useCallback((event) => {
|
|
140
|
+
processStreamEventRef.current = useCallback((event) => {
|
|
127
141
|
switch (event.type) {
|
|
128
142
|
case "message_start":
|
|
129
143
|
setConversationId(event.conversationId);
|
|
@@ -140,15 +154,13 @@ function useGenieChat(options) {
|
|
|
140
154
|
});
|
|
141
155
|
break;
|
|
142
156
|
case "message_result": {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
});
|
|
151
|
-
}
|
|
157
|
+
const item = makeAssistantItem(event.message);
|
|
158
|
+
setMessages((prev) => {
|
|
159
|
+
const last = prev[prev.length - 1];
|
|
160
|
+
if (!last || last.role !== "assistant") return prev;
|
|
161
|
+
if (last.id === event.message.messageId || last.id === "") return [...prev.slice(0, -1), item];
|
|
162
|
+
return prev;
|
|
163
|
+
});
|
|
152
164
|
break;
|
|
153
165
|
}
|
|
154
166
|
case "query_result":
|
|
@@ -213,7 +225,7 @@ function useGenieChat(options) {
|
|
|
213
225
|
signal: abortController.signal,
|
|
214
226
|
onMessage: async (message) => {
|
|
215
227
|
try {
|
|
216
|
-
|
|
228
|
+
processStreamEventRef.current(JSON.parse(message.data));
|
|
217
229
|
} catch {}
|
|
218
230
|
},
|
|
219
231
|
onError: (err) => {
|
|
@@ -227,12 +239,12 @@ function useGenieChat(options) {
|
|
|
227
239
|
}
|
|
228
240
|
}).then(() => {
|
|
229
241
|
if (!abortController.signal.aborted) setStatus((prev) => prev === "error" ? "error" : "idle");
|
|
242
|
+
}).catch(() => {
|
|
243
|
+
if (abortController.signal.aborted) return;
|
|
244
|
+
setError("Connection error. Please try again.");
|
|
245
|
+
setStatus("error");
|
|
230
246
|
});
|
|
231
|
-
}, [
|
|
232
|
-
alias,
|
|
233
|
-
basePath,
|
|
234
|
-
processStreamEvent
|
|
235
|
-
]);
|
|
247
|
+
}, [alias, basePath]);
|
|
236
248
|
/** Creates an AbortController, stores it in the given ref, and fetches a conversation page. */
|
|
237
249
|
const fetchPage = useCallback((controllerRef, convId, options) => {
|
|
238
250
|
controllerRef.current?.abort();
|
|
@@ -256,6 +268,30 @@ function useGenieChat(options) {
|
|
|
256
268
|
abortController
|
|
257
269
|
};
|
|
258
270
|
}, [alias, basePath]);
|
|
271
|
+
const pollPendingMessage = useCallback((convId, messageId, parentAbortController) => {
|
|
272
|
+
setStatus("streaming");
|
|
273
|
+
const requestId = crypto.randomUUID();
|
|
274
|
+
connectSSE({
|
|
275
|
+
url: `${basePath}/${encodeURIComponent(alias)}/conversations/${encodeURIComponent(convId)}/messages/${encodeURIComponent(messageId)}?requestId=${encodeURIComponent(requestId)}`,
|
|
276
|
+
signal: parentAbortController.signal,
|
|
277
|
+
onMessage: async (message) => {
|
|
278
|
+
try {
|
|
279
|
+
processStreamEventRef.current(JSON.parse(message.data));
|
|
280
|
+
} catch {}
|
|
281
|
+
},
|
|
282
|
+
onError: (err) => {
|
|
283
|
+
if (parentAbortController.signal.aborted) return;
|
|
284
|
+
setError(err instanceof Error ? err.message : "Failed to poll pending message.");
|
|
285
|
+
setStatus("error");
|
|
286
|
+
}
|
|
287
|
+
}).then(() => {
|
|
288
|
+
if (!parentAbortController.signal.aborted) setStatus((prev) => prev === "error" ? "error" : "idle");
|
|
289
|
+
}).catch(() => {
|
|
290
|
+
if (parentAbortController.signal.aborted) return;
|
|
291
|
+
setError("Failed to poll pending message.");
|
|
292
|
+
setStatus("error");
|
|
293
|
+
});
|
|
294
|
+
}, [alias, basePath]);
|
|
259
295
|
const loadHistory = useCallback((convId) => {
|
|
260
296
|
paginationAbortRef.current?.abort();
|
|
261
297
|
setStatus("loading-history");
|
|
@@ -264,12 +300,13 @@ function useGenieChat(options) {
|
|
|
264
300
|
setConversationId(convId);
|
|
265
301
|
const { promise, abortController } = fetchPage(abortControllerRef, convId, { errorMessage: "Failed to load conversation history." });
|
|
266
302
|
promise.then((items) => {
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
303
|
+
if (abortController.signal.aborted) return;
|
|
304
|
+
setMessages(items);
|
|
305
|
+
const lastItem = items[items.length - 1];
|
|
306
|
+
if (lastItem?.role === "assistant" && !TERMINAL_STATUSES.has(lastItem.status)) pollPendingMessage(convId, lastItem.id, abortController);
|
|
307
|
+
else setStatus((prev) => prev === "error" ? "error" : "idle");
|
|
271
308
|
});
|
|
272
|
-
}, [fetchPage]);
|
|
309
|
+
}, [fetchPage, pollPendingMessage]);
|
|
273
310
|
const fetchPreviousPage = useCallback(() => {
|
|
274
311
|
if (!nextPageTokenRef.current || !conversationIdRef.current || isLoadingOlderRef.current) return;
|
|
275
312
|
isLoadingOlderRef.current = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-genie-chat.js","names":[],"sources":["../../../src/react/genie/use-genie-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { connectSSE } from \"@/js\";\nimport type {\n GenieChatStatus,\n GenieMessageItem,\n GenieMessageResponse,\n GenieStreamEvent,\n UseGenieChatOptions,\n UseGenieChatReturn,\n} from \"./types\";\n\nfunction getUrlParam(name: string): string | null {\n return new URLSearchParams(window.location.search).get(name);\n}\n\nfunction setUrlParam(name: string, value: string): void {\n const url = new URL(window.location.href);\n url.searchParams.set(name, value);\n window.history.replaceState({}, \"\", url.toString());\n}\n\nfunction removeUrlParam(name: string): void {\n const url = new URL(window.location.href);\n url.searchParams.delete(name);\n window.history.replaceState({}, \"\", url.toString());\n}\n\n/**\n * The Genie API puts the user's question in `message.content` and the\n * actual AI answer in text attachments. Extract the text attachment\n * content so we display the real answer, not the question echo.\n */\nfunction extractAssistantContent(msg: GenieMessageResponse): string {\n const textParts = (msg.attachments ?? [])\n .map((att) => att.text?.content)\n .filter(Boolean) as string[];\n return textParts.length > 0 ? textParts.join(\"\\n\\n\") : msg.content;\n}\n\nfunction makeUserItem(\n msg: GenieMessageResponse,\n idSuffix = \"\",\n): GenieMessageItem {\n return {\n id: `${msg.messageId}${idSuffix}`,\n role: \"user\",\n content: msg.content,\n status: msg.status,\n attachments: [],\n queryResults: new Map(),\n };\n}\n\nfunction makeAssistantItem(msg: GenieMessageResponse): GenieMessageItem {\n return {\n id: msg.messageId,\n role: \"assistant\",\n content: extractAssistantContent(msg),\n status: msg.status,\n attachments: msg.attachments ?? [],\n queryResults: new Map(),\n error: msg.error,\n };\n}\n\n/**\n * The API bundles user question (content) and AI answer (attachments) in one message.\n * Split into separate user + assistant items for display.\n */\nfunction messageResultToItems(msg: GenieMessageResponse): GenieMessageItem[] {\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n if (!hasAttachments) return [makeUserItem(msg)];\n return [makeUserItem(msg, \"-user\"), makeAssistantItem(msg)];\n}\n\n/**\n * Streams a conversation page via SSE. Collects message items and query\n * results into a buffer and returns them when the stream completes.\n */\nfunction fetchConversationPage(\n basePath: string,\n alias: string,\n convId: string,\n options: {\n pageToken?: string;\n signal?: AbortSignal;\n onPaginationInfo?: (nextPageToken: string | null) => void;\n onError?: (error: string) => void;\n onConnectionError?: (err: unknown) => void;\n },\n): Promise<GenieMessageItem[]> {\n const params = new URLSearchParams({\n requestId: crypto.randomUUID(),\n });\n if (options.pageToken) {\n params.set(\"pageToken\", options.pageToken);\n }\n\n const items: GenieMessageItem[] = [];\n return connectSSE({\n url: `${basePath}/${encodeURIComponent(alias)}/conversations/${encodeURIComponent(convId)}?${params}`,\n signal: options.signal,\n onMessage: async (message) => {\n try {\n const event = JSON.parse(message.data) as GenieStreamEvent;\n switch (event.type) {\n case \"message_result\":\n items.push(...messageResultToItems(event.message));\n break;\n case \"query_result\":\n for (let i = items.length - 1; i >= 0; i--) {\n const item = items[i];\n if (\n item.attachments.some(\n (a) => a.attachmentId === event.attachmentId,\n )\n ) {\n item.queryResults.set(event.attachmentId, event.data);\n break;\n }\n }\n break;\n case \"history_info\":\n options.onPaginationInfo?.(event.nextPageToken);\n break;\n case \"error\":\n options.onError?.(event.error);\n break;\n }\n } catch {\n // Malformed SSE data\n }\n },\n onError: (err) => options.onConnectionError?.(err),\n }).then(() => items);\n}\n\n/** Minimum time (ms) to hold the loading-older state so scroll inertia settles before prepending messages. */\nconst MIN_PREVIOUS_PAGE_LOAD_MS = 800;\n\n/**\n * Manages the full Genie chat lifecycle:\n * SSE streaming, conversation persistence via URL, and history replay.\n *\n * @example\n * ```tsx\n * const { messages, status, sendMessage, reset } = useGenieChat({ alias: \"demo\" });\n * ```\n */\nexport function useGenieChat(options: UseGenieChatOptions): UseGenieChatReturn {\n const {\n alias,\n basePath = \"/api/genie\",\n persistInUrl = true,\n urlParamName = \"conversationId\",\n } = options;\n\n const [messages, setMessages] = useState<GenieMessageItem[]>([]);\n const [status, setStatus] = useState<GenieChatStatus>(\"idle\");\n const [conversationId, setConversationId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [nextPageToken, setNextPageToken] = useState<string | null>(null);\n\n const hasPreviousPage = nextPageToken !== null;\n const isFetchingPreviousPage = status === \"loading-older\";\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const paginationAbortRef = useRef<AbortController | null>(null);\n const conversationIdRef = useRef<string | null>(null);\n const nextPageTokenRef = useRef<string | null>(null);\n const isLoadingOlderRef = useRef(false);\n\n useEffect(() => {\n conversationIdRef.current = conversationId;\n nextPageTokenRef.current = nextPageToken;\n }, [conversationId, nextPageToken]);\n\n /** Process SSE events during live message streaming (sendMessage). */\n const processStreamEvent = useCallback(\n (event: GenieStreamEvent) => {\n switch (event.type) {\n case \"message_start\": {\n setConversationId(event.conversationId);\n if (persistInUrl) {\n setUrlParam(urlParamName, event.conversationId);\n }\n break;\n }\n\n case \"status\": {\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n if (last?.role === \"assistant\") {\n return [...prev.slice(0, -1), { ...last, status: event.status }];\n }\n return prev;\n });\n break;\n }\n\n case \"message_result\": {\n const msg = event.message;\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n\n if (hasAttachments) {\n // During streaming we already appended the user message locally,\n // so only handle assistant results. Messages without attachments\n // are the user-message echo from the API — skip those.\n const item = makeAssistantItem(msg);\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n if (last?.role === \"assistant\" && last.id === \"\") {\n return [...prev.slice(0, -1), item];\n }\n return [...prev, item];\n });\n }\n break;\n }\n\n case \"query_result\": {\n setMessages((prev) => {\n // Reverse scan — query results typically match recent messages\n for (let i = prev.length - 1; i >= 0; i--) {\n const msg = prev[i];\n if (\n msg.attachments.some(\n (a) => a.attachmentId === event.attachmentId,\n )\n ) {\n const updated = prev.slice();\n updated[i] = {\n ...msg,\n queryResults: new Map(msg.queryResults).set(\n event.attachmentId,\n event.data,\n ),\n };\n return updated;\n }\n }\n return prev;\n });\n break;\n }\n\n case \"error\": {\n setError(event.error);\n setStatus(\"error\");\n break;\n }\n }\n },\n [persistInUrl, urlParamName],\n );\n\n const sendMessage = useCallback(\n (content: string) => {\n const trimmed = content.trim();\n if (!trimmed) return;\n\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n setError(null);\n setStatus(\"streaming\");\n\n const userMessage: GenieMessageItem = {\n id: crypto.randomUUID(),\n role: \"user\",\n content: trimmed,\n status: \"COMPLETED\",\n attachments: [],\n queryResults: new Map(),\n };\n\n const assistantPlaceholder: GenieMessageItem = {\n id: \"\",\n role: \"assistant\",\n content: \"\",\n status: \"ASKING_AI\",\n attachments: [],\n queryResults: new Map(),\n };\n\n setMessages((prev) => [...prev, userMessage, assistantPlaceholder]);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n const requestId = crypto.randomUUID();\n\n connectSSE({\n url: `${basePath}/${encodeURIComponent(alias)}/messages?requestId=${encodeURIComponent(requestId)}`,\n payload: {\n content: trimmed,\n conversationId: conversationIdRef.current ?? undefined,\n },\n signal: abortController.signal,\n onMessage: async (message) => {\n try {\n processStreamEvent(JSON.parse(message.data) as GenieStreamEvent);\n } catch {\n // Malformed SSE data\n }\n },\n onError: (err) => {\n if (abortController.signal.aborted) return;\n setError(\n err instanceof Error\n ? err.message\n : \"Connection error. Please try again.\",\n );\n setStatus(\"error\");\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n return last?.role === \"assistant\" && last.id === \"\"\n ? prev.slice(0, -1)\n : prev;\n });\n },\n }).then(() => {\n if (!abortController.signal.aborted) {\n setStatus((prev) => (prev === \"error\" ? \"error\" : \"idle\"));\n }\n });\n },\n [alias, basePath, processStreamEvent],\n );\n\n /** Creates an AbortController, stores it in the given ref, and fetches a conversation page. */\n const fetchPage = useCallback(\n (\n controllerRef: { current: AbortController | null },\n convId: string,\n options?: { pageToken?: string; errorMessage?: string },\n ) => {\n controllerRef.current?.abort();\n const abortController = new AbortController();\n controllerRef.current = abortController;\n\n const promise = fetchConversationPage(basePath, alias, convId, {\n pageToken: options?.pageToken,\n signal: abortController.signal,\n onPaginationInfo: setNextPageToken,\n onError: (msg) => {\n setError(msg);\n setStatus(\"error\");\n },\n onConnectionError: (err) => {\n if (abortController.signal.aborted) return;\n setError(\n err instanceof Error\n ? err.message\n : (options?.errorMessage ?? \"Failed to load messages.\"),\n );\n setStatus(\"error\");\n },\n });\n\n return { promise, abortController };\n },\n [alias, basePath],\n );\n\n const loadHistory = useCallback(\n (convId: string) => {\n paginationAbortRef.current?.abort();\n setStatus(\"loading-history\");\n setError(null);\n setMessages([]);\n setConversationId(convId);\n\n const { promise, abortController } = fetchPage(\n abortControllerRef,\n convId,\n { errorMessage: \"Failed to load conversation history.\" },\n );\n promise.then((items) => {\n if (!abortController.signal.aborted) {\n setMessages(items);\n setStatus((prev) => (prev === \"error\" ? \"error\" : \"idle\"));\n }\n });\n },\n [fetchPage],\n );\n\n const fetchPreviousPage = useCallback(() => {\n if (\n !nextPageTokenRef.current ||\n !conversationIdRef.current ||\n isLoadingOlderRef.current\n )\n return;\n\n isLoadingOlderRef.current = true;\n setStatus(\"loading-older\");\n setError(null);\n\n const startTime = Date.now();\n const { promise, abortController } = fetchPage(\n paginationAbortRef,\n conversationIdRef.current,\n {\n pageToken: nextPageTokenRef.current,\n errorMessage: \"Failed to load older messages.\",\n },\n );\n promise\n .then(async (items) => {\n if (abortController.signal.aborted) return;\n const elapsed = Date.now() - startTime;\n if (elapsed < MIN_PREVIOUS_PAGE_LOAD_MS) {\n await new Promise((r) =>\n setTimeout(r, MIN_PREVIOUS_PAGE_LOAD_MS - elapsed),\n );\n }\n if (abortController.signal.aborted) return;\n if (items.length > 0) {\n setMessages((prev) => [...items, ...prev]);\n }\n setStatus((current) =>\n current === \"loading-older\" ? \"idle\" : current,\n );\n })\n .finally(() => {\n isLoadingOlderRef.current = false;\n });\n }, [fetchPage]);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n setMessages([]);\n setConversationId(null);\n setError(null);\n setStatus(\"idle\");\n setNextPageToken(null);\n if (persistInUrl) {\n removeUrlParam(urlParamName);\n }\n }, [persistInUrl, urlParamName]);\n\n useEffect(() => {\n if (!persistInUrl) return;\n const existingId = getUrlParam(urlParamName);\n if (existingId) {\n loadHistory(existingId);\n }\n return () => {\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n };\n }, [persistInUrl, urlParamName, loadHistory]);\n\n return {\n messages,\n status,\n conversationId,\n error,\n sendMessage,\n reset,\n hasPreviousPage,\n isFetchingPreviousPage,\n fetchPreviousPage,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-genie-chat.js","names":[],"sources":["../../../src/react/genie/use-genie-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { connectSSE } from \"@/js\";\nimport {\n type GenieChatStatus,\n type GenieMessageItem,\n type GenieMessageResponse,\n type GenieStreamEvent,\n TERMINAL_STATUSES,\n type UseGenieChatOptions,\n type UseGenieChatReturn,\n} from \"./types\";\n\nfunction getUrlParam(name: string): string | null {\n return new URLSearchParams(window.location.search).get(name);\n}\n\nfunction setUrlParam(name: string, value: string): void {\n const url = new URL(window.location.href);\n url.searchParams.set(name, value);\n window.history.replaceState({}, \"\", url.toString());\n}\n\nfunction removeUrlParam(name: string): void {\n const url = new URL(window.location.href);\n url.searchParams.delete(name);\n window.history.replaceState({}, \"\", url.toString());\n}\n\n/**\n * The Genie API puts the user's question in `message.content` and the\n * actual AI answer in text attachments. Extract the text attachment\n * content so we display the real answer, not the question echo.\n */\nfunction extractAssistantContent(msg: GenieMessageResponse): string {\n const textParts = (msg.attachments ?? [])\n .map((att) => att.text?.content)\n .filter(Boolean) as string[];\n return textParts.length > 0 ? textParts.join(\"\\n\\n\") : msg.content;\n}\n\nfunction makeUserItem(\n msg: GenieMessageResponse,\n idSuffix = \"\",\n): GenieMessageItem {\n return {\n id: `${msg.messageId}${idSuffix}`,\n role: \"user\",\n content: msg.content,\n status: msg.status,\n attachments: [],\n queryResults: new Map(),\n };\n}\n\nfunction makeAssistantItem(msg: GenieMessageResponse): GenieMessageItem {\n return {\n id: msg.messageId,\n role: \"assistant\",\n content: extractAssistantContent(msg),\n status: msg.status,\n attachments: msg.attachments ?? [],\n queryResults: new Map(),\n error: msg.error,\n };\n}\n\n/**\n * The API bundles user question (content) and AI answer (attachments) in one message.\n * Split into separate user + assistant items for display.\n *\n * When a message is still in-progress (non-terminal status) and has no\n * attachments yet, we emit an empty assistant placeholder so the UI can\n * show a loading indicator and later poll for the completed response.\n */\nfunction messageResultToItems(msg: GenieMessageResponse): GenieMessageItem[] {\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n\n if (!hasAttachments && TERMINAL_STATUSES.has(msg.status)) {\n return [makeUserItem(msg)];\n }\n if (!hasAttachments) {\n return [\n makeUserItem(msg, \"-user\"),\n {\n id: msg.messageId,\n role: \"assistant\",\n content: \"\",\n status: msg.status,\n attachments: [],\n queryResults: new Map(),\n },\n ];\n }\n return [makeUserItem(msg, \"-user\"), makeAssistantItem(msg)];\n}\n\n/**\n * Streams a conversation page via SSE. Collects message items and query\n * results into a buffer and returns them when the stream completes.\n */\nfunction fetchConversationPage(\n basePath: string,\n alias: string,\n convId: string,\n options: {\n pageToken?: string;\n signal?: AbortSignal;\n onPaginationInfo?: (nextPageToken: string | null) => void;\n onError?: (error: string) => void;\n onConnectionError?: (err: unknown) => void;\n },\n): Promise<GenieMessageItem[]> {\n const params = new URLSearchParams({\n requestId: crypto.randomUUID(),\n });\n if (options.pageToken) {\n params.set(\"pageToken\", options.pageToken);\n }\n\n const items: GenieMessageItem[] = [];\n return connectSSE({\n url: `${basePath}/${encodeURIComponent(alias)}/conversations/${encodeURIComponent(convId)}?${params}`,\n signal: options.signal,\n onMessage: async (message) => {\n try {\n const event = JSON.parse(message.data) as GenieStreamEvent;\n switch (event.type) {\n case \"message_result\":\n items.push(...messageResultToItems(event.message));\n break;\n case \"query_result\":\n for (let i = items.length - 1; i >= 0; i--) {\n const item = items[i];\n if (\n item.attachments.some(\n (a) => a.attachmentId === event.attachmentId,\n )\n ) {\n item.queryResults.set(event.attachmentId, event.data);\n break;\n }\n }\n break;\n case \"history_info\":\n options.onPaginationInfo?.(event.nextPageToken);\n break;\n case \"error\":\n options.onError?.(event.error);\n break;\n }\n } catch {\n // Malformed SSE data\n }\n },\n onError: (err) => options.onConnectionError?.(err),\n }).then(() => items);\n}\n\n/** Minimum time (ms) to hold the loading-older state so scroll inertia settles before prepending messages. */\nconst MIN_PREVIOUS_PAGE_LOAD_MS = 800;\n\n/**\n * Manages the full Genie chat lifecycle:\n * SSE streaming, conversation persistence via URL, and history replay.\n *\n * @example\n * ```tsx\n * const { messages, status, sendMessage, reset } = useGenieChat({ alias: \"demo\" });\n * ```\n */\nexport function useGenieChat(options: UseGenieChatOptions): UseGenieChatReturn {\n const {\n alias,\n basePath = \"/api/genie\",\n persistInUrl = true,\n urlParamName = \"conversationId\",\n } = options;\n\n const [messages, setMessages] = useState<GenieMessageItem[]>([]);\n const [status, setStatus] = useState<GenieChatStatus>(\"idle\");\n const [conversationId, setConversationId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [nextPageToken, setNextPageToken] = useState<string | null>(null);\n\n const hasPreviousPage = nextPageToken !== null;\n const isFetchingPreviousPage = status === \"loading-older\";\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const paginationAbortRef = useRef<AbortController | null>(null);\n const conversationIdRef = useRef<string | null>(null);\n const nextPageTokenRef = useRef<string | null>(null);\n const isLoadingOlderRef = useRef(false);\n const processStreamEventRef = useRef<(event: GenieStreamEvent) => void>(\n () => {},\n );\n\n useEffect(() => {\n conversationIdRef.current = conversationId;\n nextPageTokenRef.current = nextPageToken;\n }, [conversationId, nextPageToken]);\n\n /** Process SSE events during live message streaming (sendMessage). */\n const processStreamEvent = useCallback(\n (event: GenieStreamEvent) => {\n switch (event.type) {\n case \"message_start\": {\n setConversationId(event.conversationId);\n if (persistInUrl) {\n setUrlParam(urlParamName, event.conversationId);\n }\n break;\n }\n\n case \"status\": {\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n if (last?.role === \"assistant\") {\n return [...prev.slice(0, -1), { ...last, status: event.status }];\n }\n return prev;\n });\n break;\n }\n\n case \"message_result\": {\n const item = makeAssistantItem(event.message);\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n if (!last || last.role !== \"assistant\") return prev;\n\n if (last.id === event.message.messageId || last.id === \"\") {\n return [...prev.slice(0, -1), item];\n }\n\n return prev;\n });\n break;\n }\n\n case \"query_result\": {\n setMessages((prev) => {\n // Reverse scan — query results typically match recent messages\n for (let i = prev.length - 1; i >= 0; i--) {\n const msg = prev[i];\n if (\n msg.attachments.some(\n (a) => a.attachmentId === event.attachmentId,\n )\n ) {\n const updated = prev.slice();\n updated[i] = {\n ...msg,\n queryResults: new Map(msg.queryResults).set(\n event.attachmentId,\n event.data,\n ),\n };\n return updated;\n }\n }\n return prev;\n });\n break;\n }\n\n case \"error\": {\n setError(event.error);\n setStatus(\"error\");\n break;\n }\n }\n },\n [persistInUrl, urlParamName],\n );\n\n processStreamEventRef.current = processStreamEvent;\n\n const sendMessage = useCallback(\n (content: string) => {\n const trimmed = content.trim();\n if (!trimmed) return;\n\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n setError(null);\n setStatus(\"streaming\");\n\n const userMessage: GenieMessageItem = {\n id: crypto.randomUUID(),\n role: \"user\",\n content: trimmed,\n status: \"COMPLETED\",\n attachments: [],\n queryResults: new Map(),\n };\n\n const assistantPlaceholder: GenieMessageItem = {\n id: \"\",\n role: \"assistant\",\n content: \"\",\n status: \"ASKING_AI\",\n attachments: [],\n queryResults: new Map(),\n };\n\n setMessages((prev) => [...prev, userMessage, assistantPlaceholder]);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n const requestId = crypto.randomUUID();\n\n connectSSE({\n url: `${basePath}/${encodeURIComponent(alias)}/messages?requestId=${encodeURIComponent(requestId)}`,\n payload: {\n content: trimmed,\n conversationId: conversationIdRef.current ?? undefined,\n },\n signal: abortController.signal,\n onMessage: async (message) => {\n try {\n processStreamEventRef.current(\n JSON.parse(message.data) as GenieStreamEvent,\n );\n } catch {\n // Malformed SSE data\n }\n },\n onError: (err) => {\n if (abortController.signal.aborted) return;\n setError(\n err instanceof Error\n ? err.message\n : \"Connection error. Please try again.\",\n );\n setStatus(\"error\");\n setMessages((prev) => {\n const last = prev[prev.length - 1];\n return last?.role === \"assistant\" && last.id === \"\"\n ? prev.slice(0, -1)\n : prev;\n });\n },\n })\n .then(() => {\n if (!abortController.signal.aborted) {\n setStatus((prev) => (prev === \"error\" ? \"error\" : \"idle\"));\n }\n })\n .catch(() => {\n if (abortController.signal.aborted) return;\n setError(\"Connection error. Please try again.\");\n setStatus(\"error\");\n });\n },\n [alias, basePath],\n );\n\n /** Creates an AbortController, stores it in the given ref, and fetches a conversation page. */\n const fetchPage = useCallback(\n (\n controllerRef: { current: AbortController | null },\n convId: string,\n options?: { pageToken?: string; errorMessage?: string },\n ) => {\n controllerRef.current?.abort();\n const abortController = new AbortController();\n controllerRef.current = abortController;\n\n const promise = fetchConversationPage(basePath, alias, convId, {\n pageToken: options?.pageToken,\n signal: abortController.signal,\n onPaginationInfo: setNextPageToken,\n onError: (msg) => {\n setError(msg);\n setStatus(\"error\");\n },\n onConnectionError: (err) => {\n if (abortController.signal.aborted) return;\n setError(\n err instanceof Error\n ? err.message\n : (options?.errorMessage ?? \"Failed to load messages.\"),\n );\n setStatus(\"error\");\n },\n });\n\n return { promise, abortController };\n },\n [alias, basePath],\n );\n\n const pollPendingMessage = useCallback(\n (\n convId: string,\n messageId: string,\n parentAbortController: AbortController,\n ) => {\n setStatus(\"streaming\");\n\n const requestId = crypto.randomUUID();\n const url =\n `${basePath}/${encodeURIComponent(alias)}/conversations/${encodeURIComponent(convId)}` +\n `/messages/${encodeURIComponent(messageId)}?requestId=${encodeURIComponent(requestId)}`;\n\n connectSSE({\n url,\n signal: parentAbortController.signal,\n onMessage: async (message) => {\n try {\n processStreamEventRef.current(\n JSON.parse(message.data) as GenieStreamEvent,\n );\n } catch {\n // Malformed SSE data\n }\n },\n onError: (err) => {\n if (parentAbortController.signal.aborted) return;\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to poll pending message.\",\n );\n setStatus(\"error\");\n },\n })\n .then(() => {\n if (!parentAbortController.signal.aborted) {\n setStatus((prev) => (prev === \"error\" ? \"error\" : \"idle\"));\n }\n })\n .catch(() => {\n if (parentAbortController.signal.aborted) return;\n setError(\"Failed to poll pending message.\");\n setStatus(\"error\");\n });\n },\n [alias, basePath],\n );\n\n const loadHistory = useCallback(\n (convId: string) => {\n paginationAbortRef.current?.abort();\n setStatus(\"loading-history\");\n setError(null);\n setMessages([]);\n setConversationId(convId);\n\n const { promise, abortController } = fetchPage(\n abortControllerRef,\n convId,\n { errorMessage: \"Failed to load conversation history.\" },\n );\n promise.then((items) => {\n if (abortController.signal.aborted) return;\n setMessages(items);\n\n const lastItem = items[items.length - 1];\n if (\n lastItem?.role === \"assistant\" &&\n !TERMINAL_STATUSES.has(lastItem.status)\n ) {\n pollPendingMessage(convId, lastItem.id, abortController);\n } else {\n setStatus((prev) => (prev === \"error\" ? \"error\" : \"idle\"));\n }\n });\n },\n [fetchPage, pollPendingMessage],\n );\n\n const fetchPreviousPage = useCallback(() => {\n if (\n !nextPageTokenRef.current ||\n !conversationIdRef.current ||\n isLoadingOlderRef.current\n )\n return;\n\n isLoadingOlderRef.current = true;\n setStatus(\"loading-older\");\n setError(null);\n\n const startTime = Date.now();\n const { promise, abortController } = fetchPage(\n paginationAbortRef,\n conversationIdRef.current,\n {\n pageToken: nextPageTokenRef.current,\n errorMessage: \"Failed to load older messages.\",\n },\n );\n promise\n .then(async (items) => {\n if (abortController.signal.aborted) return;\n const elapsed = Date.now() - startTime;\n if (elapsed < MIN_PREVIOUS_PAGE_LOAD_MS) {\n await new Promise((r) =>\n setTimeout(r, MIN_PREVIOUS_PAGE_LOAD_MS - elapsed),\n );\n }\n if (abortController.signal.aborted) return;\n if (items.length > 0) {\n setMessages((prev) => [...items, ...prev]);\n }\n setStatus((current) =>\n current === \"loading-older\" ? \"idle\" : current,\n );\n })\n .finally(() => {\n isLoadingOlderRef.current = false;\n });\n }, [fetchPage]);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n setMessages([]);\n setConversationId(null);\n setError(null);\n setStatus(\"idle\");\n setNextPageToken(null);\n if (persistInUrl) {\n removeUrlParam(urlParamName);\n }\n }, [persistInUrl, urlParamName]);\n\n useEffect(() => {\n if (!persistInUrl) return;\n const existingId = getUrlParam(urlParamName);\n if (existingId) {\n loadHistory(existingId);\n }\n return () => {\n abortControllerRef.current?.abort();\n paginationAbortRef.current?.abort();\n };\n }, [persistInUrl, urlParamName, loadHistory]);\n\n return {\n messages,\n status,\n conversationId,\n error,\n sendMessage,\n reset,\n hasPreviousPage,\n isFetchingPreviousPage,\n fetchPreviousPage,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { UseChartDataOptions, UseChartDataResult, useChartData } from "./use-chart-data.js";
|
|
2
2
|
import { AnalyticsFormat, InferResultByFormat, InferRowType, PluginRegistry, QueryRegistry, TypedArrowTable, UseAnalyticsQueryOptions, UseAnalyticsQueryResult } from "./types.js";
|
|
3
|
-
import { useAnalyticsQuery } from "./use-analytics-query.js";
|
|
3
|
+
import { useAnalyticsQuery } from "./use-analytics-query.js";
|
|
4
|
+
import { usePluginClientConfig } from "./use-plugin-config.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/react/hooks/use-plugin-config.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Returns the client-side config exposed by a plugin's `clientConfig()` method.
|
|
4
|
+
*
|
|
5
|
+
* The value is read once from the boot-time `<script id="__appkit__">` payload
|
|
6
|
+
* and cached for the lifetime of the component. Because `clientConfig()` runs
|
|
7
|
+
* at server startup, the data is static and never changes during a session.
|
|
8
|
+
*
|
|
9
|
+
* @param pluginName - The plugin name as registered in AppKit (e.g. `"files"`)
|
|
10
|
+
* @returns The plugin's client config object, typed via the generic parameter
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* interface FilesConfig { volumes: string[] }
|
|
15
|
+
*
|
|
16
|
+
* function MyComponent() {
|
|
17
|
+
* const { volumes } = usePluginClientConfig<FilesConfig>("files");
|
|
18
|
+
* return <ul>{volumes.map(v => <li key={v}>{v}</li>)}</ul>;
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function usePluginClientConfig<T = Record<string, unknown>>(pluginName: string): T;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { usePluginClientConfig };
|
|
25
|
+
//# sourceMappingURL=use-plugin-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-plugin-config.d.ts","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"mappings":";;AAuBA;;;;;;;;;;;;;;;;;;;iBAAgB,qBAAA,KAA0B,MAAA,kBAAA,CACxC,UAAA,WACC,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getPluginClientConfig } from "../../js/config.js";
|
|
2
|
+
import "../../js/index.js";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/react/hooks/use-plugin-config.ts
|
|
6
|
+
/**
|
|
7
|
+
* Returns the client-side config exposed by a plugin's `clientConfig()` method.
|
|
8
|
+
*
|
|
9
|
+
* The value is read once from the boot-time `<script id="__appkit__">` payload
|
|
10
|
+
* and cached for the lifetime of the component. Because `clientConfig()` runs
|
|
11
|
+
* at server startup, the data is static and never changes during a session.
|
|
12
|
+
*
|
|
13
|
+
* @param pluginName - The plugin name as registered in AppKit (e.g. `"files"`)
|
|
14
|
+
* @returns The plugin's client config object, typed via the generic parameter
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* interface FilesConfig { volumes: string[] }
|
|
19
|
+
*
|
|
20
|
+
* function MyComponent() {
|
|
21
|
+
* const { volumes } = usePluginClientConfig<FilesConfig>("files");
|
|
22
|
+
* return <ul>{volumes.map(v => <li key={v}>{v}</li>)}</ul>;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function usePluginClientConfig(pluginName) {
|
|
27
|
+
return useMemo(() => getPluginClientConfig(pluginName), [pluginName]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { usePluginClientConfig };
|
|
32
|
+
//# sourceMappingURL=use-plugin-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-plugin-config.js","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { getPluginClientConfig } from \"@/js\";\n\n/**\n * Returns the client-side config exposed by a plugin's `clientConfig()` method.\n *\n * The value is read once from the boot-time `<script id=\"__appkit__\">` payload\n * and cached for the lifetime of the component. Because `clientConfig()` runs\n * at server startup, the data is static and never changes during a session.\n *\n * @param pluginName - The plugin name as registered in AppKit (e.g. `\"files\"`)\n * @returns The plugin's client config object, typed via the generic parameter\n *\n * @example\n * ```tsx\n * interface FilesConfig { volumes: string[] }\n *\n * function MyComponent() {\n * const { volumes } = usePluginClientConfig<FilesConfig>(\"files\");\n * return <ul>{volumes.map(v => <li key={v}>{v}</li>)}</ul>;\n * }\n * ```\n */\nexport function usePluginClientConfig<T = Record<string, unknown>>(\n pluginName: string,\n): T {\n return useMemo(() => getPluginClientConfig<T>(pluginName), [pluginName]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,sBACd,YACG;AACH,QAAO,cAAc,sBAAyB,WAAW,EAAE,CAAC,WAAW,CAAC"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { NewFolderInput, NewFolderInputProps } from "./file-browser/new-folder-i
|
|
|
28
28
|
import "./file-browser/index.js";
|
|
29
29
|
import { ColumnCategory, GenieColumnMeta, TransformedGenieData, transformGenieData } from "./genie/genie-query-transform.js";
|
|
30
30
|
import { ChartInference, getCompatibleChartTypes, inferChartType } from "./genie/genie-chart-inference.js";
|
|
31
|
-
import { GenieChatProps, GenieChatStatus, GenieMessageItem, UseGenieChatOptions, UseGenieChatReturn } from "./genie/types.js";
|
|
31
|
+
import { GenieChatProps, GenieChatStatus, GenieMessageItem, TERMINAL_STATUSES, UseGenieChatOptions, UseGenieChatReturn } from "./genie/types.js";
|
|
32
32
|
import { GenieChat } from "./genie/genie-chat.js";
|
|
33
33
|
import { GenieChatInput } from "./genie/genie-chat-input.js";
|
|
34
34
|
import { GenieChatMessage } from "./genie/genie-chat-message.js";
|
|
@@ -38,6 +38,7 @@ import { useGenieChat } from "./genie/use-genie-chat.js";
|
|
|
38
38
|
import "./genie/index.js";
|
|
39
39
|
import { AnalyticsFormat, InferResultByFormat, InferRowType, PluginRegistry, QueryRegistry, TypedArrowTable, UseAnalyticsQueryOptions, UseAnalyticsQueryResult } from "./hooks/types.js";
|
|
40
40
|
import { useAnalyticsQuery } from "./hooks/use-analytics-query.js";
|
|
41
|
+
import { usePluginClientConfig } from "./hooks/use-plugin-config.js";
|
|
41
42
|
import "./hooks/index.js";
|
|
42
43
|
import { cn } from "./lib/utils.js";
|
|
43
44
|
import { PortalContainerContext, PortalContainerProvider, usePortalContainer, useResolvedPortalContainer } from "./portal-container-context.js";
|
|
@@ -97,4 +98,4 @@ import { Textarea } from "./ui/textarea.js";
|
|
|
97
98
|
import { Toggle, toggleVariants } from "./ui/toggle.js";
|
|
98
99
|
import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
|
|
99
100
|
import "./ui/index.js";
|
|
100
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartConfig, ChartContainer, ChartData, ChartInference, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartType, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
|
|
101
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnalyticsFormat, AreaChart, AreaChartProps, AreaChartSpecificProps, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BarChartProps, BarChartSpecificProps, BaseChart, BaseChartProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CartesianContext, ChartBaseProps, ChartColorPalette, ChartConfig, ChartContainer, ChartData, ChartInference, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartType, ChartWrapper, ChartWrapperProps, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnCategory, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataFormat, DataProps, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryEntry, DirectoryList, DirectoryListProps, DonutChart, DonutChartProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileBreadcrumbProps, FileBrowserLabels, FileEntry, FileEntryProps, FilePreview, FilePreviewPanel, FilePreviewPanelProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieAttachmentResponse, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieChatProps, GenieChatStatus, GenieColumnMeta, GenieMessageItem, GenieMessageResponse, GenieQueryVisualization, GenieStatementResponse, GenieStreamEvent, HeatmapChart, HeatmapChartProps, HeatmapChartSpecificProps, HeatmapContext, HoverCard, HoverCardContent, HoverCardTrigger, InferResultByFormat, InferRowType, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, LineChartProps, LineChartSpecificProps, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, NewFolderInputProps, NormalizedChartData, NormalizedChartDataBase, NormalizedHeatmapData, OptionBuilderContext, Orientation, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, PieChartProps, PieChartSpecificProps, PluginRegistry, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, QueryProps, QueryRegistry, RadarChart, RadarChartProps, RadarChartSpecificProps, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScatterChartProps, ScatterChartSpecificProps, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, TERMINAL_STATUSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TransformedGenieData, TypedArrowTable, UnifiedChartProps, UseAnalyticsQueryOptions, UseAnalyticsQueryResult, UseChartDataOptions, UseChartDataResult, UseGenieChatOptions, UseGenieChatReturn, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
|
package/dist/react/index.js
CHANGED
|
@@ -46,6 +46,7 @@ import { GenieChatMessageList } from "./genie/genie-chat-message-list.js";
|
|
|
46
46
|
import { useGenieChat } from "./genie/use-genie-chat.js";
|
|
47
47
|
import { GenieChat } from "./genie/genie-chat.js";
|
|
48
48
|
import "./genie/index.js";
|
|
49
|
+
import { usePluginClientConfig } from "./hooks/use-plugin-config.js";
|
|
49
50
|
import "./hooks/index.js";
|
|
50
51
|
import { Input } from "./ui/input.js";
|
|
51
52
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./ui/select.js";
|
|
@@ -94,4 +95,4 @@ import { Toggle, toggleVariants } from "./ui/toggle.js";
|
|
|
94
95
|
import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group.js";
|
|
95
96
|
import "./ui/index.js";
|
|
96
97
|
|
|
97
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
|
|
98
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AreaChart, AspectRatio, Avatar, AvatarFallback, AvatarImage, Badge, BarChart, BaseChart, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, ChartWrapper, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DATE_FIELD_PATTERNS, DataTable, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DirectoryList, DonutChart, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, FileBreadcrumb, FileEntry, FilePreviewPanel, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, GenieChat, GenieChatInput, GenieChatMessage, GenieChatMessageList, GenieQueryVisualization, HeatmapChart, HoverCard, HoverCardContent, HoverCardTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Label, LineChart, METADATA_DATE_PATTERNS, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NAME_FIELD_PATTERNS, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NewFolderInput, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PieChart, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PortalContainerContext, PortalContainerProvider, Progress, RadarChart, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScatterChart, ScrollArea, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption, buttonGroupVariants, buttonVariants, cn, createChart, createTimeSeriesData, formatFileSize, formatLabel, getCompatibleChartTypes, inferChartType, isArrowTable, isDataProps, isQueryProps, navigationMenuTriggerStyle, normalizeChartData, normalizeHeatmapData, sortTimeSeriesAscending, toChartArray, toChartValue, toggleVariants, transformGenieData, truncateLabel, useAllThemeColors, useAnalyticsQuery, useChartData, useFormField, useGenieChat, usePluginClientConfig, usePortalContainer, useResolvedPortalContainer, useSidebar, useThemeColors };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./plugin.js";
|
|
1
|
+
import { PluginClientConfigs, PluginEndpointMap, PluginEndpoints } from "./plugin.js";
|
|
2
2
|
import "./cache.js";
|
|
3
3
|
import "./execute.js";
|
|
4
4
|
import { GenieAttachmentResponse, GenieMessageResponse, GenieStatementResponse, GenieStreamEvent } from "./genie.js";
|
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
import "express";
|
|
2
|
-
import "json-schema";
|
|
2
|
+
import "json-schema";
|
|
3
|
+
|
|
4
|
+
//#region ../shared/src/plugin.d.ts
|
|
5
|
+
/** Map of endpoint names to their full paths for a plugin */
|
|
6
|
+
type PluginEndpointMap = Record<string, string>;
|
|
7
|
+
/** Map of plugin names to their endpoint maps */
|
|
8
|
+
type PluginEndpoints = Record<string, PluginEndpointMap>;
|
|
9
|
+
/** Map of plugin names to their client-exposed config */
|
|
10
|
+
type PluginClientConfigs = Record<string, Record<string, unknown>>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { PluginClientConfigs, PluginEndpointMap, PluginEndpoints };
|
|
13
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../../../shared/src/plugin.ts"],"mappings":";;;;;KAuNY,iBAAA,GAAoB,MAAA;;KAGpB,eAAA,GAAkB,MAAA,SAAe,iBAAA;;KAGjC,mBAAA,GAAsB,MAAA,SAAe,MAAA"}
|
|
@@ -251,7 +251,65 @@ A proxied plugin instance that executes as the user
|
|
|
251
251
|
|
|
252
252
|
#### Throws[](#throws "Direct link to Throws")
|
|
253
253
|
|
|
254
|
-
AuthenticationError if user token is not available in request headers (production only). In development mode (`NODE_ENV=development`),
|
|
254
|
+
AuthenticationError if user token is not available in request headers (production only). In development mode (`NODE_ENV=development`), skips user impersonation instead of throwing.
|
|
255
|
+
|
|
256
|
+
***
|
|
257
|
+
|
|
258
|
+
### clientConfig()[](#clientconfig "Direct link to clientConfig()")
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
clientConfig(): Record<string, unknown>;
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Returns startup config to expose to the client. Override this to surface server-side values that are safe to publish to the frontend, such as feature flags, resource IDs, or other app boot settings.
|
|
266
|
+
|
|
267
|
+
This runs once when the server starts, so it should not depend on request-scoped or user-specific state.
|
|
268
|
+
|
|
269
|
+
String values that match non-public environment variables are redacted unless you intentionally expose them via a matching `PUBLIC_APPKIT_` env var.
|
|
270
|
+
|
|
271
|
+
Values must be JSON-serializable plain data (no functions, Dates, classes, Maps, Sets, BigInts, or circular references). By default returns an empty object (plugin contributes nothing to client config).
|
|
272
|
+
|
|
273
|
+
On the client, read the config with the `usePluginClientConfig` hook (React) or the `getPluginClientConfig` function (vanilla JS), both from `@databricks/appkit-ui`.
|
|
274
|
+
|
|
275
|
+
#### Returns[](#returns-3 "Direct link to Returns")
|
|
276
|
+
|
|
277
|
+
`Record`<`string`, `unknown`>
|
|
278
|
+
|
|
279
|
+
#### Example[](#example "Direct link to Example")
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
// Server — plugin definition
|
|
283
|
+
class MyPlugin extends Plugin<MyConfig> {
|
|
284
|
+
clientConfig() {
|
|
285
|
+
return {
|
|
286
|
+
warehouseId: this.config.warehouseId,
|
|
287
|
+
features: { darkMode: true },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Client — React component
|
|
293
|
+
import { usePluginClientConfig } from "@databricks/appkit-ui/react";
|
|
294
|
+
|
|
295
|
+
interface MyPluginConfig { warehouseId: string; features: { darkMode: boolean } }
|
|
296
|
+
|
|
297
|
+
const config = usePluginClientConfig<MyPluginConfig>("myPlugin");
|
|
298
|
+
config.warehouseId; // "abc-123"
|
|
299
|
+
|
|
300
|
+
// Client — vanilla JS
|
|
301
|
+
import { getPluginClientConfig } from "@databricks/appkit-ui/js";
|
|
302
|
+
|
|
303
|
+
const config = getPluginClientConfig<MyPluginConfig>("myPlugin");
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Implementation of[](#implementation-of-2 "Direct link to Implementation of")
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
BasePlugin.clientConfig
|
|
311
|
+
|
|
312
|
+
```
|
|
255
313
|
|
|
256
314
|
***
|
|
257
315
|
|
|
@@ -283,7 +341,7 @@ All errors are caught and `undefined` is returned (production-safe). Route handl
|
|
|
283
341
|
| `options` | `PluginExecutionSettings` |
|
|
284
342
|
| `userKey?` | `string` |
|
|
285
343
|
|
|
286
|
-
#### Returns[](#returns-
|
|
344
|
+
#### Returns[](#returns-4 "Direct link to Returns")
|
|
287
345
|
|
|
288
346
|
`Promise`<`T` | `undefined`>
|
|
289
347
|
|
|
@@ -315,7 +373,7 @@ userKey?: string): Promise<void>;
|
|
|
315
373
|
| `options` | [`StreamExecutionSettings`](./docs/api/appkit/Interface.StreamExecutionSettings.md) |
|
|
316
374
|
| `userKey?` | `string` |
|
|
317
375
|
|
|
318
|
-
#### Returns[](#returns-
|
|
376
|
+
#### Returns[](#returns-5 "Direct link to Returns")
|
|
319
377
|
|
|
320
378
|
`Promise`<`void`>
|
|
321
379
|
|
|
@@ -332,11 +390,11 @@ Returns the public exports for this plugin. Override this to define a custom pub
|
|
|
332
390
|
|
|
333
391
|
The returned object becomes the plugin's public API on the AppKit instance (e.g. `appkit.myPlugin.method()`). AppKit automatically binds method context and adds `asUser(req)` for user-scoped execution.
|
|
334
392
|
|
|
335
|
-
#### Returns[](#returns-
|
|
393
|
+
#### Returns[](#returns-6 "Direct link to Returns")
|
|
336
394
|
|
|
337
395
|
`unknown`
|
|
338
396
|
|
|
339
|
-
#### Example[](#example "Direct link to Example")
|
|
397
|
+
#### Example[](#example-1 "Direct link to Example")
|
|
340
398
|
|
|
341
399
|
```ts
|
|
342
400
|
class MyPlugin extends Plugin {
|
|
@@ -353,7 +411,7 @@ appkit.myPlugin.getData();
|
|
|
353
411
|
|
|
354
412
|
```
|
|
355
413
|
|
|
356
|
-
#### Implementation of[](#implementation-of-
|
|
414
|
+
#### Implementation of[](#implementation-of-3 "Direct link to Implementation of")
|
|
357
415
|
|
|
358
416
|
```ts
|
|
359
417
|
BasePlugin.exports
|
|
@@ -369,11 +427,11 @@ getEndpoints(): PluginEndpointMap;
|
|
|
369
427
|
|
|
370
428
|
```
|
|
371
429
|
|
|
372
|
-
#### Returns[](#returns-
|
|
430
|
+
#### Returns[](#returns-7 "Direct link to Returns")
|
|
373
431
|
|
|
374
432
|
`PluginEndpointMap`
|
|
375
433
|
|
|
376
|
-
#### Implementation of[](#implementation-of-
|
|
434
|
+
#### Implementation of[](#implementation-of-4 "Direct link to Implementation of")
|
|
377
435
|
|
|
378
436
|
```ts
|
|
379
437
|
BasePlugin.getEndpoints
|
|
@@ -389,11 +447,11 @@ getSkipBodyParsingPaths(): ReadonlySet<string>;
|
|
|
389
447
|
|
|
390
448
|
```
|
|
391
449
|
|
|
392
|
-
#### Returns[](#returns-
|
|
450
|
+
#### Returns[](#returns-8 "Direct link to Returns")
|
|
393
451
|
|
|
394
452
|
`ReadonlySet`<`string`>
|
|
395
453
|
|
|
396
|
-
#### Implementation of[](#implementation-of-
|
|
454
|
+
#### Implementation of[](#implementation-of-5 "Direct link to Implementation of")
|
|
397
455
|
|
|
398
456
|
```ts
|
|
399
457
|
BasePlugin.getSkipBodyParsingPaths
|
|
@@ -415,11 +473,11 @@ injectRoutes(_: Router): void;
|
|
|
415
473
|
| --------- | -------- |
|
|
416
474
|
| `_` | `Router` |
|
|
417
475
|
|
|
418
|
-
#### Returns[](#returns-
|
|
476
|
+
#### Returns[](#returns-9 "Direct link to Returns")
|
|
419
477
|
|
|
420
478
|
`void`
|
|
421
479
|
|
|
422
|
-
#### Implementation of[](#implementation-of-
|
|
480
|
+
#### Implementation of[](#implementation-of-6 "Direct link to Implementation of")
|
|
423
481
|
|
|
424
482
|
```ts
|
|
425
483
|
BasePlugin.injectRoutes
|
|
@@ -442,7 +500,7 @@ protected registerEndpoint(name: string, path: string): void;
|
|
|
442
500
|
| `name` | `string` |
|
|
443
501
|
| `path` | `string` |
|
|
444
502
|
|
|
445
|
-
#### Returns[](#returns-
|
|
503
|
+
#### Returns[](#returns-10 "Direct link to Returns")
|
|
446
504
|
|
|
447
505
|
`void`
|
|
448
506
|
|
|
@@ -465,7 +523,7 @@ Returns the `x-forwarded-user` header when present. In development mode (`NODE_E
|
|
|
465
523
|
| --------- | --------- |
|
|
466
524
|
| `req` | `Request` |
|
|
467
525
|
|
|
468
|
-
#### Returns[](#returns-
|
|
526
|
+
#### Returns[](#returns-11 "Direct link to Returns")
|
|
469
527
|
|
|
470
528
|
`string`
|
|
471
529
|
|
|
@@ -495,7 +553,7 @@ protected route<_TResponse>(router: Router, config: RouteConfig): void;
|
|
|
495
553
|
| `router` | `Router` |
|
|
496
554
|
| `config` | `RouteConfig` |
|
|
497
555
|
|
|
498
|
-
#### Returns[](#returns-
|
|
556
|
+
#### Returns[](#returns-12 "Direct link to Returns")
|
|
499
557
|
|
|
500
558
|
`void`
|
|
501
559
|
|
|
@@ -508,11 +566,11 @@ setup(): Promise<void>;
|
|
|
508
566
|
|
|
509
567
|
```
|
|
510
568
|
|
|
511
|
-
#### Returns[](#returns-
|
|
569
|
+
#### Returns[](#returns-13 "Direct link to Returns")
|
|
512
570
|
|
|
513
571
|
`Promise`<`void`>
|
|
514
572
|
|
|
515
|
-
#### Implementation of[](#implementation-of-
|
|
573
|
+
#### Implementation of[](#implementation-of-7 "Direct link to Implementation of")
|
|
516
574
|
|
|
517
575
|
```ts
|
|
518
576
|
BasePlugin.setup
|