@useatlas/react 0.0.1 → 0.0.2

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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -2
  3. package/dist/{chunk-5SEVKHS5.cjs → chunk-35SCTKSW.js} +100 -7
  4. package/dist/chunk-35SCTKSW.js.map +1 -0
  5. package/dist/{chunk-UIRB6L36.cjs → chunk-DZFSZSQB.cjs} +46 -54
  6. package/dist/chunk-DZFSZSQB.cjs.map +1 -0
  7. package/dist/{chunk-2WFDP7G5.js → chunk-FMSGREKS.js} +46 -54
  8. package/dist/chunk-FMSGREKS.js.map +1 -0
  9. package/dist/{chunk-44HBZYKP.js → chunk-IDXGFWFS.cjs} +109 -3
  10. package/dist/chunk-IDXGFWFS.cjs.map +1 -0
  11. package/dist/global.d.ts +36 -0
  12. package/dist/hooks.cjs +10 -10
  13. package/dist/hooks.cjs.map +1 -1
  14. package/dist/hooks.d.cts +2 -2
  15. package/dist/hooks.d.ts +2 -2
  16. package/dist/hooks.js +3 -3
  17. package/dist/hooks.js.map +1 -1
  18. package/dist/index.cjs +385 -265
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +224 -4
  21. package/dist/index.d.ts +224 -4
  22. package/dist/index.js +328 -208
  23. package/dist/index.js.map +1 -1
  24. package/dist/lib/widget-types.d.ts +232 -0
  25. package/dist/{result-chart-YLCKBNV4.cjs → result-chart-ANZOT6FL.cjs} +24 -34
  26. package/dist/result-chart-ANZOT6FL.cjs.map +1 -0
  27. package/dist/{result-chart-NFAJ4IQ5.js → result-chart-C3EJTN5G.js} +22 -32
  28. package/dist/result-chart-C3EJTN5G.js.map +1 -0
  29. package/dist/widget.css +2 -2
  30. package/dist/widget.js +215 -246
  31. package/package.json +26 -16
  32. package/src/components/__tests__/data-table.test.tsx +125 -0
  33. package/src/components/actions/action-approval-card.tsx +26 -19
  34. package/src/components/actions/action-status-badge.tsx +3 -3
  35. package/src/components/atlas-chat.tsx +97 -37
  36. package/src/components/chart/result-chart.tsx +13 -37
  37. package/src/components/chat/api-key-bar.tsx +4 -4
  38. package/src/components/chat/data-table.tsx +42 -3
  39. package/src/components/chat/error-banner.tsx +108 -5
  40. package/src/components/chat/follow-up-chips.tsx +1 -1
  41. package/src/components/chat/managed-auth-card.tsx +6 -6
  42. package/src/components/conversations/conversation-item.tsx +19 -14
  43. package/src/components/conversations/conversation-list.tsx +3 -3
  44. package/src/components/conversations/conversation-sidebar.tsx +15 -4
  45. package/src/components/conversations/delete-confirmation.tsx +2 -2
  46. package/src/components/error-boundary.tsx +66 -0
  47. package/src/components/schema-explorer/schema-explorer.tsx +4 -0
  48. package/src/env.d.ts +9 -7
  49. package/src/global.d.ts +36 -0
  50. package/src/hooks/__tests__/use-atlas-conversations.test.tsx +4 -6
  51. package/src/hooks/use-atlas-chat.ts +1 -1
  52. package/src/hooks/use-atlas-conversations.ts +2 -2
  53. package/src/hooks/use-conversations.ts +60 -68
  54. package/src/index.ts +8 -0
  55. package/src/lib/action-types.ts +2 -2
  56. package/src/lib/helpers.ts +16 -16
  57. package/src/lib/types.ts +3 -2
  58. package/src/lib/widget-types.ts +232 -0
  59. package/src/test-setup.ts +2 -2
  60. package/dist/chunk-2WFDP7G5.js.map +0 -1
  61. package/dist/chunk-44HBZYKP.js.map +0 -1
  62. package/dist/chunk-5SEVKHS5.cjs.map +0 -1
  63. package/dist/chunk-UIRB6L36.cjs.map +0 -1
  64. package/dist/result-chart-NFAJ4IQ5.js.map +0 -1
  65. package/dist/result-chart-YLCKBNV4.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,19 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var chunkUIRB6L36_cjs = require('./chunk-UIRB6L36.cjs');
4
- var chunk5SEVKHS5_cjs = require('./chunk-5SEVKHS5.cjs');
3
+ var chunkDZFSZSQB_cjs = require('./chunk-DZFSZSQB.cjs');
4
+ var chunkIDXGFWFS_cjs = require('./chunk-IDXGFWFS.cjs');
5
5
  var react = require('@ai-sdk/react');
6
6
  var ai = require('ai');
7
7
  var React = require('react');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
- var action = require('@useatlas/types/action');
10
9
  var lucideReact = require('lucide-react');
10
+ var action = require('@useatlas/types/action');
11
11
  var ReactMarkdown = require('react-markdown');
12
12
  var remarkGfm = require('remark-gfm');
13
- var classVarianceAuthority = require('class-variance-authority');
14
13
  var radixUi = require('radix-ui');
15
- var clsx = require('clsx');
16
- var tailwindMerge = require('tailwind-merge');
14
+ var classVarianceAuthority = require('class-variance-authority');
17
15
 
18
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
17
 
@@ -67,9 +65,30 @@ function ActionAuthProvider({
67
65
  );
68
66
  return /* @__PURE__ */ jsxRuntime.jsx(ActionAuthContext.Provider, { value, children });
69
67
  }
70
- function ErrorBanner({ error, authMode }) {
71
- const info = React.useMemo(() => chunkUIRB6L36_cjs.parseChatError(error, authMode), [error, authMode]);
68
+ function ErrorIcon({ clientCode }) {
69
+ switch (clientCode) {
70
+ case "offline":
71
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.WifiOff, { className: "size-4 shrink-0" });
72
+ case "api_unreachable":
73
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ServerCrash, { className: "size-4 shrink-0" });
74
+ case "auth_failure":
75
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldAlert, { className: "size-4 shrink-0" });
76
+ case "rate_limited_http":
77
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "size-4 shrink-0" });
78
+ case "server_error":
79
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ServerCrash, { className: "size-4 shrink-0" });
80
+ default:
81
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "size-4 shrink-0" });
82
+ }
83
+ }
84
+ function ErrorBanner({
85
+ error,
86
+ authMode,
87
+ onRetry
88
+ }) {
89
+ const info = React.useMemo(() => chunkDZFSZSQB_cjs.parseChatError(error, authMode), [error, authMode]);
72
90
  const [countdown, setCountdown] = React.useState(info.retryAfterSeconds ?? 0);
91
+ const [restoredOnline, setRestoredOnline] = React.useState(false);
73
92
  React.useEffect(() => {
74
93
  if (!info.retryAfterSeconds) return;
75
94
  setCountdown(info.retryAfterSeconds);
@@ -84,11 +103,74 @@ function ErrorBanner({ error, authMode }) {
84
103
  }, 1e3);
85
104
  return () => clearInterval(interval);
86
105
  }, [info.retryAfterSeconds]);
106
+ React.useEffect(() => {
107
+ if (info.retryAfterSeconds && countdown === 0 && onRetry) {
108
+ onRetry();
109
+ }
110
+ }, [countdown, info.retryAfterSeconds, onRetry]);
111
+ React.useEffect(() => {
112
+ if (info.clientCode !== "offline") return;
113
+ function handleOnline() {
114
+ setRestoredOnline(true);
115
+ onRetry?.();
116
+ }
117
+ window.addEventListener("online", handleOnline);
118
+ return () => {
119
+ window.removeEventListener("online", handleOnline);
120
+ };
121
+ }, [info.clientCode, onRetry]);
122
+ React.useEffect(() => {
123
+ try {
124
+ if (typeof window !== "undefined" && window.parent !== window) {
125
+ window.parent.postMessage(
126
+ {
127
+ type: "atlas:error",
128
+ error: {
129
+ code: info.clientCode ?? info.code ?? "unknown",
130
+ message: info.title,
131
+ detail: info.detail,
132
+ retryable: info.retryable
133
+ }
134
+ },
135
+ "*"
136
+ );
137
+ }
138
+ } catch {
139
+ }
140
+ }, [info.clientCode, info.code, info.title, info.detail, info.retryable]);
141
+ if (info.clientCode === "offline" && restoredOnline) {
142
+ return null;
143
+ }
87
144
  const detail = info.retryAfterSeconds && countdown > 0 ? `Try again in ${countdown} second${countdown !== 1 ? "s" : ""}.` : info.detail;
88
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 rounded-lg border border-red-300 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/20 dark:text-red-400 px-4 py-3 text-sm", children: [
89
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: info.title }),
90
- detail && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs opacity-80", children: detail })
91
- ] });
145
+ const showRetry = info.retryable && onRetry && countdown === 0 && info.clientCode !== "offline";
146
+ return /* @__PURE__ */ jsxRuntime.jsx(
147
+ "div",
148
+ {
149
+ className: "mb-2 rounded-lg border border-red-300 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/20 dark:text-red-400 px-4 py-3 text-sm",
150
+ role: "alert",
151
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
152
+ /* @__PURE__ */ jsxRuntime.jsx(ErrorIcon, { clientCode: info.clientCode }),
153
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
154
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: info.title }),
155
+ detail && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs opacity-80", children: detail }),
156
+ info.requestId && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-xs opacity-60", children: [
157
+ "Request ID: ",
158
+ info.requestId
159
+ ] }),
160
+ showRetry && /* @__PURE__ */ jsxRuntime.jsx(
161
+ chunkIDXGFWFS_cjs.Button,
162
+ {
163
+ variant: "link",
164
+ size: "sm",
165
+ onClick: onRetry,
166
+ className: "mt-2 h-auto p-0 text-xs font-medium text-red-700 dark:text-red-400",
167
+ children: "Try again"
168
+ }
169
+ )
170
+ ] })
171
+ ] })
172
+ }
173
+ );
92
174
  }
93
175
  function ApiKeyBar({
94
176
  apiKey,
@@ -106,7 +188,7 @@ function ApiKeyBar({
106
188
  setDraft(apiKey);
107
189
  setEditing(true);
108
190
  },
109
- className: "rounded border border-zinc-200 px-2 py-0.5 text-zinc-500 transition-colors hover:border-zinc-400 hover:text-zinc-800 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
191
+ className: "rounded border border-zinc-200 px-2 py-0.5 text-zinc-500 transition-colors hover:border-zinc-400 hover:text-zinc-800 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
110
192
  children: "Change"
111
193
  }
112
194
  )
@@ -131,7 +213,7 @@ function ApiKeyBar({
131
213
  value: draft,
132
214
  onChange: (e) => setDraft(e.target.value),
133
215
  placeholder: "Enter your API key...",
134
- className: "flex-1 bg-transparent text-xs text-zinc-900 placeholder-zinc-400 outline-none dark:text-zinc-100 dark:placeholder-zinc-600",
216
+ className: "flex-1 bg-transparent text-xs text-zinc-900 placeholder-zinc-400 outline-none focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:text-zinc-100 dark:placeholder-zinc-600",
135
217
  autoFocus: true
136
218
  }
137
219
  ),
@@ -140,7 +222,7 @@ function ApiKeyBar({
140
222
  {
141
223
  type: "submit",
142
224
  disabled: !draft.trim(),
143
- className: "rounded border border-zinc-200 px-2 py-0.5 text-xs text-zinc-500 transition-colors hover:border-zinc-400 hover:text-zinc-800 disabled:opacity-40 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
225
+ className: "rounded border border-zinc-200 px-2 py-0.5 text-xs text-zinc-500 transition-colors hover:border-zinc-400 hover:text-zinc-800 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-40 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
144
226
  children: "Save"
145
227
  }
146
228
  ),
@@ -149,7 +231,7 @@ function ApiKeyBar({
149
231
  {
150
232
  type: "button",
151
233
  onClick: () => setEditing(false),
152
- className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300",
234
+ className: "rounded text-xs text-zinc-400 hover:text-zinc-600 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:hover:text-zinc-300",
153
235
  children: "Cancel"
154
236
  }
155
237
  )
@@ -206,7 +288,7 @@ function ManagedAuthCard() {
206
288
  value: name,
207
289
  onChange: (e) => setName(e.target.value),
208
290
  placeholder: "Name (optional)",
209
- className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
291
+ className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus-visible:border-blue-500 focus-visible:ring-[3px] focus-visible:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
210
292
  }
211
293
  ),
212
294
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -217,7 +299,7 @@ function ManagedAuthCard() {
217
299
  onChange: (e) => setEmail(e.target.value),
218
300
  placeholder: "Email",
219
301
  required: true,
220
- className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
302
+ className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus-visible:border-blue-500 focus-visible:ring-[3px] focus-visible:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
221
303
  }
222
304
  ),
223
305
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -229,7 +311,7 @@ function ManagedAuthCard() {
229
311
  placeholder: "Password",
230
312
  required: true,
231
313
  minLength: 8,
232
- className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
314
+ className: "w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 outline-none focus-visible:border-blue-500 focus-visible:ring-[3px] focus-visible:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600"
233
315
  }
234
316
  ),
235
317
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-600 dark:text-red-400", children: error }),
@@ -238,7 +320,7 @@ function ManagedAuthCard() {
238
320
  {
239
321
  type: "submit",
240
322
  disabled: loading,
241
- className: "w-full rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-500 disabled:opacity-40",
323
+ className: "w-full rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-500 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-blue-500/50 disabled:opacity-40",
242
324
  children: loading ? "..." : view === "login" ? "Sign in" : "Create account"
243
325
  }
244
326
  )
@@ -249,14 +331,14 @@ function ManagedAuthCard() {
249
331
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => {
250
332
  setView("signup");
251
333
  setError("");
252
- }, className: "text-blue-600 hover:underline dark:text-blue-400", children: "Create one" })
334
+ }, className: "rounded text-blue-600 hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-blue-500/50 dark:text-blue-400", children: "Create one" })
253
335
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
254
336
  "Already have an account?",
255
337
  " ",
256
338
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => {
257
339
  setView("login");
258
340
  setError("");
259
- }, className: "text-blue-600 hover:underline dark:text-blue-400", children: "Sign in" })
341
+ }, className: "rounded text-blue-600 hover:underline focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-blue-500/50 dark:text-blue-400", children: "Sign in" })
260
342
  ] }) })
261
343
  ] }) });
262
344
  }
@@ -316,7 +398,7 @@ function downloadCSV(csv, filename = "atlas-results.csv") {
316
398
  a.download = filename;
317
399
  a.click();
318
400
  } catch (err) {
319
- console.error("CSV download failed:", err);
401
+ console.error("CSV download failed:", err instanceof Error ? err.message : String(err));
320
402
  window.alert("CSV download failed");
321
403
  } finally {
322
404
  if (url) {
@@ -335,31 +417,31 @@ function coerceExcelCell(v) {
335
417
  return String(v);
336
418
  }
337
419
  async function downloadExcel(columns, rows, filename = "atlas-results.xlsx") {
338
- let XLSX;
420
+ let ExcelJS;
339
421
  try {
340
- XLSX = await import(
341
- 'xlsx'
422
+ ExcelJS = await import(
423
+ 'exceljs'
342
424
  /* webpackIgnore: true */
343
425
  );
344
426
  } catch (err) {
345
- console.error("Failed to load xlsx library:", err);
427
+ console.error("Failed to load exceljs library:", err instanceof Error ? err.message : String(err));
346
428
  window.alert("Excel export is unavailable. The spreadsheet library failed to load.");
347
429
  return;
348
430
  }
349
431
  let url = null;
350
432
  try {
351
- const data = rows.map((row) => {
433
+ const wb = new ExcelJS.Workbook();
434
+ const ws = wb.addWorksheet("Results");
435
+ ws.columns = columns.map((col) => ({ header: col, key: col }));
436
+ for (const row of rows) {
352
437
  const obj = {};
353
438
  for (const col of columns) {
354
439
  obj[col] = coerceExcelCell(row[col]);
355
440
  }
356
- return obj;
357
- });
358
- const ws = XLSX.utils.json_to_sheet(data, { header: columns });
359
- const wb = XLSX.utils.book_new();
360
- XLSX.utils.book_append_sheet(wb, ws, "Results");
361
- const wbOut = XLSX.write(wb, { bookType: "xlsx", type: "array" });
362
- const blob = new Blob([wbOut], {
441
+ ws.addRow(obj);
442
+ }
443
+ const buffer = await wb.xlsx.writeBuffer();
444
+ const blob = new Blob([buffer], {
363
445
  type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
364
446
  });
365
447
  url = URL.createObjectURL(blob);
@@ -368,8 +450,8 @@ async function downloadExcel(columns, rows, filename = "atlas-results.xlsx") {
368
450
  a.download = filename;
369
451
  a.click();
370
452
  } catch (err) {
371
- console.error("Excel download failed:", err);
372
- const detail = err instanceof Error ? err.message : "Unknown error";
453
+ const detail = err instanceof Error ? err.message : String(err);
454
+ console.error("Excel download failed:", detail);
373
455
  window.alert(`Excel download failed: ${detail}
374
456
 
375
457
  You can try the CSV download as an alternative.`);
@@ -433,13 +515,19 @@ function LoadingCard({ label }) {
433
515
  label
434
516
  ] });
435
517
  }
436
- function DataTable({
518
+ function DataTableInner({
437
519
  columns,
438
520
  rows,
439
521
  maxRows = 10
440
522
  }) {
441
523
  const [sortCol, setSortCol] = React.useState(null);
442
524
  const [sortDir, setSortDir] = React.useState("asc");
525
+ if (rows.length === 0) {
526
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-zinc-200 px-4 py-8 text-center dark:border-zinc-700", children: [
527
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-zinc-500 dark:text-zinc-400", children: "Query returned no results" }),
528
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-zinc-400 dark:text-zinc-500", children: "Try adjusting your query filters or criteria" })
529
+ ] });
530
+ }
443
531
  const hasMore = rows.length > maxRows;
444
532
  const cell = (row, colIdx) => {
445
533
  if (Array.isArray(row)) return row[colIdx];
@@ -482,8 +570,17 @@ function DataTable({
482
570
  /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-zinc-200 bg-zinc-100/80 dark:border-zinc-700 dark:bg-zinc-800/80", children: columns.map((col, i) => /* @__PURE__ */ jsxRuntime.jsxs(
483
571
  "th",
484
572
  {
573
+ tabIndex: 0,
574
+ role: "columnheader",
575
+ "aria-sort": sortCol === i ? sortDir === "asc" ? "ascending" : "descending" : "none",
485
576
  onClick: () => handleSort(i),
486
- className: "group cursor-pointer select-none whitespace-nowrap px-3 py-2 text-left font-medium text-zinc-500 transition-colors hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200",
577
+ onKeyDown: (e) => {
578
+ if (e.key === "Enter" || e.key === " ") {
579
+ e.preventDefault();
580
+ handleSort(i);
581
+ }
582
+ },
583
+ className: "group cursor-pointer select-none whitespace-nowrap px-3 py-2 text-left font-medium text-zinc-500 transition-colors hover:text-zinc-800 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:text-zinc-400 dark:hover:text-zinc-200",
487
584
  children: [
488
585
  col,
489
586
  sortCol === i ? sortDir === "asc" ? " \u25B2" : " \u25BC" : ""
@@ -509,6 +606,18 @@ function DataTable({
509
606
  ] })
510
607
  ] });
511
608
  }
609
+ function DataTable(props) {
610
+ return /* @__PURE__ */ jsxRuntime.jsx(
611
+ chunkIDXGFWFS_cjs.ErrorBoundary,
612
+ {
613
+ fallbackRender: (_error, reset) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700 dark:border-red-900/50 dark:bg-red-950/20 dark:text-red-400", children: [
614
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Unable to render results." }),
615
+ /* @__PURE__ */ jsxRuntime.jsx(chunkIDXGFWFS_cjs.Button, { variant: "link", onClick: reset, children: "Retry" })
616
+ ] }),
617
+ children: /* @__PURE__ */ jsxRuntime.jsx(DataTableInner, { ...props })
618
+ }
619
+ );
620
+ }
512
621
  function CopyButton({ text, label = "Copy" }) {
513
622
  const [state, setState] = React.useState("idle");
514
623
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -538,7 +647,7 @@ var SQL_BLOCK_STYLE = {
538
647
  padding: "0.75rem 1rem"
539
648
  };
540
649
  function SQLBlock({ sql }) {
541
- const dark = React.useContext(chunkUIRB6L36_cjs.DarkModeContext);
650
+ const dark = React.useContext(chunkDZFSZSQB_cjs.DarkModeContext);
542
651
  const [mod, setMod] = React.useState(_cache);
543
652
  React.useEffect(() => {
544
653
  if (_cache) return;
@@ -563,13 +672,13 @@ function SQLBlock({ sql }) {
563
672
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-2 top-2", children: /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { text: sql, label: "Copy SQL" }) })
564
673
  ] });
565
674
  }
566
- var ResultChart = React.lazy(() => import('./result-chart-YLCKBNV4.cjs').then((m) => ({ default: m.ResultChart })));
675
+ var ResultChart = React.lazy(() => import('./result-chart-ANZOT6FL.cjs').then((m) => ({ default: m.ResultChart })));
567
676
  var ChartFallback = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-64 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" });
568
677
  function toStringRows(columns, rows) {
569
678
  return rows.map((row) => columns.map((col) => row[col] == null ? "" : String(row[col])));
570
679
  }
571
680
  function SQLResultCard({ part }) {
572
- const dark = React.useContext(chunkUIRB6L36_cjs.DarkModeContext);
681
+ const dark = React.useContext(chunkDZFSZSQB_cjs.DarkModeContext);
573
682
  const args = getToolArgs(part);
574
683
  const result = getToolResult(part);
575
684
  const done = isToolComplete(part);
@@ -587,7 +696,7 @@ function SQLResultCard({ part }) {
587
696
  const sql = String(args.sql ?? "");
588
697
  const stringRows = React.useMemo(() => toStringRows(columns, rows), [columns, rows]);
589
698
  const chartResult = React.useMemo(
590
- () => columns.length > 0 ? chunk5SEVKHS5_cjs.detectCharts(columns, stringRows) : { chartable: false, columns: [] },
699
+ () => columns.length > 0 ? chunkIDXGFWFS_cjs.detectCharts(columns, stringRows) : { chartable: false, columns: [] },
591
700
  [columns, stringRows]
592
701
  );
593
702
  if (!done) return /* @__PURE__ */ jsxRuntime.jsx(LoadingCard, { label: "Executing query..." });
@@ -734,7 +843,8 @@ function borderColor(status) {
734
843
  case "denied":
735
844
  case "failed":
736
845
  return "border-red-300 dark:border-red-900/50";
737
- default:
846
+ case "rolled_back":
847
+ case "timed_out":
738
848
  return "border-zinc-200 dark:border-zinc-700";
739
849
  }
740
850
  }
@@ -756,7 +866,7 @@ function ActionApprovalCard({ part }) {
756
866
  const effectiveStatus = cardState.phase === "resolved" ? cardState.status : toolResult.status;
757
867
  const isPending = effectiveStatus === "pending_approval" && cardState.phase !== "submitting";
758
868
  const isSubmitting = cardState.phase === "submitting";
759
- const resolvedResult = cardState.phase === "resolved" ? cardState.result : toolResult.result;
869
+ const resolvedResult = cardState.phase === "resolved" ? cardState.result : toolResult.status === "approved" || toolResult.status === "executed" || toolResult.status === "auto_approved" ? toolResult.result : void 0;
760
870
  async function callAction(endpoint, body) {
761
871
  if (!actionAuth) {
762
872
  console.warn("ActionApprovalCard: No ActionAuthProvider found. API calls will be sent without authentication.");
@@ -782,8 +892,10 @@ function ActionApprovalCard({ part }) {
782
892
  } catch {
783
893
  throw new Error("Action was already resolved, but the response could not be read. Refresh the page.");
784
894
  }
785
- const status = typeof data2.status === "string" ? data2.status : "failed";
786
- const label = status.replace(/_/g, " ");
895
+ if (typeof data2.status !== "string" || !action.RESOLVED_STATUSES.has(data2.status)) {
896
+ throw new Error("Action was already resolved with an unrecognized status. Refresh the page.");
897
+ }
898
+ const label = data2.status.replace(/_/g, " ");
787
899
  throw new Error(`This action was already ${label} by another user or policy.`);
788
900
  }
789
901
  if (!res.ok) {
@@ -796,8 +908,8 @@ function ActionApprovalCard({ part }) {
796
908
  } catch {
797
909
  throw new Error("Action succeeded, but the response could not be read. Refresh the page.");
798
910
  }
799
- if (typeof data.status !== "string") {
800
- throw new Error("Action succeeded, but the server returned an invalid status. Refresh the page.");
911
+ if (typeof data.status !== "string" || !action.RESOLVED_STATUSES.has(data.status)) {
912
+ throw new Error("Action succeeded, but the server returned an unrecognized status. Refresh the page.");
801
913
  }
802
914
  setCardState({ phase: "resolved", status: data.status, result: data.result });
803
915
  }
@@ -840,11 +952,11 @@ function ActionApprovalCard({ part }) {
840
952
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Result: " }),
841
953
  typeof resolvedResult === "string" ? resolvedResult : safeStringify(resolvedResult)
842
954
  ] }),
843
- toolResult.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 rounded bg-red-50 p-2 text-xs text-red-700 dark:bg-red-900/20 dark:text-red-400", children: [
955
+ toolResult.status === "failed" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 rounded bg-red-50 p-2 text-xs text-red-700 dark:bg-red-900/20 dark:text-red-400", children: [
844
956
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Error: " }),
845
957
  toolResult.error
846
958
  ] }),
847
- toolResult.reason && action.RESOLVED_STATUSES.has(effectiveStatus) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 text-xs text-zinc-500 dark:text-zinc-400", children: [
959
+ toolResult.status === "denied" && action.RESOLVED_STATUSES.has(effectiveStatus) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 text-xs text-zinc-500 dark:text-zinc-400", children: [
848
960
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Reason: " }),
849
961
  toolResult.reason
850
962
  ] })
@@ -857,7 +969,7 @@ function ActionApprovalCard({ part }) {
857
969
  {
858
970
  onClick: handleApprove,
859
971
  disabled: isSubmitting,
860
- className: "inline-flex items-center gap-1.5 rounded bg-blue-600 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-blue-500 disabled:opacity-40",
972
+ className: "inline-flex items-center gap-1.5 rounded bg-blue-600 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-blue-500 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-blue-500/50 disabled:opacity-40",
861
973
  children: [
862
974
  isSubmitting && cardState.action === "approve" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block h-3 w-3 animate-spin rounded-full border-2 border-white/30 border-t-white" }),
863
975
  "Approve"
@@ -869,7 +981,7 @@ function ActionApprovalCard({ part }) {
869
981
  {
870
982
  onClick: () => setShowDenyInput(true),
871
983
  disabled: isSubmitting,
872
- className: "rounded border border-zinc-300 px-3 py-1.5 text-xs font-medium text-zinc-600 transition-colors hover:border-zinc-400 hover:text-zinc-800 disabled:opacity-40 dark:border-zinc-600 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
984
+ className: "rounded border border-zinc-300 px-3 py-1.5 text-xs font-medium text-zinc-600 transition-colors hover:border-zinc-400 hover:text-zinc-800 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-40 dark:border-zinc-600 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
873
985
  children: "Deny"
874
986
  }
875
987
  ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
@@ -879,7 +991,7 @@ function ActionApprovalCard({ part }) {
879
991
  value: denyReason,
880
992
  onChange: (e) => setDenyReason(e.target.value),
881
993
  placeholder: "Reason (optional)",
882
- className: "flex-1 rounded border border-zinc-200 bg-white px-2 py-1 text-xs text-zinc-900 placeholder-zinc-400 outline-none focus:border-red-400 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600",
994
+ className: "flex-1 rounded border border-zinc-200 bg-white px-2 py-1 text-xs text-zinc-900 placeholder-zinc-400 outline-none focus-visible:border-red-400 focus-visible:ring-[3px] focus-visible:ring-red-400/30 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-100 dark:placeholder-zinc-600",
883
995
  disabled: isSubmitting
884
996
  }
885
997
  ),
@@ -888,7 +1000,7 @@ function ActionApprovalCard({ part }) {
888
1000
  {
889
1001
  onClick: handleDeny,
890
1002
  disabled: isSubmitting,
891
- className: "inline-flex items-center gap-1.5 rounded bg-red-600 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-red-500 disabled:opacity-40",
1003
+ className: "inline-flex items-center gap-1.5 rounded bg-red-600 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-red-500 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-red-500/50 disabled:opacity-40",
892
1004
  children: [
893
1005
  isSubmitting && cardState.action === "deny" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block h-3 w-3 animate-spin rounded-full border-2 border-white/30 border-t-white" }),
894
1006
  "Confirm Deny"
@@ -903,7 +1015,7 @@ function ActionApprovalCard({ part }) {
903
1015
  setDenyReason("");
904
1016
  },
905
1017
  disabled: isSubmitting,
906
- className: "text-xs text-zinc-400 hover:text-zinc-600 disabled:opacity-40 dark:hover:text-zinc-300",
1018
+ className: "rounded text-xs text-zinc-400 hover:text-zinc-600 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-40 dark:hover:text-zinc-300",
907
1019
  children: "Cancel"
908
1020
  }
909
1021
  )
@@ -912,7 +1024,7 @@ function ActionApprovalCard({ part }) {
912
1024
  ] })
913
1025
  ] });
914
1026
  }
915
- var ResultChart2 = React.lazy(() => import('./result-chart-YLCKBNV4.cjs').then((m) => ({ default: m.ResultChart })));
1027
+ var ResultChart2 = React.lazy(() => import('./result-chart-ANZOT6FL.cjs').then((m) => ({ default: m.ResultChart })));
916
1028
  var ALLOWED_IMAGE_MIME = /* @__PURE__ */ new Set(["image/png", "image/jpeg"]);
917
1029
  var PythonErrorBoundary = class extends React.Component {
918
1030
  constructor(props) {
@@ -939,7 +1051,7 @@ function PythonResultCard({ part }) {
939
1051
  return /* @__PURE__ */ jsxRuntime.jsx(PythonErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(PythonResultCardInner, { part }) });
940
1052
  }
941
1053
  function PythonResultCardInner({ part }) {
942
- const dark = React.useContext(chunkUIRB6L36_cjs.DarkModeContext);
1054
+ const dark = React.useContext(chunkDZFSZSQB_cjs.DarkModeContext);
943
1055
  const args = getToolArgs(part);
944
1056
  const raw = getToolResult(part);
945
1057
  const done = isToolComplete(part);
@@ -1162,7 +1274,7 @@ var mdComponents = {
1162
1274
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children })
1163
1275
  };
1164
1276
  var Markdown = React.memo(function Markdown2({ content }) {
1165
- const dark = React.useContext(chunkUIRB6L36_cjs.DarkModeContext);
1277
+ const dark = React.useContext(chunkDZFSZSQB_cjs.DarkModeContext);
1166
1278
  return /* @__PURE__ */ jsxRuntime.jsx(
1167
1279
  ReactMarkdown__default.default,
1168
1280
  {
@@ -1196,64 +1308,13 @@ var STARTER_PROMPTS = [
1196
1308
  "What is the headcount breakdown by department?",
1197
1309
  "What is total MRR by plan type?"
1198
1310
  ];
1199
- function cn(...inputs) {
1200
- return tailwindMerge.twMerge(clsx.clsx(inputs));
1201
- }
1202
- var buttonVariants = classVarianceAuthority.cva(
1203
- "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1204
- {
1205
- variants: {
1206
- variant: {
1207
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1208
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1209
- outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1210
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1211
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1212
- link: "text-primary underline-offset-4 hover:underline"
1213
- },
1214
- size: {
1215
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1216
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
1217
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1218
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1219
- icon: "size-9",
1220
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
1221
- "icon-sm": "size-8",
1222
- "icon-lg": "size-10"
1223
- }
1224
- },
1225
- defaultVariants: {
1226
- variant: "default",
1227
- size: "default"
1228
- }
1229
- }
1230
- );
1231
- function Button({
1232
- className,
1233
- variant = "default",
1234
- size = "default",
1235
- asChild = false,
1236
- ...props
1237
- }) {
1238
- const Comp = asChild ? radixUi.Slot.Root : "button";
1239
- return /* @__PURE__ */ jsxRuntime.jsx(
1240
- Comp,
1241
- {
1242
- "data-slot": "button",
1243
- "data-variant": variant,
1244
- "data-size": size,
1245
- className: cn(buttonVariants({ variant, size, className })),
1246
- ...props
1247
- }
1248
- );
1249
- }
1250
1311
  function FollowUpChips({
1251
1312
  suggestions,
1252
1313
  onSelect
1253
1314
  }) {
1254
1315
  if (suggestions.length === 0) return null;
1255
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 pt-1", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
1256
- Button,
1316
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 pt-1", role: "group", "aria-label": "Suggested follow-up questions", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
1317
+ chunkIDXGFWFS_cjs.Button,
1257
1318
  {
1258
1319
  variant: "outline",
1259
1320
  size: "sm",
@@ -1305,7 +1366,7 @@ function ToggleGroup({
1305
1366
  "data-size": size,
1306
1367
  "data-spacing": spacing,
1307
1368
  style: { "--gap": spacing },
1308
- className: cn(
1369
+ className: chunkIDXGFWFS_cjs.cn(
1309
1370
  "group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-xs",
1310
1371
  className
1311
1372
  ),
@@ -1329,7 +1390,7 @@ function ToggleGroupItem({
1329
1390
  "data-variant": context.variant || variant,
1330
1391
  "data-size": context.size || size,
1331
1392
  "data-spacing": context.spacing,
1332
- className: cn(
1393
+ className: chunkIDXGFWFS_cjs.cn(
1333
1394
  toggleVariants({
1334
1395
  variant: context.variant || variant,
1335
1396
  size: context.size || size
@@ -1373,7 +1434,7 @@ function Badge({
1373
1434
  {
1374
1435
  "data-slot": "badge",
1375
1436
  "data-variant": variant,
1376
- className: cn(badgeVariants({ variant }), className),
1437
+ className: chunkIDXGFWFS_cjs.cn(badgeVariants({ variant }), className),
1377
1438
  ...props
1378
1439
  }
1379
1440
  );
@@ -1388,7 +1449,7 @@ function DeleteConfirmation({
1388
1449
  "button",
1389
1450
  {
1390
1451
  onClick: onCancel,
1391
- className: "rounded px-2 py-0.5 text-zinc-500 transition-colors hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200",
1452
+ className: "rounded px-2 py-0.5 text-zinc-500 transition-colors hover:text-zinc-800 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:text-zinc-400 dark:hover:text-zinc-200",
1392
1453
  children: "Cancel"
1393
1454
  }
1394
1455
  ),
@@ -1396,7 +1457,7 @@ function DeleteConfirmation({
1396
1457
  "button",
1397
1458
  {
1398
1459
  onClick: onConfirm,
1399
- className: "rounded bg-red-600 px-2 py-0.5 text-white transition-colors hover:bg-red-500",
1460
+ className: "rounded bg-red-600 px-2 py-0.5 text-white transition-colors hover:bg-red-500 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-red-500/50 ",
1400
1461
  children: "Delete"
1401
1462
  }
1402
1463
  )
@@ -1426,6 +1487,7 @@ function ConversationItem({
1426
1487
  const [confirmDelete, setConfirmDelete] = React.useState(false);
1427
1488
  const [deleting, setDeleting] = React.useState(false);
1428
1489
  const [starPending, setStarPending] = React.useState(false);
1490
+ const [error, setError] = React.useState(null);
1429
1491
  if (confirmDelete) {
1430
1492
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-red-200 bg-red-50/50 dark:border-red-900/30 dark:bg-red-950/10", children: /* @__PURE__ */ jsxRuntime.jsx(
1431
1493
  DeleteConfirmation,
@@ -1434,12 +1496,11 @@ function ConversationItem({
1434
1496
  onConfirm: async () => {
1435
1497
  setDeleting(true);
1436
1498
  try {
1437
- const success = await onDelete();
1438
- if (success) {
1439
- setConfirmDelete(false);
1440
- }
1441
- } catch (err) {
1442
- console.warn("Failed to delete conversation:", err);
1499
+ await onDelete();
1500
+ setConfirmDelete(false);
1501
+ } catch {
1502
+ setError("Failed to delete");
1503
+ setTimeout(() => setError(null), 3e3);
1443
1504
  } finally {
1444
1505
  setDeleting(false);
1445
1506
  }
@@ -1459,15 +1520,15 @@ function ConversationItem({
1459
1520
  onSelect();
1460
1521
  }
1461
1522
  },
1462
- className: `group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm transition-colors ${isActive ? "bg-blue-50 text-blue-700 dark:bg-blue-600/10 dark:text-blue-400" : "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"}`,
1523
+ className: `group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 ${isActive ? "bg-blue-50 text-blue-700 dark:bg-blue-600/10 dark:text-blue-400" : "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"}`,
1463
1524
  children: [
1464
1525
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
1465
1526
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-medium", children: conversation.title || "New conversation" }),
1466
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: relativeTime(conversation.updatedAt) })
1527
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500 dark:text-red-400", children: error }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: relativeTime(conversation.updatedAt) })
1467
1528
  ] }),
1468
1529
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [
1469
1530
  /* @__PURE__ */ jsxRuntime.jsx(
1470
- Button,
1531
+ chunkIDXGFWFS_cjs.Button,
1471
1532
  {
1472
1533
  variant: "ghost",
1473
1534
  size: "icon",
@@ -1477,8 +1538,9 @@ function ConversationItem({
1477
1538
  setStarPending(true);
1478
1539
  try {
1479
1540
  await onStar(!conversation.starred);
1480
- } catch (err) {
1481
- console.warn("Failed to update star:", err);
1541
+ } catch {
1542
+ setError("Failed to update");
1543
+ setTimeout(() => setError(null), 3e3);
1482
1544
  } finally {
1483
1545
  setStarPending(false);
1484
1546
  }
@@ -1490,7 +1552,7 @@ function ConversationItem({
1490
1552
  }
1491
1553
  ),
1492
1554
  /* @__PURE__ */ jsxRuntime.jsx(
1493
- Button,
1555
+ chunkIDXGFWFS_cjs.Button,
1494
1556
  {
1495
1557
  variant: "ghost",
1496
1558
  size: "icon",
@@ -1519,7 +1581,7 @@ function ConversationList({
1519
1581
  emptyMessage
1520
1582
  }) {
1521
1583
  if (conversations.length === 0) {
1522
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-6 text-center text-xs text-zinc-400 dark:text-zinc-500", children: emptyMessage ?? "No conversations yet" });
1584
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-6 text-center text-xs text-zinc-500 dark:text-zinc-400", children: emptyMessage ?? "No conversations yet" });
1523
1585
  }
1524
1586
  function renderItems(items) {
1525
1587
  return items.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -1560,6 +1622,14 @@ function ConversationSidebar({
1560
1622
  onMobileClose
1561
1623
  }) {
1562
1624
  const [filter, setFilter] = React.useState("all");
1625
+ React.useEffect(() => {
1626
+ if (!mobileOpen) return;
1627
+ function handleKeyDown(e) {
1628
+ if (e.key === "Escape") onMobileClose();
1629
+ }
1630
+ document.addEventListener("keydown", handleKeyDown);
1631
+ return () => document.removeEventListener("keydown", handleKeyDown);
1632
+ }, [mobileOpen, onMobileClose]);
1563
1633
  const starredConversations = conversations.filter((c) => c.starred);
1564
1634
  const filteredConversations = filter === "saved" ? starredConversations : conversations;
1565
1635
  const sidebar = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col border-r border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950", children: [
@@ -1569,7 +1639,7 @@ function ConversationSidebar({
1569
1639
  "button",
1570
1640
  {
1571
1641
  onClick: onNewChat,
1572
- className: "rounded-lg border border-zinc-200 px-3 py-1.5 text-xs font-medium text-zinc-600 transition-colors hover:border-zinc-400 hover:text-zinc-900 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
1642
+ className: "rounded-lg border border-zinc-200 px-3 py-1.5 text-xs font-medium text-zinc-600 transition-colors hover:border-zinc-400 hover:text-zinc-900 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
1573
1643
  children: "+ New"
1574
1644
  }
1575
1645
  )
@@ -1639,7 +1709,7 @@ function AlertDialogOverlay({
1639
1709
  radixUi.AlertDialog.Overlay,
1640
1710
  {
1641
1711
  "data-slot": "alert-dialog-overlay",
1642
- className: cn(
1712
+ className: chunkIDXGFWFS_cjs.cn(
1643
1713
  "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
1644
1714
  className
1645
1715
  ),
@@ -1659,7 +1729,7 @@ function AlertDialogContent({
1659
1729
  {
1660
1730
  "data-slot": "alert-dialog-content",
1661
1731
  "data-size": size,
1662
- className: cn(
1732
+ className: chunkIDXGFWFS_cjs.cn(
1663
1733
  "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=default]:sm:max-w-lg",
1664
1734
  className
1665
1735
  ),
@@ -1676,7 +1746,7 @@ function AlertDialogHeader({
1676
1746
  "div",
1677
1747
  {
1678
1748
  "data-slot": "alert-dialog-header",
1679
- className: cn(
1749
+ className: chunkIDXGFWFS_cjs.cn(
1680
1750
  "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
1681
1751
  className
1682
1752
  ),
@@ -1692,7 +1762,7 @@ function AlertDialogFooter({
1692
1762
  "div",
1693
1763
  {
1694
1764
  "data-slot": "alert-dialog-footer",
1695
- className: cn(
1765
+ className: chunkIDXGFWFS_cjs.cn(
1696
1766
  "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
1697
1767
  className
1698
1768
  ),
@@ -1708,7 +1778,7 @@ function AlertDialogTitle({
1708
1778
  radixUi.AlertDialog.Title,
1709
1779
  {
1710
1780
  "data-slot": "alert-dialog-title",
1711
- className: cn(
1781
+ className: chunkIDXGFWFS_cjs.cn(
1712
1782
  "text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
1713
1783
  className
1714
1784
  ),
@@ -1724,7 +1794,7 @@ function AlertDialogDescription({
1724
1794
  radixUi.AlertDialog.Description,
1725
1795
  {
1726
1796
  "data-slot": "alert-dialog-description",
1727
- className: cn("text-sm text-muted-foreground", className),
1797
+ className: chunkIDXGFWFS_cjs.cn("text-sm text-muted-foreground", className),
1728
1798
  ...props
1729
1799
  }
1730
1800
  );
@@ -1735,7 +1805,7 @@ function Input({ className, type, ...props }) {
1735
1805
  {
1736
1806
  type,
1737
1807
  "data-slot": "input",
1738
- className: cn(
1808
+ className: chunkIDXGFWFS_cjs.cn(
1739
1809
  "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
1740
1810
  "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
1741
1811
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
@@ -1838,7 +1908,7 @@ function ChangePasswordDialog({
1838
1908
  ] }),
1839
1909
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-600 dark:text-red-400", children: error })
1840
1910
  ] }),
1841
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", disabled: loading, children: loading ? "Changing..." : "Change password" }) })
1911
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(chunkIDXGFWFS_cjs.Button, { type: "submit", disabled: loading, children: loading ? "Changing..." : "Change password" }) })
1842
1912
  ] }) }) });
1843
1913
  }
1844
1914
  function Sheet({ ...props }) {
@@ -1857,7 +1927,7 @@ function SheetOverlay({
1857
1927
  radixUi.Dialog.Overlay,
1858
1928
  {
1859
1929
  "data-slot": "sheet-overlay",
1860
- className: cn(
1930
+ className: chunkIDXGFWFS_cjs.cn(
1861
1931
  "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
1862
1932
  className
1863
1933
  ),
@@ -1878,7 +1948,7 @@ function SheetContent({
1878
1948
  radixUi.Dialog.Content,
1879
1949
  {
1880
1950
  "data-slot": "sheet-content",
1881
- className: cn(
1951
+ className: chunkIDXGFWFS_cjs.cn(
1882
1952
  "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
1883
1953
  side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
1884
1954
  side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
@@ -1903,7 +1973,7 @@ function SheetHeader({ className, ...props }) {
1903
1973
  "div",
1904
1974
  {
1905
1975
  "data-slot": "sheet-header",
1906
- className: cn("flex flex-col gap-1.5 p-4", className),
1976
+ className: chunkIDXGFWFS_cjs.cn("flex flex-col gap-1.5 p-4", className),
1907
1977
  ...props
1908
1978
  }
1909
1979
  );
@@ -1916,7 +1986,20 @@ function SheetTitle({
1916
1986
  radixUi.Dialog.Title,
1917
1987
  {
1918
1988
  "data-slot": "sheet-title",
1919
- className: cn("font-semibold text-foreground", className),
1989
+ className: chunkIDXGFWFS_cjs.cn("font-semibold text-foreground", className),
1990
+ ...props
1991
+ }
1992
+ );
1993
+ }
1994
+ function SheetDescription({
1995
+ className,
1996
+ ...props
1997
+ }) {
1998
+ return /* @__PURE__ */ jsxRuntime.jsx(
1999
+ radixUi.Dialog.Description,
2000
+ {
2001
+ "data-slot": "sheet-description",
2002
+ className: chunkIDXGFWFS_cjs.cn("text-sm text-muted-foreground", className),
1920
2003
  ...props
1921
2004
  }
1922
2005
  );
@@ -1931,7 +2014,7 @@ function ScrollArea({
1931
2014
  radixUi.ScrollArea.Root,
1932
2015
  {
1933
2016
  "data-slot": "scroll-area",
1934
- className: cn("relative overflow-hidden", className),
2017
+ className: chunkIDXGFWFS_cjs.cn("relative overflow-hidden", className),
1935
2018
  ...props,
1936
2019
  children: [
1937
2020
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1959,7 +2042,7 @@ function ScrollBar({
1959
2042
  {
1960
2043
  "data-slot": "scroll-area-scrollbar",
1961
2044
  orientation,
1962
- className: cn(
2045
+ className: chunkIDXGFWFS_cjs.cn(
1963
2046
  "flex touch-none p-px transition-colors select-none",
1964
2047
  orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
1965
2048
  orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
@@ -1988,7 +2071,7 @@ function Separator({
1988
2071
  "data-slot": "separator",
1989
2072
  decorative,
1990
2073
  orientation,
1991
- className: cn(
2074
+ className: chunkIDXGFWFS_cjs.cn(
1992
2075
  "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
1993
2076
  className
1994
2077
  ),
@@ -2006,7 +2089,7 @@ function Table({ className, ...props }) {
2006
2089
  "table",
2007
2090
  {
2008
2091
  "data-slot": "table",
2009
- className: cn("w-full caption-bottom text-sm", className),
2092
+ className: chunkIDXGFWFS_cjs.cn("w-full caption-bottom text-sm", className),
2010
2093
  ...props
2011
2094
  }
2012
2095
  )
@@ -2018,7 +2101,7 @@ function TableHeader({ className, ...props }) {
2018
2101
  "thead",
2019
2102
  {
2020
2103
  "data-slot": "table-header",
2021
- className: cn("[&_tr]:border-b", className),
2104
+ className: chunkIDXGFWFS_cjs.cn("[&_tr]:border-b", className),
2022
2105
  ...props
2023
2106
  }
2024
2107
  );
@@ -2028,7 +2111,7 @@ function TableBody({ className, ...props }) {
2028
2111
  "tbody",
2029
2112
  {
2030
2113
  "data-slot": "table-body",
2031
- className: cn("[&_tr:last-child]:border-0", className),
2114
+ className: chunkIDXGFWFS_cjs.cn("[&_tr:last-child]:border-0", className),
2032
2115
  ...props
2033
2116
  }
2034
2117
  );
@@ -2038,7 +2121,7 @@ function TableRow({ className, ...props }) {
2038
2121
  "tr",
2039
2122
  {
2040
2123
  "data-slot": "table-row",
2041
- className: cn(
2124
+ className: chunkIDXGFWFS_cjs.cn(
2042
2125
  "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
2043
2126
  className
2044
2127
  ),
@@ -2051,7 +2134,7 @@ function TableHead({ className, ...props }) {
2051
2134
  "th",
2052
2135
  {
2053
2136
  "data-slot": "table-head",
2054
- className: cn(
2137
+ className: chunkIDXGFWFS_cjs.cn(
2055
2138
  "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
2056
2139
  className
2057
2140
  ),
@@ -2064,7 +2147,7 @@ function TableCell({ className, ...props }) {
2064
2147
  "td",
2065
2148
  {
2066
2149
  "data-slot": "table-cell",
2067
- className: cn(
2150
+ className: chunkIDXGFWFS_cjs.cn(
2068
2151
  "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
2069
2152
  className
2070
2153
  ),
@@ -2077,7 +2160,7 @@ function Card({ className, ...props }) {
2077
2160
  "div",
2078
2161
  {
2079
2162
  "data-slot": "card",
2080
- className: cn(
2163
+ className: chunkIDXGFWFS_cjs.cn(
2081
2164
  "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
2082
2165
  className
2083
2166
  ),
@@ -2090,7 +2173,7 @@ function CardContent({ className, ...props }) {
2090
2173
  "div",
2091
2174
  {
2092
2175
  "data-slot": "card-content",
2093
- className: cn("px-6", className),
2176
+ className: chunkIDXGFWFS_cjs.cn("px-6", className),
2094
2177
  ...props
2095
2178
  }
2096
2179
  );
@@ -2207,7 +2290,7 @@ function EntityDetailView({
2207
2290
  const patterns = normalizeList(entity.query_patterns, "name");
2208
2291
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col", children: [
2209
2292
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-4 pb-3", children: [
2210
- /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", className: "size-7", onClick: onBack, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "size-3.5" }) }),
2293
+ /* @__PURE__ */ jsxRuntime.jsx(chunkIDXGFWFS_cjs.Button, { variant: "ghost", size: "icon", className: "size-7", onClick: onBack, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "size-3.5" }) }),
2211
2294
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2212
2295
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate text-sm font-semibold", children: entity.table }),
2213
2296
  entity.type === "view" && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "outline", className: "text-[10px]", children: "view" })
@@ -2372,14 +2455,17 @@ function SchemaExplorer({
2372
2455
  onOpenChange(false);
2373
2456
  }
2374
2457
  return /* @__PURE__ */ jsxRuntime.jsx(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "right", className: "flex w-full flex-col p-0 sm:max-w-xl", children: [
2375
- /* @__PURE__ */ jsxRuntime.jsx(SheetHeader, { className: "px-4 pt-4", children: /* @__PURE__ */ jsxRuntime.jsxs(SheetTitle, { className: "flex items-center gap-2 text-base", children: [
2376
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TableProperties, { className: "size-4" }),
2377
- "Schema Explorer"
2378
- ] }) }),
2458
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetHeader, { className: "px-4 pt-4", children: [
2459
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetTitle, { className: "flex items-center gap-2 text-base", children: [
2460
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TableProperties, { className: "size-4" }),
2461
+ "Schema Explorer"
2462
+ ] }),
2463
+ /* @__PURE__ */ jsxRuntime.jsx(SheetDescription, { className: "sr-only", children: "Browse tables, columns, joins, and query patterns from the semantic layer" })
2464
+ ] }),
2379
2465
  /* @__PURE__ */ jsxRuntime.jsx(Separator, { className: "mt-3" }),
2380
2466
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden pt-3", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-400", children: "Loading schema..." }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-4", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs text-red-500", children: error }) }) : selectedName ? detailError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-2 px-4", children: [
2381
2467
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs text-red-500", children: detailError }),
2382
- /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: handleBack, className: "text-xs", children: [
2468
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkIDXGFWFS_cjs.Button, { variant: "ghost", size: "sm", onClick: handleBack, className: "text-xs", children: [
2383
2469
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "mr-1 size-3" }),
2384
2470
  " Back to list"
2385
2471
  ] })
@@ -2434,7 +2520,7 @@ function DropdownMenuContent({
2434
2520
  {
2435
2521
  "data-slot": "dropdown-menu-content",
2436
2522
  sideOffset,
2437
- className: cn(
2523
+ className: chunkIDXGFWFS_cjs.cn(
2438
2524
  "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
2439
2525
  className
2440
2526
  ),
@@ -2454,7 +2540,7 @@ function DropdownMenuItem({
2454
2540
  "data-slot": "dropdown-menu-item",
2455
2541
  "data-inset": inset,
2456
2542
  "data-variant": variant,
2457
- className: cn(
2543
+ className: chunkIDXGFWFS_cjs.cn(
2458
2544
  "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
2459
2545
  className
2460
2546
  ),
@@ -2481,17 +2567,17 @@ var THEME_OPTIONS = [
2481
2567
  { value: "system", label: "System", icon: lucideReact.Monitor }
2482
2568
  ];
2483
2569
  function ThemeToggle() {
2484
- const mode = chunkUIRB6L36_cjs.useThemeMode();
2570
+ const mode = chunkDZFSZSQB_cjs.useThemeMode();
2485
2571
  const CurrentIcon = mode === "dark" ? lucideReact.Moon : mode === "light" ? lucideReact.Sun : lucideReact.Monitor;
2486
2572
  return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
2487
- /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "icon", className: "size-11 sm:size-8 text-zinc-500 dark:text-zinc-400", children: [
2573
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(chunkIDXGFWFS_cjs.Button, { variant: "ghost", size: "icon", className: "size-11 sm:size-8 text-zinc-500 dark:text-zinc-400", children: [
2488
2574
  /* @__PURE__ */ jsxRuntime.jsx(CurrentIcon, { className: "size-4" }),
2489
2575
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Toggle theme" })
2490
2576
  ] }) }),
2491
2577
  /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "end", children: THEME_OPTIONS.map(({ value, label, icon: Icon }) => /* @__PURE__ */ jsxRuntime.jsxs(
2492
2578
  DropdownMenuItem,
2493
2579
  {
2494
- onClick: () => chunkUIRB6L36_cjs.setTheme(value),
2580
+ onClick: () => chunkDZFSZSQB_cjs.setTheme(value),
2495
2581
  className: mode === value ? "bg-accent" : "",
2496
2582
  children: [
2497
2583
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "mr-2 size-4" }),
@@ -2520,7 +2606,7 @@ function SaveButton({
2520
2606
  }
2521
2607
  }
2522
2608
  return /* @__PURE__ */ jsxRuntime.jsxs(
2523
- Button,
2609
+ chunkIDXGFWFS_cjs.Button,
2524
2610
  {
2525
2611
  variant: "ghost",
2526
2612
  size: "xs",
@@ -2543,10 +2629,12 @@ function AtlasChat(props) {
2543
2629
  sidebar = false,
2544
2630
  schemaExplorer: schemaExplorerEnabled = false,
2545
2631
  authClient = noopAuthClient,
2546
- toolRenderers
2632
+ toolRenderers,
2633
+ chatEndpoint = "/api/v1/chat",
2634
+ conversationsEndpoint = "/api/v1/conversations"
2547
2635
  } = props;
2548
2636
  React.useEffect(() => {
2549
- chunkUIRB6L36_cjs.setTheme(propTheme);
2637
+ chunkDZFSZSQB_cjs.setTheme(propTheme);
2550
2638
  }, [propTheme]);
2551
2639
  return /* @__PURE__ */ jsxRuntime.jsx(AtlasUIProvider, { config: { apiUrl, authClient }, children: /* @__PURE__ */ jsxRuntime.jsx(
2552
2640
  AtlasChatInner,
@@ -2554,7 +2642,9 @@ function AtlasChat(props) {
2554
2642
  propApiKey,
2555
2643
  sidebar,
2556
2644
  schemaExplorerEnabled,
2557
- toolRenderers
2645
+ toolRenderers,
2646
+ chatEndpoint,
2647
+ conversationsEndpoint
2558
2648
  }
2559
2649
  ) });
2560
2650
  }
@@ -2562,10 +2652,12 @@ function AtlasChatInner({
2562
2652
  propApiKey,
2563
2653
  sidebar,
2564
2654
  schemaExplorerEnabled,
2565
- toolRenderers
2655
+ toolRenderers,
2656
+ chatEndpoint,
2657
+ conversationsEndpoint
2566
2658
  }) {
2567
2659
  const { apiUrl, isCrossOrigin, authClient } = useAtlasConfig();
2568
- const dark = chunkUIRB6L36_cjs.useDarkMode();
2660
+ const dark = chunkDZFSZSQB_cjs.useDarkMode();
2569
2661
  const [input, setInput] = React.useState("");
2570
2662
  const [authMode, setAuthMode] = React.useState(null);
2571
2663
  const [healthWarning, setHealthWarning] = React.useState("");
@@ -2592,11 +2684,12 @@ function AtlasChatInner({
2592
2684
  const getCredentials = React.useCallback(() => {
2593
2685
  return isCrossOrigin ? "include" : "same-origin";
2594
2686
  }, [isCrossOrigin]);
2595
- const convos = chunkUIRB6L36_cjs.useConversations({
2687
+ const convos = chunkDZFSZSQB_cjs.useConversations({
2596
2688
  apiUrl,
2597
2689
  enabled: sidebar,
2598
2690
  getHeaders,
2599
- getCredentials
2691
+ getCredentials,
2692
+ conversationsEndpoint
2600
2693
  });
2601
2694
  const refreshConvosRef = React.useRef(convos.refresh);
2602
2695
  refreshConvosRef.current = convos.refresh;
@@ -2629,15 +2722,15 @@ function AtlasChatInner({
2629
2722
  }
2630
2723
  const data = await res.json();
2631
2724
  const mode = data?.checks?.auth?.mode;
2632
- if (typeof mode === "string" && chunkUIRB6L36_cjs.AUTH_MODES.includes(mode)) {
2725
+ if (typeof mode === "string" && chunkDZFSZSQB_cjs.AUTH_MODES.includes(mode)) {
2633
2726
  setAuthMode(mode);
2634
2727
  } else {
2635
2728
  console.warn("Health check succeeded but returned no valid auth mode:", data);
2636
2729
  setHealthWarning("Could not determine authentication mode from the server.");
2637
2730
  setAuthMode("none");
2638
2731
  }
2639
- if (typeof data?.brandColor === "string" && chunkUIRB6L36_cjs.OKLCH_RE.test(data.brandColor)) {
2640
- chunkUIRB6L36_cjs.applyBrandColor(data.brandColor);
2732
+ if (typeof data?.brandColor === "string" && chunkDZFSZSQB_cjs.OKLCH_RE.test(data.brandColor)) {
2733
+ chunkDZFSZSQB_cjs.applyBrandColor(data.brandColor);
2641
2734
  }
2642
2735
  } catch (err) {
2643
2736
  console.warn("Health endpoint unavailable:", err);
@@ -2688,7 +2781,7 @@ function AtlasChatInner({
2688
2781
  headers["Authorization"] = `Bearer ${apiKey}`;
2689
2782
  }
2690
2783
  return new ai.DefaultChatTransport({
2691
- api: `${apiUrl}/api/chat`,
2784
+ api: `${apiUrl}${chatEndpoint}`,
2692
2785
  headers,
2693
2786
  credentials: isCrossOrigin ? "include" : void 0,
2694
2787
  body: () => conversationIdRef.current ? { conversationId: conversationIdRef.current } : {},
@@ -2706,7 +2799,7 @@ function AtlasChatInner({
2706
2799
  return response;
2707
2800
  })
2708
2801
  });
2709
- }, [apiKey, apiUrl, isCrossOrigin]);
2802
+ }, [apiKey, apiUrl, isCrossOrigin, chatEndpoint]);
2710
2803
  const { messages, setMessages, sendMessage, status, error } = react.useChat({ transport });
2711
2804
  const isLoading = status === "streaming" || status === "submitted";
2712
2805
  React.useEffect(() => {
@@ -2731,17 +2824,12 @@ function AtlasChatInner({
2731
2824
  setLoadingConversation(true);
2732
2825
  try {
2733
2826
  const uiMessages = await convos.loadConversation(id);
2734
- if (uiMessages) {
2735
- setMessages(uiMessages);
2736
- setConversationId(id);
2737
- convos.setSelectedId(id);
2738
- setMobileMenuOpen(false);
2739
- } else {
2740
- setHealthWarning("Could not load conversation. It may have been deleted.");
2741
- setTimeout(() => setHealthWarning(""), 5e3);
2742
- }
2827
+ setMessages(uiMessages);
2828
+ setConversationId(id);
2829
+ convos.setSelectedId(id);
2830
+ setMobileMenuOpen(false);
2743
2831
  } catch (err) {
2744
- console.warn("Failed to load conversation:", err);
2832
+ console.warn("Failed to load conversation:", err instanceof Error ? err.message : String(err));
2745
2833
  setHealthWarning("Failed to load conversation. Please try again.");
2746
2834
  setTimeout(() => setHealthWarning(""), 5e3);
2747
2835
  } finally {
@@ -2756,10 +2844,10 @@ function AtlasChatInner({
2756
2844
  setMobileMenuOpen(false);
2757
2845
  }
2758
2846
  if (!authResolved || isManaged && managedSession.isPending) {
2759
- return /* @__PURE__ */ jsxRuntime.jsx(chunkUIRB6L36_cjs.DarkModeContext.Provider, { value: dark, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "atlas-root flex h-dvh items-center justify-center bg-white dark:bg-zinc-950" }) });
2847
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkDZFSZSQB_cjs.DarkModeContext.Provider, { value: dark, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "atlas-root flex h-dvh items-center justify-center bg-white dark:bg-zinc-950" }) });
2760
2848
  }
2761
2849
  const showSidebar = sidebar && convos.available;
2762
- return /* @__PURE__ */ jsxRuntime.jsxs(chunkUIRB6L36_cjs.DarkModeContext.Provider, { value: dark, children: [
2850
+ return /* @__PURE__ */ jsxRuntime.jsxs(chunkDZFSZSQB_cjs.DarkModeContext.Provider, { value: dark, children: [
2763
2851
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "atlas-root flex h-dvh", children: [
2764
2852
  showSidebar && /* @__PURE__ */ jsxRuntime.jsx(
2765
2853
  ConversationSidebar,
@@ -2775,14 +2863,16 @@ function AtlasChatInner({
2775
2863
  onMobileClose: () => setMobileMenuOpen(false)
2776
2864
  }
2777
2865
  ),
2778
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-4xl flex-1 flex-col overflow-hidden p-4", children: [
2866
+ /* @__PURE__ */ jsxRuntime.jsx("main", { id: "main", tabIndex: -1, className: "flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-4xl flex-1 flex-col overflow-hidden p-4", children: [
2779
2867
  /* @__PURE__ */ jsxRuntime.jsx("header", { className: "mb-4 flex-none border-b border-zinc-100 pb-3 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2780
2868
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2781
2869
  showSidebar && /* @__PURE__ */ jsxRuntime.jsx(
2782
- "button",
2870
+ chunkIDXGFWFS_cjs.Button,
2783
2871
  {
2872
+ variant: "ghost",
2873
+ size: "icon",
2784
2874
  onClick: () => setMobileMenuOpen(true),
2785
- className: "flex size-11 items-center justify-center rounded text-zinc-400 hover:text-zinc-700 md:hidden dark:hover:text-zinc-200",
2875
+ className: "size-11 text-zinc-400 hover:text-zinc-700 md:hidden dark:hover:text-zinc-200",
2786
2876
  "aria-label": "Open conversation history",
2787
2877
  children: MenuIcon
2788
2878
  }
@@ -2797,7 +2887,7 @@ function AtlasChatInner({
2797
2887
  ] }),
2798
2888
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2799
2889
  schemaExplorerEnabled && /* @__PURE__ */ jsxRuntime.jsx(
2800
- Button,
2890
+ chunkIDXGFWFS_cjs.Button,
2801
2891
  {
2802
2892
  variant: "ghost",
2803
2893
  size: "icon",
@@ -2811,8 +2901,10 @@ function AtlasChatInner({
2811
2901
  isSignedIn && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2812
2902
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden text-xs text-zinc-500 sm:inline dark:text-zinc-400", children: managedSession.data?.user?.email }),
2813
2903
  /* @__PURE__ */ jsxRuntime.jsx(
2814
- "button",
2904
+ chunkIDXGFWFS_cjs.Button,
2815
2905
  {
2906
+ variant: "outline",
2907
+ size: "sm",
2816
2908
  onClick: () => {
2817
2909
  authClient.signOut().catch((err) => {
2818
2910
  console.error("Sign out failed:", err);
@@ -2820,75 +2912,101 @@ function AtlasChatInner({
2820
2912
  setTimeout(() => setHealthWarning(""), 5e3);
2821
2913
  });
2822
2914
  },
2823
- className: "rounded border border-zinc-200 px-3 py-2 text-xs text-zinc-500 transition-colors hover:border-zinc-400 hover:text-zinc-800 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200",
2915
+ className: "text-xs text-zinc-500 dark:text-zinc-400",
2824
2916
  children: "Sign out"
2825
2917
  }
2826
2918
  )
2827
2919
  ] })
2828
2920
  ] })
2829
2921
  ] }) }),
2830
- healthWarning && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs text-zinc-400 dark:text-zinc-500", children: healthWarning }),
2922
+ (healthWarning || convos.fetchError) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs text-zinc-400 dark:text-zinc-500", children: healthWarning || convos.fetchError }),
2831
2923
  isManaged && !isSignedIn ? /* @__PURE__ */ jsxRuntime.jsx(ManagedAuthCard, {}) : /* @__PURE__ */ jsxRuntime.jsxs(ActionAuthProvider, { getHeaders, getCredentials, children: [
2832
2924
  authMode === "simple-key" && !propApiKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(ApiKeyBar, { apiKey, onSave: handleSaveApiKey }) }),
2833
- /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { viewportRef: scrollRef, className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-atlas-messages": true, className: "space-y-4 pb-4 pr-3", children: [
2834
- messages.length === 0 && !error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-6", children: [
2835
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2836
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-medium text-zinc-500 dark:text-zinc-400", children: "What would you like to know?" }),
2837
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-zinc-400 dark:text-zinc-600", children: "Ask a question about your data to get started" })
2925
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { viewportRef: scrollRef, className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
2926
+ chunkIDXGFWFS_cjs.ErrorBoundary,
2927
+ {
2928
+ fallbackRender: (_error, reset) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center gap-2 p-6 text-sm text-red-600 dark:text-red-400", children: [
2929
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Failed to render messages." }),
2930
+ /* @__PURE__ */ jsxRuntime.jsx(chunkIDXGFWFS_cjs.Button, { variant: "link", size: "sm", onClick: reset, className: "text-xs", children: "Try again" })
2838
2931
  ] }),
2839
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid w-full max-w-lg grid-cols-1 gap-2 sm:grid-cols-2", children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
2840
- "button",
2841
- {
2842
- onClick: () => handleSend(prompt),
2843
- className: "rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2.5 text-left text-sm text-zinc-500 transition-colors hover:border-zinc-400 hover:bg-zinc-100 hover:text-zinc-800 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:bg-zinc-800 dark:hover:text-zinc-200",
2844
- children: prompt
2845
- },
2846
- prompt
2847
- )) })
2848
- ] }),
2849
- messages.map((m, msgIndex) => {
2850
- if (m.role === "user") {
2851
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[85%] rounded-xl bg-blue-600 px-4 py-3 text-sm text-white", children: m.parts?.map(
2852
- (part, i) => part.type === "text" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: part.text }, i) : null
2853
- ) }) }, m.id);
2854
- }
2855
- const isLastAssistant = m.role === "assistant" && msgIndex === messages.length - 1;
2856
- const lastTextWithSuggestions = m.parts?.filter((p) => p.type === "text" && !!p.text.trim()).findLast((p) => parseSuggestions(p.text).suggestions.length > 0);
2857
- const suggestions = lastTextWithSuggestions ? parseSuggestions(lastTextWithSuggestions.text).suggestions : [];
2858
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
2859
- m.parts?.map((part, i) => {
2860
- if (part.type === "text" && part.text.trim()) {
2861
- const displayText = parseSuggestions(part.text).text;
2862
- if (!displayText.trim()) return null;
2863
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[90%]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl bg-zinc-100 px-4 py-3 text-sm text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { content: displayText }) }) }, i);
2864
- }
2865
- if (ai.isToolUIPart(part)) {
2866
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[95%]", children: /* @__PURE__ */ jsxRuntime.jsx(ToolPart, { part, toolRenderers }) }, i);
2932
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-atlas-messages": true, className: "space-y-4 pb-4 pr-3", children: [
2933
+ messages.length === 0 && !error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-6", children: [
2934
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2935
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-medium text-zinc-500 dark:text-zinc-400", children: "What would you like to know?" }),
2936
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-500", children: "Ask a question about your data to get started" })
2937
+ ] }),
2938
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid w-full max-w-lg grid-cols-1 gap-2 sm:grid-cols-2", children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
2939
+ chunkIDXGFWFS_cjs.Button,
2940
+ {
2941
+ variant: "outline",
2942
+ onClick: () => handleSend(prompt),
2943
+ className: "h-auto whitespace-normal justify-start rounded-lg bg-zinc-50 px-3 py-2.5 text-left text-sm text-zinc-500 hover:text-zinc-800 dark:bg-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200",
2944
+ children: prompt
2945
+ },
2946
+ prompt
2947
+ )) })
2948
+ ] }),
2949
+ messages.map((m, msgIndex) => {
2950
+ if (m.role === "user") {
2951
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", role: "article", "aria-label": "Message from you", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[85%] rounded-xl bg-blue-600 px-4 py-3 text-sm text-white", children: m.parts?.map(
2952
+ (part, i) => part.type === "text" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: part.text }, i) : null
2953
+ ) }) }, m.id);
2867
2954
  }
2868
- return null;
2955
+ const isLastAssistant = m.role === "assistant" && msgIndex === messages.length - 1;
2956
+ const hasVisibleParts = m.parts?.some(
2957
+ (p) => p.type === "text" && p.text.trim() || ai.isToolUIPart(p)
2958
+ );
2959
+ if (!hasVisibleParts && !isLastAssistant) return null;
2960
+ const lastTextWithSuggestions = m.parts?.filter((p) => p.type === "text" && !!p.text.trim()).findLast((p) => parseSuggestions(p.text).suggestions.length > 0);
2961
+ const suggestions = lastTextWithSuggestions ? parseSuggestions(lastTextWithSuggestions.text).suggestions : [];
2962
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", role: "article", "aria-label": "Message from Atlas", children: [
2963
+ m.parts?.map((part, i) => {
2964
+ if (part.type === "text" && part.text.trim()) {
2965
+ const displayText = parseSuggestions(part.text).text;
2966
+ if (!displayText.trim()) return null;
2967
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[90%]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl bg-zinc-100 px-4 py-3 text-sm text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown, { content: displayText }) }) }, i);
2968
+ }
2969
+ if (ai.isToolUIPart(part)) {
2970
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[95%]", children: /* @__PURE__ */ jsxRuntime.jsx(ToolPart, { part, toolRenderers }) }, i);
2971
+ }
2972
+ return null;
2973
+ }),
2974
+ isLastAssistant && !hasVisibleParts && !isLoading && error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[90%]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/30 dark:text-red-400", children: error.message ? `Something went wrong generating a response: ${error.message}. Try sending your message again.` : "Something went wrong generating a response. Try sending your message again." }) }),
2975
+ isLastAssistant && !isLoading && hasVisibleParts && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2976
+ /* @__PURE__ */ jsxRuntime.jsx(
2977
+ FollowUpChips,
2978
+ {
2979
+ suggestions,
2980
+ onSelect: handleSend
2981
+ }
2982
+ ),
2983
+ conversationId && sidebar && convos.available && /* @__PURE__ */ jsxRuntime.jsx(
2984
+ SaveButton,
2985
+ {
2986
+ conversationId,
2987
+ conversations: convos.conversations,
2988
+ onStar: convos.starConversation
2989
+ }
2990
+ )
2991
+ ] })
2992
+ ] }, m.id);
2869
2993
  }),
2870
- isLastAssistant && !isLoading && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2871
- /* @__PURE__ */ jsxRuntime.jsx(
2872
- FollowUpChips,
2873
- {
2874
- suggestions,
2875
- onSelect: handleSend
2876
- }
2877
- ),
2878
- conversationId && sidebar && convos.available && /* @__PURE__ */ jsxRuntime.jsx(
2879
- SaveButton,
2880
- {
2881
- conversationId,
2882
- conversations: convos.conversations,
2883
- onStar: convos.starConversation
2884
- }
2885
- )
2886
- ] })
2887
- ] }, m.id);
2888
- }),
2889
- isLoading && messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, {})
2890
- ] }) }),
2891
- error && /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, authMode }),
2994
+ isLoading && messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, {})
2995
+ ] })
2996
+ }
2997
+ ) }),
2998
+ error && /* @__PURE__ */ jsxRuntime.jsx(
2999
+ ErrorBanner,
3000
+ {
3001
+ error,
3002
+ authMode: authMode ?? "none",
3003
+ onRetry: messages.some((m) => m.role === "user") ? () => {
3004
+ const lastUserMsg = messages.toReversed().find((m) => m.role === "user");
3005
+ const text = lastUserMsg?.parts?.filter((p) => p.type === "text").map((p) => p.text).join(" ");
3006
+ if (text) handleSend(text);
3007
+ } : void 0
3008
+ }
3009
+ ),
2892
3010
  /* @__PURE__ */ jsxRuntime.jsxs(
2893
3011
  "form",
2894
3012
  {
@@ -2900,22 +3018,24 @@ function AtlasChatInner({
2900
3018
  className: "flex flex-none gap-2 border-t border-zinc-100 pt-4 dark:border-zinc-800",
2901
3019
  children: [
2902
3020
  /* @__PURE__ */ jsxRuntime.jsx(
2903
- "input",
3021
+ Input,
2904
3022
  {
2905
3023
  "data-atlas-input": true,
2906
3024
  value: input,
2907
3025
  onChange: (e) => setInput(e.target.value),
2908
3026
  placeholder: "Ask a question about your data...",
2909
- className: "min-w-0 flex-1 rounded-lg border border-zinc-200 bg-zinc-50 px-4 py-3 text-base text-zinc-900 placeholder-zinc-400 outline-none focus:border-blue-500 sm:text-sm dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder-zinc-600",
2910
- disabled: isLoading || healthFailed
3027
+ className: "min-w-0 flex-1 py-3 text-base sm:text-sm",
3028
+ disabled: isLoading || healthFailed,
3029
+ "aria-label": "Chat message"
2911
3030
  }
2912
3031
  ),
2913
3032
  /* @__PURE__ */ jsxRuntime.jsx(
2914
- "button",
3033
+ chunkIDXGFWFS_cjs.Button,
2915
3034
  {
2916
3035
  type: "submit",
2917
- disabled: isLoading || healthFailed || !input.trim(),
2918
- className: "shrink-0 rounded-lg bg-blue-600 px-5 py-3 text-sm font-medium text-white transition-colors hover:bg-blue-500 disabled:opacity-40",
3036
+ disabled: isLoading || healthFailed,
3037
+ "aria-disabled": !(isLoading || healthFailed) && !input.trim() ? true : void 0,
3038
+ className: "shrink-0 px-5",
2919
3039
  children: "Ask"
2920
3040
  }
2921
3041
  )
@@ -2947,27 +3067,27 @@ function AtlasChatInner({
2947
3067
 
2948
3068
  Object.defineProperty(exports, "AUTH_MODES", {
2949
3069
  enumerable: true,
2950
- get: function () { return chunkUIRB6L36_cjs.AUTH_MODES; }
3070
+ get: function () { return chunkDZFSZSQB_cjs.AUTH_MODES; }
2951
3071
  });
2952
3072
  Object.defineProperty(exports, "THEME_STORAGE_KEY", {
2953
3073
  enumerable: true,
2954
- get: function () { return chunkUIRB6L36_cjs.THEME_STORAGE_KEY; }
3074
+ get: function () { return chunkDZFSZSQB_cjs.THEME_STORAGE_KEY; }
2955
3075
  });
2956
3076
  Object.defineProperty(exports, "buildThemeInitScript", {
2957
3077
  enumerable: true,
2958
- get: function () { return chunkUIRB6L36_cjs.buildThemeInitScript; }
3078
+ get: function () { return chunkDZFSZSQB_cjs.buildThemeInitScript; }
2959
3079
  });
2960
3080
  Object.defineProperty(exports, "parseChatError", {
2961
3081
  enumerable: true,
2962
- get: function () { return chunkUIRB6L36_cjs.parseChatError; }
3082
+ get: function () { return chunkDZFSZSQB_cjs.parseChatError; }
2963
3083
  });
2964
3084
  Object.defineProperty(exports, "setTheme", {
2965
3085
  enumerable: true,
2966
- get: function () { return chunkUIRB6L36_cjs.setTheme; }
3086
+ get: function () { return chunkDZFSZSQB_cjs.setTheme; }
2967
3087
  });
2968
3088
  Object.defineProperty(exports, "useConversations", {
2969
3089
  enumerable: true,
2970
- get: function () { return chunkUIRB6L36_cjs.useConversations; }
3090
+ get: function () { return chunkDZFSZSQB_cjs.useConversations; }
2971
3091
  });
2972
3092
  exports.AtlasChat = AtlasChat;
2973
3093
  exports.AtlasUIProvider = AtlasUIProvider;