@prismiq/react 0.1.1 → 0.2.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.
Files changed (49) hide show
  1. package/dist/{CustomSQLEditor-d84v_Cgp.d.cts → ChatBubble-ARocmvZD.d.cts} +40 -3
  2. package/dist/{CustomSQLEditor-CYlOtecq.d.ts → ChatBubble-BN_CjIpk.d.ts} +40 -3
  3. package/dist/{DashboardDialog-CZD8I-6z.d.cts → DashboardDialog-UhUGXx2h.d.ts} +5 -3
  4. package/dist/{DashboardDialog-DBNTVVSp.d.ts → DashboardDialog-Z-HypxmG.d.cts} +5 -3
  5. package/dist/charts/index.d.cts +2 -2
  6. package/dist/charts/index.d.ts +2 -2
  7. package/dist/{chunk-VQDFS6VS.cjs → chunk-FKXCINUF.cjs} +368 -210
  8. package/dist/chunk-FKXCINUF.cjs.map +1 -0
  9. package/dist/{chunk-ET7GCREP.js → chunk-GELI7MDZ.js} +482 -7
  10. package/dist/chunk-GELI7MDZ.js.map +1 -0
  11. package/dist/{chunk-WWTT2OJ5.js → chunk-HKZFEXT6.js} +27 -9
  12. package/dist/chunk-HKZFEXT6.js.map +1 -0
  13. package/dist/{chunk-3LDRRDJ6.js → chunk-JBJ5LEAG.js} +186 -28
  14. package/dist/chunk-JBJ5LEAG.js.map +1 -0
  15. package/dist/{chunk-URJH4H6G.cjs → chunk-PG7QBH3G.cjs} +485 -6
  16. package/dist/chunk-PG7QBH3G.cjs.map +1 -0
  17. package/dist/{chunk-MDXGGZSW.cjs → chunk-ZYVN6XAZ.cjs} +35 -37
  18. package/dist/chunk-ZYVN6XAZ.cjs.map +1 -0
  19. package/dist/components/index.cjs +62 -54
  20. package/dist/components/index.d.cts +2 -2
  21. package/dist/components/index.d.ts +2 -2
  22. package/dist/components/index.js +1 -1
  23. package/dist/dashboard/index.cjs +34 -34
  24. package/dist/dashboard/index.d.cts +7 -5
  25. package/dist/dashboard/index.d.ts +7 -5
  26. package/dist/dashboard/index.js +2 -2
  27. package/dist/export/index.cjs +7 -7
  28. package/dist/export/index.d.cts +6 -4
  29. package/dist/export/index.d.ts +6 -4
  30. package/dist/export/index.js +1 -1
  31. package/dist/{index-CvKj3SWO.d.cts → index-B8DelfpL.d.cts} +1 -1
  32. package/dist/{index-DXGLs1yY.d.ts → index-RbfYPQD_.d.ts} +1 -1
  33. package/dist/index.cjs +119 -103
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +74 -7
  36. package/dist/index.d.ts +74 -7
  37. package/dist/index.js +5 -5
  38. package/dist/index.js.map +1 -1
  39. package/dist/{types-j0kPJ9Hz.d.cts → types-ccB9Ps3k.d.cts} +44 -1
  40. package/dist/{types-j0kPJ9Hz.d.ts → types-ccB9Ps3k.d.ts} +44 -1
  41. package/dist/utils/index.d.cts +1 -1
  42. package/dist/utils/index.d.ts +1 -1
  43. package/package.json +2 -6
  44. package/dist/chunk-3LDRRDJ6.js.map +0 -1
  45. package/dist/chunk-ET7GCREP.js.map +0 -1
  46. package/dist/chunk-MDXGGZSW.cjs.map +0 -1
  47. package/dist/chunk-URJH4H6G.cjs.map +0 -1
  48. package/dist/chunk-VQDFS6VS.cjs.map +0 -1
  49. package/dist/chunk-WWTT2OJ5.js.map +0 -1
@@ -2843,6 +2843,83 @@ var PrismiqClient = class {
2843
2843
  })
2844
2844
  });
2845
2845
  }
2846
+ // ============================================================================
2847
+ // LLM Methods
2848
+ // ============================================================================
2849
+ /**
2850
+ * Get the LLM agent status.
2851
+ *
2852
+ * @returns LLM status including enabled state, provider, and model.
2853
+ */
2854
+ async getLLMStatus() {
2855
+ return this.request("/llm/status");
2856
+ }
2857
+ /**
2858
+ * Stream a chat response from the LLM agent via SSE.
2859
+ *
2860
+ * @param message - User's message.
2861
+ * @param history - Previous conversation messages.
2862
+ * @param currentSql - Current SQL in the editor (for context).
2863
+ * @param signal - Optional AbortSignal for cancellation.
2864
+ * @yields StreamChunk objects as the response is generated.
2865
+ */
2866
+ async *streamChat(message, history, currentSql, signal) {
2867
+ const url = `${this.endpoint}/llm/chat`;
2868
+ const headers = {
2869
+ "Content-Type": "application/json",
2870
+ "X-Tenant-ID": this.tenantId
2871
+ };
2872
+ if (this.userId) headers["X-User-ID"] = this.userId;
2873
+ if (this.schemaName) headers["X-Schema-Name"] = this.schemaName;
2874
+ if (this.customHeaders) Object.assign(headers, this.customHeaders);
2875
+ if (this.getToken) {
2876
+ const token = await this.getToken();
2877
+ headers["Authorization"] = `Bearer ${token}`;
2878
+ }
2879
+ const response = await fetch(url, {
2880
+ method: "POST",
2881
+ headers,
2882
+ body: JSON.stringify({
2883
+ message,
2884
+ history,
2885
+ current_sql: currentSql
2886
+ }),
2887
+ signal
2888
+ });
2889
+ if (!response.ok) {
2890
+ throw new PrismiqError(
2891
+ `LLM chat failed: ${response.status} ${response.statusText}`,
2892
+ response.status
2893
+ );
2894
+ }
2895
+ const reader = response.body?.getReader();
2896
+ if (!reader) return;
2897
+ const decoder = new TextDecoder();
2898
+ let buffer = "";
2899
+ try {
2900
+ while (true) {
2901
+ const { done, value } = await reader.read();
2902
+ if (done) break;
2903
+ buffer += decoder.decode(value, { stream: true });
2904
+ const lines = buffer.split("\n");
2905
+ buffer = lines.pop() ?? "";
2906
+ for (const line of lines) {
2907
+ if (line.startsWith("data: ")) {
2908
+ const data = line.slice(6).trim();
2909
+ if (data) {
2910
+ try {
2911
+ const chunk = JSON.parse(data);
2912
+ yield chunk;
2913
+ } catch {
2914
+ }
2915
+ }
2916
+ }
2917
+ }
2918
+ }
2919
+ } finally {
2920
+ reader.releaseLock();
2921
+ }
2922
+ }
2846
2923
  };
2847
2924
  var AnalyticsContext = react.createContext(null);
2848
2925
  var CallbacksContext = react.createContext({});
@@ -3900,6 +3977,140 @@ function CrossFilterProvider({
3900
3977
  function useCrossFilterOptional() {
3901
3978
  return react.useContext(CrossFilterContext);
3902
3979
  }
3980
+
3981
+ // src/hooks/useLLMStatus.ts
3982
+ function useLLMStatus() {
3983
+ const { client } = useAnalytics();
3984
+ const [status, setStatus] = react.useState({ enabled: false });
3985
+ const [isLoading, setIsLoading] = react.useState(true);
3986
+ const [error, setError] = react.useState(null);
3987
+ react.useEffect(() => {
3988
+ if (!client) {
3989
+ setIsLoading(false);
3990
+ return;
3991
+ }
3992
+ let cancelled = false;
3993
+ client.getLLMStatus().then((result) => {
3994
+ if (!cancelled) {
3995
+ setStatus(result);
3996
+ setError(null);
3997
+ }
3998
+ }).catch((err) => {
3999
+ if (!cancelled) {
4000
+ setStatus({ enabled: false });
4001
+ setError(err instanceof Error ? err : new Error(String(err)));
4002
+ }
4003
+ }).finally(() => {
4004
+ if (!cancelled) setIsLoading(false);
4005
+ });
4006
+ return () => {
4007
+ cancelled = true;
4008
+ };
4009
+ }, [client]);
4010
+ return {
4011
+ enabled: status.enabled,
4012
+ provider: status.provider,
4013
+ model: status.model,
4014
+ isLoading,
4015
+ error
4016
+ };
4017
+ }
4018
+ function useLLMChat() {
4019
+ const { client } = useAnalytics();
4020
+ const [messages, setMessages] = react.useState([]);
4021
+ const [isStreaming, setIsStreaming] = react.useState(false);
4022
+ const [streamingContent, setStreamingContent] = react.useState("");
4023
+ const [suggestedSql, setSuggestedSql] = react.useState(null);
4024
+ const [error, setError] = react.useState(null);
4025
+ const abortRef = react.useRef(null);
4026
+ const isStreamingRef = react.useRef(false);
4027
+ const messagesRef = react.useRef([]);
4028
+ messagesRef.current = messages;
4029
+ const sendMessage = react.useCallback(
4030
+ async (message, currentSql) => {
4031
+ if (!client || isStreamingRef.current) return;
4032
+ abortRef.current?.abort();
4033
+ const controller = new AbortController();
4034
+ abortRef.current = controller;
4035
+ const userMsg = { role: "user", content: message };
4036
+ setMessages((prev) => [...prev, userMsg]);
4037
+ isStreamingRef.current = true;
4038
+ setIsStreaming(true);
4039
+ setStreamingContent("");
4040
+ setSuggestedSql(null);
4041
+ setError(null);
4042
+ let accumulatedText = "";
4043
+ let lastSql = null;
4044
+ try {
4045
+ const history = messagesRef.current.map((m) => ({
4046
+ role: m.role,
4047
+ content: m.content
4048
+ }));
4049
+ for await (const chunk of client.streamChat(
4050
+ message,
4051
+ history,
4052
+ currentSql,
4053
+ controller.signal
4054
+ )) {
4055
+ if (controller.signal.aborted) break;
4056
+ switch (chunk.type) {
4057
+ case "text":
4058
+ accumulatedText += chunk.content ?? "";
4059
+ setStreamingContent(accumulatedText);
4060
+ break;
4061
+ case "sql":
4062
+ lastSql = chunk.content ?? null;
4063
+ setSuggestedSql(lastSql);
4064
+ break;
4065
+ case "error":
4066
+ setError(chunk.content ?? "Unknown error");
4067
+ break;
4068
+ case "done":
4069
+ break;
4070
+ }
4071
+ }
4072
+ } catch (err) {
4073
+ if (err instanceof Error && err.name !== "AbortError") {
4074
+ setError(err.message);
4075
+ }
4076
+ } finally {
4077
+ isStreamingRef.current = false;
4078
+ setIsStreaming(false);
4079
+ if (accumulatedText) {
4080
+ const assistantMsg = {
4081
+ role: "assistant",
4082
+ content: accumulatedText
4083
+ };
4084
+ setMessages((prev) => [...prev, assistantMsg]);
4085
+ }
4086
+ setStreamingContent("");
4087
+ }
4088
+ },
4089
+ [client]
4090
+ );
4091
+ react.useEffect(() => {
4092
+ return () => {
4093
+ abortRef.current?.abort();
4094
+ };
4095
+ }, []);
4096
+ const clearHistory = react.useCallback(() => {
4097
+ abortRef.current?.abort();
4098
+ setMessages([]);
4099
+ setStreamingContent("");
4100
+ setSuggestedSql(null);
4101
+ setError(null);
4102
+ setIsStreaming(false);
4103
+ }, []);
4104
+ return {
4105
+ messages,
4106
+ isStreaming,
4107
+ streamingContent,
4108
+ suggestedSql,
4109
+ sendMessage,
4110
+ clearHistory,
4111
+ error
4112
+ };
4113
+ }
3903
4114
  var nodeStyles = {
3904
4115
  display: "flex",
3905
4116
  alignItems: "center",
@@ -4362,6 +4573,7 @@ function SchemaExplorer({
4362
4573
  selectedColumns = [],
4363
4574
  searchable = true,
4364
4575
  collapsible = true,
4576
+ headerAction,
4365
4577
  className,
4366
4578
  style
4367
4579
  }) {
@@ -4397,10 +4609,12 @@ function SchemaExplorer({
4397
4609
  style: { ...containerStyles4, ...style },
4398
4610
  role: "tree",
4399
4611
  "aria-label": "Database schema",
4612
+ "data-testid": "schema-explorer-root",
4400
4613
  children: [
4401
4614
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyles2, children: [
4402
4615
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "table", size: 16, style: { color: "var(--prismiq-color-primary)" } }),
4403
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: titleStyles2, children: "Schema Explorer" })
4616
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...titleStyles2, flex: 1 }, children: "Schema Explorer" }),
4617
+ headerAction
4404
4618
  ] }),
4405
4619
  searchable && /* @__PURE__ */ jsxRuntime.jsx("div", { style: searchContainerStyles, children: /* @__PURE__ */ jsxRuntime.jsx(
4406
4620
  Input,
@@ -4409,7 +4623,8 @@ function SchemaExplorer({
4409
4623
  placeholder: "Search tables and columns...",
4410
4624
  value: searchQuery,
4411
4625
  onChange: (e) => setSearchQuery(e.target.value),
4412
- style: { width: "100%" }
4626
+ style: { width: "100%" },
4627
+ "data-testid": "schema-explorer-search"
4413
4628
  }
4414
4629
  ) }),
4415
4630
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: treeContainerStyles, children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(LoadingSkeleton, {}) : error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: errorStyles2, children: [
@@ -8873,7 +9088,14 @@ function CustomSQLEditor({
8873
9088
  style
8874
9089
  }) {
8875
9090
  const [sql, setSql] = react.useState(initialSql);
9091
+ const prevInitialSqlRef = react.useRef(initialSql);
8876
9092
  const [isFocused, setIsFocused] = react.useState(false);
9093
+ react.useEffect(() => {
9094
+ if (initialSql !== prevInitialSqlRef.current) {
9095
+ prevInitialSqlRef.current = initialSql;
9096
+ setSql(initialSql);
9097
+ }
9098
+ }, [initialSql]);
8877
9099
  const [executeEnabled, setExecuteEnabled] = react.useState(false);
8878
9100
  const [lastExecutedSql, setLastExecutedSql] = react.useState(null);
8879
9101
  const {
@@ -8919,7 +9141,7 @@ function CustomSQLEditor({
8919
9141
  ...buttonStyles,
8920
9142
  ...canExecute ? {} : buttonDisabledStyles
8921
9143
  };
8922
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...containerStyles17, ...style }, children: [
9144
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...containerStyles17, ...style }, "data-testid": "custom-sql-editor", children: [
8923
9145
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: editorWrapperStyles, children: [
8924
9146
  /* @__PURE__ */ jsxRuntime.jsx(
8925
9147
  "textarea",
@@ -8933,7 +9155,8 @@ function CustomSQLEditor({
8933
9155
  style: mergedTextareaStyles,
8934
9156
  spellCheck: false,
8935
9157
  autoComplete: "off",
8936
- autoCapitalize: "off"
9158
+ autoCapitalize: "off",
9159
+ "data-testid": "custom-sql-textarea"
8937
9160
  }
8938
9161
  ),
8939
9162
  validation && !validation.valid && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: validationErrorStyles, children: [
@@ -8957,6 +9180,7 @@ function CustomSQLEditor({
8957
9180
  disabled: !canExecute,
8958
9181
  style: mergedButtonStyles,
8959
9182
  type: "button",
9183
+ "data-testid": "custom-sql-run-button",
8960
9184
  children: [
8961
9185
  isLoading ? "Executing..." : "Run Query",
8962
9186
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "11px", opacity: 0.7 }, children: "(Cmd+Enter)" })
@@ -9158,6 +9382,257 @@ function TableSelector({
9158
9382
  ] })
9159
9383
  ] });
9160
9384
  }
9385
+ function parseContent(content) {
9386
+ const startToken = "```sql";
9387
+ const endToken = "```";
9388
+ const parts = [];
9389
+ let cursor = 0;
9390
+ while (cursor < content.length) {
9391
+ const start = content.indexOf(startToken, cursor);
9392
+ if (start === -1) break;
9393
+ if (start > cursor) {
9394
+ parts.push({ type: "text", value: content.slice(cursor, start) });
9395
+ }
9396
+ const sqlStart = content.indexOf("\n", start + startToken.length);
9397
+ if (sqlStart === -1) break;
9398
+ const end = content.indexOf(endToken, sqlStart + 1);
9399
+ if (end === -1) break;
9400
+ const sql = content.slice(sqlStart + 1, end).trim();
9401
+ if (sql) {
9402
+ parts.push({ type: "sql", value: sql });
9403
+ }
9404
+ cursor = end + endToken.length;
9405
+ }
9406
+ if (cursor < content.length) {
9407
+ parts.push({ type: "text", value: content.slice(cursor) });
9408
+ }
9409
+ return parts;
9410
+ }
9411
+ function ChatBubble({ message, onApplySql }) {
9412
+ const { theme } = chunkLMTG3LRC_cjs.useTheme();
9413
+ const isUser = message.role === "user";
9414
+ const parts = react.useMemo(() => parseContent(message.content), [message.content]);
9415
+ const bubbleStyle = {
9416
+ maxWidth: "85%",
9417
+ padding: `${theme.spacing.sm} ${theme.spacing.md}`,
9418
+ borderRadius: theme.radius.md,
9419
+ fontSize: theme.fontSizes.sm,
9420
+ lineHeight: 1.5,
9421
+ whiteSpace: "pre-wrap",
9422
+ wordBreak: "break-word",
9423
+ alignSelf: isUser ? "flex-end" : "flex-start",
9424
+ backgroundColor: isUser ? theme.colors.primary : theme.colors.surface,
9425
+ color: isUser ? "#fff" : theme.colors.text,
9426
+ border: isUser ? "none" : `1px solid ${theme.colors.border}`
9427
+ };
9428
+ const sqlBlockStyle = {
9429
+ backgroundColor: isUser ? "rgba(0,0,0,0.2)" : theme.colors.background,
9430
+ borderRadius: theme.radius.sm,
9431
+ padding: theme.spacing.sm,
9432
+ margin: `${theme.spacing.xs} 0`,
9433
+ fontFamily: theme.fonts.mono,
9434
+ fontSize: theme.fontSizes.xs,
9435
+ overflow: "auto",
9436
+ position: "relative"
9437
+ };
9438
+ const applyBtnContainerStyle = {
9439
+ display: "flex",
9440
+ justifyContent: "flex-end",
9441
+ marginTop: theme.spacing.xs
9442
+ };
9443
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: bubbleStyle, children: parts.map((part, i) => {
9444
+ if (part.type === "sql") {
9445
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": `chat-sql-${i}`, children: [
9446
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { style: sqlBlockStyle, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: part.value }) }),
9447
+ onApplySql && /* @__PURE__ */ jsxRuntime.jsx("div", { style: applyBtnContainerStyle, children: /* @__PURE__ */ jsxRuntime.jsx(
9448
+ Button,
9449
+ {
9450
+ variant: "ghost",
9451
+ size: "sm",
9452
+ onClick: () => onApplySql(part.value),
9453
+ "data-testid": `apply-sql-btn-${i}`,
9454
+ children: "Apply to Editor"
9455
+ }
9456
+ ) })
9457
+ ] }, i);
9458
+ }
9459
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: part.value }, i);
9460
+ }) });
9461
+ }
9462
+ function ChatPanel({ currentSql, onApplySql }) {
9463
+ const { theme } = chunkLMTG3LRC_cjs.useTheme();
9464
+ const {
9465
+ messages,
9466
+ isStreaming,
9467
+ streamingContent,
9468
+ suggestedSql,
9469
+ sendMessage,
9470
+ clearHistory,
9471
+ error
9472
+ } = useLLMChat();
9473
+ const [input, setInput] = react.useState("");
9474
+ const messagesEndRef = react.useRef(null);
9475
+ react.useEffect(() => {
9476
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
9477
+ }, [messages, streamingContent]);
9478
+ const handleSend = react.useCallback(() => {
9479
+ const trimmed = input.trim();
9480
+ if (!trimmed) return;
9481
+ setInput("");
9482
+ void sendMessage(trimmed, currentSql);
9483
+ }, [input, currentSql, sendMessage]);
9484
+ const handleKeyDown = react.useCallback(
9485
+ (e) => {
9486
+ if (e.key === "Enter" && !e.shiftKey) {
9487
+ e.preventDefault();
9488
+ handleSend();
9489
+ }
9490
+ },
9491
+ [handleSend]
9492
+ );
9493
+ const containerStyle = {
9494
+ display: "flex",
9495
+ flexDirection: "column",
9496
+ height: "100%",
9497
+ borderLeft: `1px solid ${theme.colors.border}`,
9498
+ backgroundColor: theme.colors.background
9499
+ };
9500
+ const headerStyle = {
9501
+ display: "flex",
9502
+ alignItems: "center",
9503
+ justifyContent: "space-between",
9504
+ padding: `${theme.spacing.sm} ${theme.spacing.md}`,
9505
+ borderBottom: `1px solid ${theme.colors.border}`,
9506
+ flexShrink: 0
9507
+ };
9508
+ const headerTitleStyle = {
9509
+ display: "flex",
9510
+ alignItems: "center",
9511
+ gap: theme.spacing.xs,
9512
+ fontSize: theme.fontSizes.sm,
9513
+ fontWeight: 600,
9514
+ color: theme.colors.text
9515
+ };
9516
+ const messagesStyle = {
9517
+ flex: 1,
9518
+ overflow: "auto",
9519
+ padding: theme.spacing.md,
9520
+ display: "flex",
9521
+ flexDirection: "column",
9522
+ gap: theme.spacing.md
9523
+ };
9524
+ const streamingStyle = {
9525
+ alignSelf: "flex-start",
9526
+ maxWidth: "85%",
9527
+ padding: `${theme.spacing.sm} ${theme.spacing.md}`,
9528
+ borderRadius: theme.radius.md,
9529
+ fontSize: theme.fontSizes.sm,
9530
+ lineHeight: 1.5,
9531
+ whiteSpace: "pre-wrap",
9532
+ wordBreak: "break-word",
9533
+ backgroundColor: theme.colors.surface,
9534
+ color: theme.colors.text,
9535
+ border: `1px solid ${theme.colors.border}`
9536
+ };
9537
+ const suggestedSqlStyle = {
9538
+ padding: theme.spacing.sm,
9539
+ borderTop: `1px solid ${theme.colors.border}`,
9540
+ display: "flex",
9541
+ alignItems: "center",
9542
+ justifyContent: "center",
9543
+ flexShrink: 0
9544
+ };
9545
+ const inputAreaStyle = {
9546
+ display: "flex",
9547
+ gap: theme.spacing.sm,
9548
+ padding: theme.spacing.sm,
9549
+ borderTop: `1px solid ${theme.colors.border}`,
9550
+ flexShrink: 0
9551
+ };
9552
+ const textareaStyle = {
9553
+ flex: 1,
9554
+ resize: "none",
9555
+ border: `1px solid ${theme.colors.border}`,
9556
+ borderRadius: theme.radius.sm,
9557
+ padding: theme.spacing.sm,
9558
+ fontSize: theme.fontSizes.sm,
9559
+ fontFamily: theme.fonts.sans,
9560
+ backgroundColor: theme.colors.surface,
9561
+ color: theme.colors.text,
9562
+ outline: "none",
9563
+ minHeight: "36px",
9564
+ maxHeight: "120px"
9565
+ };
9566
+ const emptyStyle = {
9567
+ flex: 1,
9568
+ display: "flex",
9569
+ alignItems: "center",
9570
+ justifyContent: "center",
9571
+ textAlign: "center",
9572
+ padding: theme.spacing.lg,
9573
+ color: theme.colors.textMuted,
9574
+ fontSize: theme.fontSizes.sm
9575
+ };
9576
+ const errorStyle = {
9577
+ padding: theme.spacing.sm,
9578
+ margin: `0 ${theme.spacing.md}`,
9579
+ borderRadius: theme.radius.sm,
9580
+ backgroundColor: "rgba(239, 68, 68, 0.1)",
9581
+ color: "#ef4444",
9582
+ fontSize: theme.fontSizes.xs,
9583
+ flexShrink: 0
9584
+ };
9585
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle, className: "prismiq-chat-panel", "data-testid": "chat-panel-root", children: [
9586
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
9587
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerTitleStyle, children: [
9588
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "edit", size: 16 }),
9589
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "SQL Assistant" })
9590
+ ] }),
9591
+ messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", onClick: clearHistory, "data-testid": "chat-clear", children: "Clear" })
9592
+ ] }),
9593
+ messages.length === 0 && !isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: emptyStyle, "data-testid": "chat-empty", children: [
9594
+ "Ask me to help write SQL queries.",
9595
+ "\n",
9596
+ "I can see your database schema and validate queries."
9597
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: messagesStyle, "data-testid": "chat-messages", children: [
9598
+ messages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(ChatBubble, { message: msg, onApplySql }, i)),
9599
+ isStreaming && streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: streamingStyle, "data-testid": "chat-streaming", children: [
9600
+ streamingContent,
9601
+ "\u258D"
9602
+ ] }),
9603
+ isStreaming && !streamingContent && /* @__PURE__ */ jsxRuntime.jsx("div", { style: streamingStyle, "data-testid": "chat-streaming", children: "Thinking..." }),
9604
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
9605
+ ] }),
9606
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorStyle, "data-testid": "chat-error", children: error }),
9607
+ suggestedSql && !isStreaming && /* @__PURE__ */ jsxRuntime.jsx("div", { style: suggestedSqlStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: () => onApplySql(suggestedSql), "data-testid": "chat-apply-sql", children: "Apply SQL to Editor" }) }),
9608
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: inputAreaStyle, children: [
9609
+ /* @__PURE__ */ jsxRuntime.jsx(
9610
+ "textarea",
9611
+ {
9612
+ style: textareaStyle,
9613
+ value: input,
9614
+ onChange: (e) => setInput(e.target.value),
9615
+ onKeyDown: handleKeyDown,
9616
+ placeholder: "Ask about your data...",
9617
+ rows: 1,
9618
+ disabled: isStreaming,
9619
+ "data-testid": "chat-input"
9620
+ }
9621
+ ),
9622
+ /* @__PURE__ */ jsxRuntime.jsx(
9623
+ Button,
9624
+ {
9625
+ variant: "primary",
9626
+ size: "sm",
9627
+ onClick: handleSend,
9628
+ disabled: isStreaming || !input.trim(),
9629
+ "data-testid": "chat-send",
9630
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "play", size: 16 })
9631
+ }
9632
+ )
9633
+ ] })
9634
+ ] });
9635
+ }
9161
9636
 
9162
9637
  exports.AggregationPicker = AggregationPicker;
9163
9638
  exports.AnalyticsProvider = AnalyticsProvider;
@@ -9165,6 +9640,8 @@ exports.AutoSaveIndicator = AutoSaveIndicator;
9165
9640
  exports.Badge = Badge;
9166
9641
  exports.Button = Button;
9167
9642
  exports.CalculatedFieldBuilder = CalculatedFieldBuilder;
9643
+ exports.ChatBubble = ChatBubble;
9644
+ exports.ChatPanel = ChatPanel;
9168
9645
  exports.Checkbox = Checkbox;
9169
9646
  exports.CollapsibleSection = CollapsibleSection;
9170
9647
  exports.ColorPaletteSelector = ColorPaletteSelector;
@@ -9228,10 +9705,12 @@ exports.useDashboardMutations = useDashboardMutations;
9228
9705
  exports.useDashboardPinStatus = useDashboardPinStatus;
9229
9706
  exports.useDashboards = useDashboards;
9230
9707
  exports.useDebouncedLayoutSave = useDebouncedLayoutSave;
9708
+ exports.useLLMChat = useLLMChat;
9709
+ exports.useLLMStatus = useLLMStatus;
9231
9710
  exports.usePinMutations = usePinMutations;
9232
9711
  exports.usePinnedDashboards = usePinnedDashboards;
9233
9712
  exports.useQuery = useQuery;
9234
9713
  exports.useSavedQueries = useSavedQueries;
9235
9714
  exports.useSchema = useSchema;
9236
- //# sourceMappingURL=chunk-URJH4H6G.cjs.map
9237
- //# sourceMappingURL=chunk-URJH4H6G.cjs.map
9715
+ //# sourceMappingURL=chunk-PG7QBH3G.cjs.map
9716
+ //# sourceMappingURL=chunk-PG7QBH3G.cjs.map