@useatlas/react 0.0.1

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 (98) hide show
  1. package/README.md +95 -0
  2. package/dist/chunk-2WFDP7G5.js +231 -0
  3. package/dist/chunk-2WFDP7G5.js.map +1 -0
  4. package/dist/chunk-44HBZYKP.js +224 -0
  5. package/dist/chunk-44HBZYKP.js.map +1 -0
  6. package/dist/chunk-5SEVKHS5.cjs +229 -0
  7. package/dist/chunk-5SEVKHS5.cjs.map +1 -0
  8. package/dist/chunk-UIRB6L36.cjs +249 -0
  9. package/dist/chunk-UIRB6L36.cjs.map +1 -0
  10. package/dist/hooks.cjs +251 -0
  11. package/dist/hooks.cjs.map +1 -0
  12. package/dist/hooks.d.cts +132 -0
  13. package/dist/hooks.d.ts +132 -0
  14. package/dist/hooks.js +237 -0
  15. package/dist/hooks.js.map +1 -0
  16. package/dist/index.cjs +2976 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.d.cts +69 -0
  19. package/dist/index.d.ts +69 -0
  20. package/dist/index.js +2926 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/result-chart-NFAJ4IQ5.js +398 -0
  23. package/dist/result-chart-NFAJ4IQ5.js.map +1 -0
  24. package/dist/result-chart-YLCKBNV4.cjs +400 -0
  25. package/dist/result-chart-YLCKBNV4.cjs.map +1 -0
  26. package/dist/styles.css +59 -0
  27. package/dist/use-dark-mode-rFxawUv1.d.cts +123 -0
  28. package/dist/use-dark-mode-rFxawUv1.d.ts +123 -0
  29. package/dist/widget.css +2 -0
  30. package/dist/widget.js +445 -0
  31. package/package.json +113 -0
  32. package/src/components/__tests__/tool-renderers.test.tsx +239 -0
  33. package/src/components/actions/action-approval-card.tsx +296 -0
  34. package/src/components/actions/action-status-badge.tsx +50 -0
  35. package/src/components/admin/change-password-dialog.tsx +128 -0
  36. package/src/components/atlas-chat.tsx +656 -0
  37. package/src/components/chart/chart-detection.ts +318 -0
  38. package/src/components/chart/result-chart.tsx +590 -0
  39. package/src/components/chat/api-key-bar.tsx +66 -0
  40. package/src/components/chat/copy-button.tsx +25 -0
  41. package/src/components/chat/data-table.tsx +104 -0
  42. package/src/components/chat/error-banner.tsx +32 -0
  43. package/src/components/chat/explore-card.tsx +41 -0
  44. package/src/components/chat/follow-up-chips.tsx +29 -0
  45. package/src/components/chat/loading-card.tsx +10 -0
  46. package/src/components/chat/managed-auth-card.tsx +116 -0
  47. package/src/components/chat/markdown.tsx +146 -0
  48. package/src/components/chat/python-result-card.tsx +245 -0
  49. package/src/components/chat/sql-block.tsx +54 -0
  50. package/src/components/chat/sql-result-card.tsx +163 -0
  51. package/src/components/chat/starter-prompts.ts +6 -0
  52. package/src/components/chat/tool-part.tsx +106 -0
  53. package/src/components/chat/typing-indicator.tsx +22 -0
  54. package/src/components/conversations/conversation-item.tsx +135 -0
  55. package/src/components/conversations/conversation-list.tsx +69 -0
  56. package/src/components/conversations/conversation-sidebar.tsx +113 -0
  57. package/src/components/conversations/delete-confirmation.tsx +27 -0
  58. package/src/components/schema-explorer/schema-explorer.tsx +517 -0
  59. package/src/components/ui/alert-dialog.tsx +196 -0
  60. package/src/components/ui/badge.tsx +48 -0
  61. package/src/components/ui/button.tsx +64 -0
  62. package/src/components/ui/card.tsx +92 -0
  63. package/src/components/ui/dialog.tsx +158 -0
  64. package/src/components/ui/dropdown-menu.tsx +257 -0
  65. package/src/components/ui/input.tsx +21 -0
  66. package/src/components/ui/label.tsx +24 -0
  67. package/src/components/ui/scroll-area.tsx +62 -0
  68. package/src/components/ui/separator.tsx +28 -0
  69. package/src/components/ui/sheet.tsx +143 -0
  70. package/src/components/ui/table.tsx +116 -0
  71. package/src/components/ui/toggle-group.tsx +83 -0
  72. package/src/components/ui/toggle.tsx +47 -0
  73. package/src/context.tsx +85 -0
  74. package/src/env.d.ts +9 -0
  75. package/src/hooks/__tests__/provider.test.tsx +83 -0
  76. package/src/hooks/__tests__/use-atlas-auth.test.tsx +283 -0
  77. package/src/hooks/__tests__/use-atlas-chat.test.tsx +157 -0
  78. package/src/hooks/__tests__/use-atlas-conversations.test.tsx +159 -0
  79. package/src/hooks/__tests__/use-atlas-theme.test.tsx +56 -0
  80. package/src/hooks/index.ts +47 -0
  81. package/src/hooks/provider.tsx +77 -0
  82. package/src/hooks/theme-init-script.ts +17 -0
  83. package/src/hooks/use-atlas-auth.ts +131 -0
  84. package/src/hooks/use-atlas-chat.ts +102 -0
  85. package/src/hooks/use-atlas-conversations.ts +61 -0
  86. package/src/hooks/use-atlas-theme.ts +34 -0
  87. package/src/hooks/use-conversations.ts +189 -0
  88. package/src/hooks/use-dark-mode.ts +150 -0
  89. package/src/index.ts +36 -0
  90. package/src/lib/action-types.ts +11 -0
  91. package/src/lib/helpers.ts +198 -0
  92. package/src/lib/tool-renderer-types.ts +76 -0
  93. package/src/lib/types.ts +29 -0
  94. package/src/lib/utils.ts +6 -0
  95. package/src/styles.css +59 -0
  96. package/src/test-setup.ts +55 -0
  97. package/src/widget-entry.ts +20 -0
  98. package/src/widget.css +12 -0
@@ -0,0 +1,229 @@
1
+ 'use strict';
2
+
3
+ // src/components/chart/chart-detection.ts
4
+ var CHART_COLORS_LIGHT = [
5
+ "#3b82f6",
6
+ // blue-500
7
+ "#10b981",
8
+ // emerald-500
9
+ "#f59e0b",
10
+ // amber-500
11
+ "#ef4444",
12
+ // red-500
13
+ "#8b5cf6",
14
+ // violet-500
15
+ "#06b6d4",
16
+ // cyan-500
17
+ "#f97316",
18
+ // orange-500
19
+ "#ec4899"
20
+ // pink-500
21
+ ];
22
+ var CHART_COLORS_DARK = [
23
+ "#60a5fa",
24
+ // blue-400
25
+ "#34d399",
26
+ // emerald-400
27
+ "#fbbf24",
28
+ // amber-400
29
+ "#f87171",
30
+ // red-400
31
+ "#a78bfa",
32
+ // violet-400
33
+ "#22d3ee",
34
+ // cyan-400
35
+ "#fb923c",
36
+ // orange-400
37
+ "#f472b6"
38
+ // pink-400
39
+ ];
40
+ var DATE_HEADER_HINTS = /^(date|month|year|quarter|week|day|period|time|timestamp)$/i;
41
+ var CATEGORICAL_HEADER_HINTS = /^(name|type|category|status|region|country|industry|department|plan|tier|segment|group|label|source|channel)$/i;
42
+ var SKIP_HEADER_HINTS = /^(id|uuid|_id|pk|key)$/i;
43
+ var ISO_DATE_RE = /^\d{4}-\d{2}/;
44
+ var MONTH_NAME_RE = /^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i;
45
+ var YEAR_ONLY_RE = /^(19|20)\d{2}$/;
46
+ var QUARTER_RE = /^Q[1-4]\s*\d{4}$/i;
47
+ function classifyColumn(header, values) {
48
+ const nonEmpty = values.filter((v) => v !== "" && v != null);
49
+ if (nonEmpty.length === 0) return "unknown";
50
+ if (SKIP_HEADER_HINTS.test(header)) return "unknown";
51
+ const numericCount = nonEmpty.filter((v) => {
52
+ const n = Number(v.replace(/,/g, ""));
53
+ return isFinite(n);
54
+ }).length;
55
+ const numericRatio = numericCount / nonEmpty.length;
56
+ const dateCount = nonEmpty.filter(
57
+ (v) => ISO_DATE_RE.test(v) || MONTH_NAME_RE.test(v) || YEAR_ONLY_RE.test(v) || QUARTER_RE.test(v)
58
+ ).length;
59
+ const dateRatio = dateCount / nonEmpty.length;
60
+ if (DATE_HEADER_HINTS.test(header) && dateRatio > 0.3) return "date";
61
+ if (DATE_HEADER_HINTS.test(header) && numericRatio < 0.9) return "date";
62
+ if (dateRatio > 0.7) return "date";
63
+ if (numericRatio > 0.8) return "numeric";
64
+ if (CATEGORICAL_HEADER_HINTS.test(header)) return "categorical";
65
+ const unique = new Set(nonEmpty);
66
+ if (unique.size < 50) return "categorical";
67
+ return "unknown";
68
+ }
69
+ function detectCharts(headers, rows) {
70
+ if (headers.length === 0 || rows.length < 2) {
71
+ return { chartable: false, columns: [] };
72
+ }
73
+ const seen = /* @__PURE__ */ new Map();
74
+ const dedupedHeaders = headers.map((h) => {
75
+ const count = seen.get(h) ?? 0;
76
+ seen.set(h, count + 1);
77
+ return count > 0 ? `${h}_${count + 1}` : h;
78
+ });
79
+ const columns = dedupedHeaders.map((header, index) => {
80
+ const values = rows.map((r) => r[index] ?? "");
81
+ const type = classifyColumn(header, values);
82
+ const uniqueCount = new Set(values.filter((v) => v !== "")).size;
83
+ return { index, header, type, uniqueCount };
84
+ });
85
+ const dateColumns = columns.filter((c) => c.type === "date");
86
+ const numericColumns = columns.filter((c) => c.type === "numeric");
87
+ const categoricalColumns = columns.filter((c) => c.type === "categorical");
88
+ if (numericColumns.length === 0) {
89
+ return { chartable: false, columns };
90
+ }
91
+ const recommendations = [];
92
+ if (dateColumns.length >= 1 && numericColumns.length >= 1) {
93
+ recommendations.push({
94
+ type: "line",
95
+ categoryColumn: dateColumns[0],
96
+ valueColumns: numericColumns,
97
+ reason: `Time-series: ${dateColumns[0].header} vs ${numericColumns.map((c) => c.header).join(", ")}`
98
+ });
99
+ }
100
+ if (dateColumns.length >= 1 && numericColumns.length >= 1) {
101
+ recommendations.push({
102
+ type: "area",
103
+ categoryColumn: dateColumns[0],
104
+ valueColumns: numericColumns,
105
+ reason: `Volume over time: ${numericColumns.map((c) => c.header).join(", ")} by ${dateColumns[0].header}`
106
+ });
107
+ }
108
+ if (categoricalColumns.length >= 1 && numericColumns.length >= 2) {
109
+ recommendations.push({
110
+ type: "stacked-bar",
111
+ categoryColumn: categoricalColumns[0],
112
+ valueColumns: numericColumns,
113
+ reason: `Stacked: ${numericColumns.map((c) => c.header).join(", ")} by ${categoricalColumns[0].header}`
114
+ });
115
+ }
116
+ if (categoricalColumns.length >= 1 && numericColumns.length >= 1) {
117
+ recommendations.push({
118
+ type: "bar",
119
+ categoryColumn: categoricalColumns[0],
120
+ valueColumns: numericColumns,
121
+ reason: `Comparison: ${numericColumns.map((c) => c.header).join(", ")} by ${categoricalColumns[0].header}`
122
+ });
123
+ }
124
+ if (categoricalColumns.length >= 1 && numericColumns.length >= 1) {
125
+ const cat = categoricalColumns[0];
126
+ if (cat.uniqueCount >= 2 && cat.uniqueCount <= 7) {
127
+ recommendations.push({
128
+ type: "pie",
129
+ categoryColumn: cat,
130
+ valueColumns: [numericColumns[0]],
131
+ reason: `Distribution: ${numericColumns[0].header} by ${cat.header}`
132
+ });
133
+ }
134
+ }
135
+ if (numericColumns.length >= 2) {
136
+ const [xCol, yCol, ...rest] = numericColumns;
137
+ const scatterValues = rest.length > 0 ? [yCol, ...rest] : [yCol];
138
+ recommendations.push({
139
+ type: "scatter",
140
+ categoryColumn: xCol,
141
+ valueColumns: scatterValues,
142
+ reason: `Correlation: ${xCol.header} vs ${yCol.header}${rest.length > 0 ? ` (size: ${rest[0].header})` : ""}`
143
+ });
144
+ }
145
+ if (!recommendations.some((r) => r.type === "bar") && numericColumns.length >= 2) {
146
+ const first = columns[0];
147
+ const rest = numericColumns.filter((c) => c.index !== first.index);
148
+ if (rest.length >= 1) {
149
+ recommendations.push({
150
+ type: "bar",
151
+ categoryColumn: first,
152
+ valueColumns: rest,
153
+ reason: `Fallback: ${rest.map((c) => c.header).join(", ")} by ${first.header}`
154
+ });
155
+ }
156
+ }
157
+ if (dateColumns.length >= 1 && numericColumns.length >= 1 && !recommendations.some((r) => r.type === "bar")) {
158
+ recommendations.push({
159
+ type: "bar",
160
+ categoryColumn: dateColumns[0],
161
+ valueColumns: numericColumns,
162
+ reason: `Comparison: ${numericColumns.map((c) => c.header).join(", ")} by ${dateColumns[0].header}`
163
+ });
164
+ }
165
+ if (recommendations.length === 0) {
166
+ return { chartable: false, columns };
167
+ }
168
+ const data = transformData(rows, recommendations[0]);
169
+ return {
170
+ chartable: true,
171
+ columns,
172
+ recommendations,
173
+ data
174
+ };
175
+ }
176
+ function parseNumericValue(raw) {
177
+ const cleaned = raw.replace(/[$%,\s]/g, "");
178
+ if (cleaned === "" || cleaned === "-") return 0;
179
+ const num = Number(cleaned);
180
+ return isFinite(num) ? num : 0;
181
+ }
182
+ function isFiniteNumeric(raw) {
183
+ const cleaned = raw.replace(/[$%,\s]/g, "");
184
+ if (cleaned === "" || cleaned === "-") return false;
185
+ return isFinite(Number(cleaned));
186
+ }
187
+ function transformData(rows, recommendation) {
188
+ const catIdx = recommendation.categoryColumn.index;
189
+ const catHeader = recommendation.categoryColumn.header;
190
+ const valIdxs = recommendation.valueColumns.map((c) => c.index);
191
+ if (recommendation.type === "scatter") {
192
+ const yIdx = recommendation.valueColumns[0].index;
193
+ return rows.flatMap((row) => {
194
+ const rawX = row[catIdx] ?? "";
195
+ const rawY = row[yIdx] ?? "";
196
+ if (!isFiniteNumeric(rawX) || !isFiniteNumeric(rawY)) return [];
197
+ const record = {};
198
+ record[catHeader] = parseNumericValue(rawX);
199
+ for (const vc of recommendation.valueColumns) {
200
+ record[vc.header] = parseNumericValue(row[vc.index] ?? "0");
201
+ }
202
+ return [record];
203
+ });
204
+ }
205
+ let effectiveRows = rows;
206
+ if ((recommendation.type === "bar" || recommendation.type === "stacked-bar") && rows.length > 30) {
207
+ const valIdx = valIdxs[0];
208
+ effectiveRows = [...rows].sort((a, b) => {
209
+ const av = parseNumericValue(a[valIdx] ?? "0");
210
+ const bv = parseNumericValue(b[valIdx] ?? "0");
211
+ return bv - av;
212
+ }).slice(0, 20);
213
+ }
214
+ return effectiveRows.map((row) => {
215
+ const record = {};
216
+ record[catHeader] = row[catIdx] ?? "";
217
+ for (const vc of recommendation.valueColumns) {
218
+ record[vc.header] = parseNumericValue(row[vc.index] ?? "0");
219
+ }
220
+ return record;
221
+ });
222
+ }
223
+
224
+ exports.CHART_COLORS_DARK = CHART_COLORS_DARK;
225
+ exports.CHART_COLORS_LIGHT = CHART_COLORS_LIGHT;
226
+ exports.detectCharts = detectCharts;
227
+ exports.transformData = transformData;
228
+ //# sourceMappingURL=chunk-5SEVKHS5.cjs.map
229
+ //# sourceMappingURL=chunk-5SEVKHS5.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chart/chart-detection.ts"],"names":[],"mappings":"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","file":"chunk-5SEVKHS5.cjs","sourcesContent":["/* Chart detection — pure functions, zero React deps. Kept framework-agnostic for direct unit testing. */\n\nexport type ColumnType = \"numeric\" | \"date\" | \"categorical\" | \"unknown\";\n\nexport type ClassifiedColumn = {\n index: number;\n header: string;\n type: ColumnType;\n uniqueCount: number;\n};\n\nexport type ChartType = \"bar\" | \"line\" | \"pie\" | \"area\" | \"stacked-bar\" | \"scatter\";\n\nexport type ChartRecommendation = {\n type: ChartType;\n categoryColumn: ClassifiedColumn;\n valueColumns: [ClassifiedColumn, ...ClassifiedColumn[]];\n reason: string;\n};\n\nexport type RechartsRow = Record<string, string | number>;\n\ntype NonChartableResult = {\n chartable: false;\n columns: ClassifiedColumn[];\n};\n\ntype ChartableResult = {\n chartable: true;\n columns: ClassifiedColumn[];\n recommendations: [ChartRecommendation, ...ChartRecommendation[]];\n data: RechartsRow[];\n};\n\nexport type ChartDetectionResult = NonChartableResult | ChartableResult;\n\n/* ------------------------------------------------------------------ */\n/* Color palettes (Tailwind weights) */\n/* ------------------------------------------------------------------ */\n\nexport const CHART_COLORS_LIGHT = [\n \"#3b82f6\", // blue-500\n \"#10b981\", // emerald-500\n \"#f59e0b\", // amber-500\n \"#ef4444\", // red-500\n \"#8b5cf6\", // violet-500\n \"#06b6d4\", // cyan-500\n \"#f97316\", // orange-500\n \"#ec4899\", // pink-500\n];\n\nexport const CHART_COLORS_DARK = [\n \"#60a5fa\", // blue-400\n \"#34d399\", // emerald-400\n \"#fbbf24\", // amber-400\n \"#f87171\", // red-400\n \"#a78bfa\", // violet-400\n \"#22d3ee\", // cyan-400\n \"#fb923c\", // orange-400\n \"#f472b6\", // pink-400\n];\n\n/* ------------------------------------------------------------------ */\n/* Column classification */\n/* ------------------------------------------------------------------ */\n\nconst DATE_HEADER_HINTS = /^(date|month|year|quarter|week|day|period|time|timestamp)$/i;\nconst CATEGORICAL_HEADER_HINTS = /^(name|type|category|status|region|country|industry|department|plan|tier|segment|group|label|source|channel)$/i;\nconst SKIP_HEADER_HINTS = /^(id|uuid|_id|pk|key)$/i;\n\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}/;\nconst MONTH_NAME_RE = /^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i;\nconst YEAR_ONLY_RE = /^(19|20)\\d{2}$/;\nconst QUARTER_RE = /^Q[1-4]\\s*\\d{4}$/i;\n\nexport function classifyColumn(header: string, values: string[]): ColumnType {\n const nonEmpty = values.filter((v) => v !== \"\" && v != null);\n if (nonEmpty.length === 0) return \"unknown\";\n\n // Header hint: skip ID-like columns\n if (SKIP_HEADER_HINTS.test(header)) return \"unknown\";\n\n // Numeric check: >80% parse as finite numbers (date check takes priority for overlapping values)\n const numericCount = nonEmpty.filter((v) => {\n const n = Number(v.replace(/,/g, \"\"));\n return isFinite(n);\n }).length;\n const numericRatio = numericCount / nonEmpty.length;\n\n // Date check: >70% match date patterns (>30% when header hints match)\n const dateCount = nonEmpty.filter(\n (v) => ISO_DATE_RE.test(v) || MONTH_NAME_RE.test(v) || YEAR_ONLY_RE.test(v) || QUARTER_RE.test(v),\n ).length;\n const dateRatio = dateCount / nonEmpty.length;\n\n // Header hint tiebreaker: if header matches date keywords...\n // (a) ...and at least some values look date-like, trust the header\n // (b) ...and values aren't overwhelmingly numeric (catches year-only values)\n if (DATE_HEADER_HINTS.test(header) && dateRatio > 0.3) return \"date\";\n if (DATE_HEADER_HINTS.test(header) && numericRatio < 0.9) return \"date\";\n\n if (dateRatio > 0.7) return \"date\";\n if (numericRatio > 0.8) return \"numeric\";\n\n // Categorical header hint\n if (CATEGORICAL_HEADER_HINTS.test(header)) return \"categorical\";\n\n // Categorical fallback: text values with <50 unique entries (higher cardinality suggests free-text or IDs)\n const unique = new Set(nonEmpty);\n if (unique.size < 50) return \"categorical\";\n\n return \"unknown\";\n}\n\n/* ------------------------------------------------------------------ */\n/* Chart recommendation engine */\n/* ------------------------------------------------------------------ */\n\nexport function detectCharts(headers: string[], rows: string[][]): ChartDetectionResult {\n if (headers.length === 0 || rows.length < 2) {\n return { chartable: false, columns: [] };\n }\n\n // Deduplicate headers so chart dataKey matches transformed data keys\n const seen = new Map<string, number>();\n const dedupedHeaders = headers.map((h) => {\n const count = seen.get(h) ?? 0;\n seen.set(h, count + 1);\n return count > 0 ? `${h}_${count + 1}` : h;\n });\n\n const columns: ClassifiedColumn[] = dedupedHeaders.map((header, index) => {\n const values = rows.map((r) => r[index] ?? \"\");\n const type = classifyColumn(header, values);\n const uniqueCount = new Set(values.filter((v) => v !== \"\")).size;\n return { index, header, type, uniqueCount };\n });\n\n const dateColumns = columns.filter((c) => c.type === \"date\");\n const numericColumns = columns.filter((c) => c.type === \"numeric\");\n const categoricalColumns = columns.filter((c) => c.type === \"categorical\");\n\n if (numericColumns.length === 0) {\n return { chartable: false, columns };\n }\n\n const recommendations: ChartRecommendation[] = [];\n\n // Line: date + numeric (time-series, highest priority)\n if (dateColumns.length >= 1 && numericColumns.length >= 1) {\n recommendations.push({\n type: \"line\",\n categoryColumn: dateColumns[0],\n valueColumns: numericColumns as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Time-series: ${dateColumns[0].header} vs ${numericColumns.map((c) => c.header).join(\", \")}`,\n });\n }\n\n // Area: alternative to line for date + numeric (volume/magnitude over time)\n if (dateColumns.length >= 1 && numericColumns.length >= 1) {\n recommendations.push({\n type: \"area\",\n categoryColumn: dateColumns[0],\n valueColumns: numericColumns as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Volume over time: ${numericColumns.map((c) => c.header).join(\", \")} by ${dateColumns[0].header}`,\n });\n }\n\n // Stacked bar: categorical + multiple numeric columns (part-to-whole comparison)\n if (categoricalColumns.length >= 1 && numericColumns.length >= 2) {\n recommendations.push({\n type: \"stacked-bar\",\n categoryColumn: categoricalColumns[0],\n valueColumns: numericColumns as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Stacked: ${numericColumns.map((c) => c.header).join(\", \")} by ${categoricalColumns[0].header}`,\n });\n }\n\n // Bar: categorical + numeric\n if (categoricalColumns.length >= 1 && numericColumns.length >= 1) {\n recommendations.push({\n type: \"bar\",\n categoryColumn: categoricalColumns[0],\n valueColumns: numericColumns as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Comparison: ${numericColumns.map((c) => c.header).join(\", \")} by ${categoricalColumns[0].header}`,\n });\n }\n\n // Pie: first categorical column (2-7 unique values) + first numeric column\n if (categoricalColumns.length >= 1 && numericColumns.length >= 1) {\n const cat = categoricalColumns[0];\n if (cat.uniqueCount >= 2 && cat.uniqueCount <= 7) {\n recommendations.push({\n type: \"pie\",\n categoryColumn: cat,\n valueColumns: [numericColumns[0]],\n reason: `Distribution: ${numericColumns[0].header} by ${cat.header}`,\n });\n }\n }\n\n // Scatter: 2+ numeric columns (correlation analysis)\n if (numericColumns.length >= 2) {\n const [xCol, yCol, ...rest] = numericColumns;\n const scatterValues = rest.length > 0\n ? [yCol, ...rest] as [ClassifiedColumn, ...ClassifiedColumn[]]\n : [yCol] as [ClassifiedColumn, ...ClassifiedColumn[]];\n recommendations.push({\n type: \"scatter\",\n categoryColumn: xCol,\n valueColumns: scatterValues,\n reason: `Correlation: ${xCol.header} vs ${yCol.header}${rest.length > 0 ? ` (size: ${rest[0].header})` : \"\"}`,\n });\n }\n\n // Fallback: when all columns are numeric, treat first as category axis (often an index or bucket label)\n if (!recommendations.some((r) => r.type === \"bar\") && numericColumns.length >= 2) {\n const first = columns[0];\n const rest = numericColumns.filter((c) => c.index !== first.index);\n if (rest.length >= 1) {\n recommendations.push({\n type: \"bar\",\n categoryColumn: first,\n valueColumns: rest as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Fallback: ${rest.map((c) => c.header).join(\", \")} by ${first.header}`,\n });\n }\n }\n\n // Also allow bar for date columns (as a secondary option after line)\n if (dateColumns.length >= 1 && numericColumns.length >= 1 && !recommendations.some((r) => r.type === \"bar\")) {\n recommendations.push({\n type: \"bar\",\n categoryColumn: dateColumns[0],\n valueColumns: numericColumns as [ClassifiedColumn, ...ClassifiedColumn[]],\n reason: `Comparison: ${numericColumns.map((c) => c.header).join(\", \")} by ${dateColumns[0].header}`,\n });\n }\n\n if (recommendations.length === 0) {\n return { chartable: false, columns };\n }\n\n const data = transformData(rows, recommendations[0]);\n\n return {\n chartable: true,\n columns,\n recommendations: recommendations as [ChartRecommendation, ...ChartRecommendation[]],\n data,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Data transform */\n/* ------------------------------------------------------------------ */\n\nfunction parseNumericValue(raw: string): number {\n const cleaned = raw.replace(/[$%,\\s]/g, \"\");\n if (cleaned === \"\" || cleaned === \"-\") return 0;\n const num = Number(cleaned);\n return isFinite(num) ? num : 0;\n}\n\nfunction isFiniteNumeric(raw: string): boolean {\n const cleaned = raw.replace(/[$%,\\s]/g, \"\");\n if (cleaned === \"\" || cleaned === \"-\") return false;\n return isFinite(Number(cleaned));\n}\n\nexport function transformData(\n rows: string[][],\n recommendation: ChartRecommendation,\n): RechartsRow[] {\n const catIdx = recommendation.categoryColumn.index;\n const catHeader = recommendation.categoryColumn.header;\n const valIdxs = recommendation.valueColumns.map((c) => c.index);\n\n // Scatter: both axes are numeric — categoryColumn is x, first valueColumn is y, optional z for size\n // Filter out rows where x or y are non-numeric to avoid misleading zero-origin clusters\n if (recommendation.type === \"scatter\") {\n const yIdx = recommendation.valueColumns[0].index;\n return rows.flatMap((row) => {\n const rawX = row[catIdx] ?? \"\";\n const rawY = row[yIdx] ?? \"\";\n if (!isFiniteNumeric(rawX) || !isFiniteNumeric(rawY)) return [];\n const record: RechartsRow = {};\n record[catHeader] = parseNumericValue(rawX);\n for (const vc of recommendation.valueColumns) {\n record[vc.header] = parseNumericValue(row[vc.index] ?? \"0\");\n }\n return [record];\n });\n }\n\n // Cap rows for bar/stacked-bar charts with many categories\n let effectiveRows = rows;\n if ((recommendation.type === \"bar\" || recommendation.type === \"stacked-bar\") && rows.length > 30) {\n // Sort by first value column descending, take top 20\n const valIdx = valIdxs[0];\n effectiveRows = [...rows]\n .sort((a, b) => {\n const av = parseNumericValue(a[valIdx] ?? \"0\");\n const bv = parseNumericValue(b[valIdx] ?? \"0\");\n return bv - av;\n })\n .slice(0, 20);\n }\n\n return effectiveRows.map((row) => {\n const record: RechartsRow = {};\n record[catHeader] = row[catIdx] ?? \"\";\n for (const vc of recommendation.valueColumns) {\n record[vc.header] = parseNumericValue(row[vc.index] ?? \"0\");\n }\n return record;\n });\n}\n"]}
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ var types = require('@useatlas/types');
4
+ var errors = require('@useatlas/types/errors');
5
+ var react = require('react');
6
+
7
+ // src/lib/types.ts
8
+
9
+ // src/hooks/theme-init-script.ts
10
+ var THEME_STORAGE_KEY = "atlas-theme";
11
+ function buildThemeInitScript() {
12
+ return `try{var t=localStorage.getItem("${THEME_STORAGE_KEY}");var d=t==="dark"||(t!=="light"&&window.matchMedia("(prefers-color-scheme:dark)").matches);if(d)document.documentElement.classList.add("dark")}catch(e){}`;
13
+ }
14
+ var OKLCH_RE = /^oklch\(\s*[\d.]+\s+[\d.]+\s+[\d.]+\s*(?:\/\s*[\d.%]+\s*)?\)$/;
15
+ var _mode = "system";
16
+ var _listeners = /* @__PURE__ */ new Set();
17
+ function notify() {
18
+ for (const fn of _listeners) fn();
19
+ }
20
+ function init() {
21
+ try {
22
+ const stored = localStorage.getItem(THEME_STORAGE_KEY);
23
+ if (stored === "light" || stored === "dark" || stored === "system") {
24
+ _mode = stored;
25
+ }
26
+ } catch (err) {
27
+ console.warn("Could not read theme preference from localStorage:", err);
28
+ }
29
+ }
30
+ if (typeof window !== "undefined") init();
31
+ function systemPrefersDark() {
32
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
33
+ }
34
+ function resolveIsDark(mode) {
35
+ if (mode === "dark") return true;
36
+ if (mode === "light") return false;
37
+ return systemPrefersDark();
38
+ }
39
+ function applyClass(isDark) {
40
+ if (typeof document === "undefined") return;
41
+ document.documentElement.classList.toggle("dark", isDark);
42
+ }
43
+ function subscribeIsDark(onChange) {
44
+ _listeners.add(onChange);
45
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
46
+ const handler = () => {
47
+ applyClass(resolveIsDark(_mode));
48
+ onChange();
49
+ };
50
+ mq.addEventListener("change", handler);
51
+ return () => {
52
+ _listeners.delete(onChange);
53
+ mq.removeEventListener("change", handler);
54
+ };
55
+ }
56
+ function getSnapshotIsDark() {
57
+ return resolveIsDark(_mode);
58
+ }
59
+ function getServerSnapshotIsDark() {
60
+ return false;
61
+ }
62
+ function subscribeMode(onChange) {
63
+ _listeners.add(onChange);
64
+ return () => {
65
+ _listeners.delete(onChange);
66
+ };
67
+ }
68
+ function getSnapshotMode() {
69
+ return _mode;
70
+ }
71
+ function getServerSnapshotMode() {
72
+ return "system";
73
+ }
74
+ function setTheme(mode) {
75
+ _mode = mode;
76
+ const isDark = resolveIsDark(mode);
77
+ applyClass(isDark);
78
+ try {
79
+ localStorage.setItem(THEME_STORAGE_KEY, mode);
80
+ } catch (err) {
81
+ console.warn("Could not persist theme preference to localStorage:", err);
82
+ }
83
+ notify();
84
+ }
85
+ function applyBrandColor(color) {
86
+ if (typeof document === "undefined") return;
87
+ document.documentElement.style.setProperty("--atlas-brand", color);
88
+ }
89
+ var DarkModeContext = react.createContext(false);
90
+ function useDarkMode() {
91
+ return react.useSyncExternalStore(subscribeIsDark, getSnapshotIsDark, getServerSnapshotIsDark);
92
+ }
93
+ function useThemeMode() {
94
+ return react.useSyncExternalStore(subscribeMode, getSnapshotMode, getServerSnapshotMode);
95
+ }
96
+ function transformMessages(messages) {
97
+ return messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => {
98
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
99
+ return {
100
+ id: m.id,
101
+ role: m.role,
102
+ parts: [{ type: "text", text: content }]
103
+ };
104
+ });
105
+ }
106
+ function useConversations(opts) {
107
+ const [conversations, setConversations] = react.useState([]);
108
+ const [total, setTotal] = react.useState(0);
109
+ const [loading, setLoading] = react.useState(false);
110
+ const [available, setAvailable] = react.useState(true);
111
+ const [selectedId, setSelectedId] = react.useState(null);
112
+ const fetchedRef = react.useRef(false);
113
+ const networkFailRef = react.useRef(0);
114
+ const fetchList = react.useCallback(async () => {
115
+ if (!opts.enabled || !available) return;
116
+ setLoading(true);
117
+ try {
118
+ const res = await fetch(`${opts.apiUrl}/api/v1/conversations?limit=50`, {
119
+ headers: opts.getHeaders(),
120
+ credentials: opts.getCredentials()
121
+ });
122
+ if (res.status === 404) {
123
+ setAvailable(false);
124
+ return;
125
+ }
126
+ if (!res.ok) {
127
+ const errorBody = await res.json().catch(() => null);
128
+ if (errorBody?.code === "not_available") {
129
+ setAvailable(false);
130
+ return;
131
+ }
132
+ console.warn(`fetchList: HTTP ${res.status}`, errorBody);
133
+ return;
134
+ }
135
+ const data = await res.json();
136
+ setConversations(data.conversations ?? []);
137
+ setTotal(data.total ?? 0);
138
+ fetchedRef.current = true;
139
+ } catch (err) {
140
+ console.warn("fetchList error:", err);
141
+ if (!fetchedRef.current) {
142
+ networkFailRef.current += 1;
143
+ if (networkFailRef.current >= 3) setAvailable(false);
144
+ }
145
+ } finally {
146
+ setLoading(false);
147
+ }
148
+ }, [opts.apiUrl, opts.enabled, opts.getHeaders, opts.getCredentials, available]);
149
+ const loadConversation = react.useCallback(async (id) => {
150
+ try {
151
+ const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}`, {
152
+ headers: opts.getHeaders(),
153
+ credentials: opts.getCredentials()
154
+ });
155
+ if (!res.ok) {
156
+ console.warn(`loadConversation: HTTP ${res.status} for ${id}`);
157
+ return null;
158
+ }
159
+ const data = await res.json();
160
+ return transformMessages(data.messages);
161
+ } catch (err) {
162
+ console.warn("loadConversation error:", err);
163
+ return null;
164
+ }
165
+ }, [opts.apiUrl, opts.getHeaders, opts.getCredentials]);
166
+ const deleteConversation = react.useCallback(async (id) => {
167
+ try {
168
+ const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}`, {
169
+ method: "DELETE",
170
+ headers: opts.getHeaders(),
171
+ credentials: opts.getCredentials()
172
+ });
173
+ if (!res.ok) {
174
+ console.warn(`deleteConversation: HTTP ${res.status} for ${id}`);
175
+ return false;
176
+ }
177
+ setConversations((prev) => prev.filter((c) => c.id !== id));
178
+ setTotal((prev) => Math.max(0, prev - 1));
179
+ if (selectedId === id) setSelectedId(null);
180
+ return true;
181
+ } catch (err) {
182
+ console.warn("deleteConversation error:", err);
183
+ return false;
184
+ }
185
+ }, [opts.apiUrl, opts.getHeaders, opts.getCredentials, selectedId]);
186
+ const starConversation = react.useCallback(async (id, starred) => {
187
+ setConversations(
188
+ (prev) => prev.map((c) => c.id === id ? { ...c, starred } : c)
189
+ );
190
+ try {
191
+ const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}/star`, {
192
+ method: "PATCH",
193
+ headers: { ...opts.getHeaders(), "Content-Type": "application/json" },
194
+ credentials: opts.getCredentials(),
195
+ body: JSON.stringify({ starred })
196
+ });
197
+ if (!res.ok) {
198
+ console.warn(`starConversation: HTTP ${res.status} for ${id}`);
199
+ setConversations(
200
+ (prev) => prev.map((c) => c.id === id ? { ...c, starred: !starred } : c)
201
+ );
202
+ return false;
203
+ }
204
+ return true;
205
+ } catch (err) {
206
+ console.warn("starConversation error:", err);
207
+ setConversations(
208
+ (prev) => prev.map((c) => c.id === id ? { ...c, starred: !starred } : c)
209
+ );
210
+ return false;
211
+ }
212
+ }, [opts.apiUrl, opts.getHeaders, opts.getCredentials]);
213
+ const refresh = react.useCallback(async () => {
214
+ await fetchList();
215
+ }, [fetchList]);
216
+ return {
217
+ conversations,
218
+ total,
219
+ loading,
220
+ available,
221
+ selectedId,
222
+ setSelectedId,
223
+ fetchList,
224
+ loadConversation,
225
+ deleteConversation,
226
+ starConversation,
227
+ refresh
228
+ };
229
+ }
230
+
231
+ Object.defineProperty(exports, "AUTH_MODES", {
232
+ enumerable: true,
233
+ get: function () { return types.AUTH_MODES; }
234
+ });
235
+ Object.defineProperty(exports, "parseChatError", {
236
+ enumerable: true,
237
+ get: function () { return errors.parseChatError; }
238
+ });
239
+ exports.DarkModeContext = DarkModeContext;
240
+ exports.OKLCH_RE = OKLCH_RE;
241
+ exports.THEME_STORAGE_KEY = THEME_STORAGE_KEY;
242
+ exports.applyBrandColor = applyBrandColor;
243
+ exports.buildThemeInitScript = buildThemeInitScript;
244
+ exports.setTheme = setTheme;
245
+ exports.useConversations = useConversations;
246
+ exports.useDarkMode = useDarkMode;
247
+ exports.useThemeMode = useThemeMode;
248
+ //# sourceMappingURL=chunk-UIRB6L36.cjs.map
249
+ //# sourceMappingURL=chunk-UIRB6L36.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/theme-init-script.ts","../src/hooks/use-dark-mode.ts","../src/hooks/use-conversations.ts"],"names":["createContext","useSyncExternalStore","useState","useRef","useCallback"],"mappings":"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","file":"chunk-UIRB6L36.cjs","sourcesContent":["/**\n * Blocking inline script for layout.tsx — prevents dark-mode flash on load.\n *\n * This file intentionally has NO \"use client\" directive so it can be imported\n * by server components (layout.tsx). The storage key must stay in sync with\n * use-dark-mode.ts.\n */\n\nexport const THEME_STORAGE_KEY = \"atlas-theme\";\n\n/**\n * Returns the inline script string for the blocking `<script>` in layout.tsx.\n * Reads atlas-theme from localStorage and sets the `dark` class before first paint.\n */\nexport function buildThemeInitScript(): string {\n return `try{var t=localStorage.getItem(\"${THEME_STORAGE_KEY}\");var d=t===\"dark\"||(t!==\"light\"&&window.matchMedia(\"(prefers-color-scheme:dark)\").matches);if(d)document.documentElement.classList.add(\"dark\")}catch(e){}`;\n}\n","\"use client\";\n\nimport { createContext, useSyncExternalStore } from \"react\";\n\n// ---------------------------------------------------------------------------\n// Theme types & constants\n// ---------------------------------------------------------------------------\n\nimport { THEME_STORAGE_KEY } from \"./theme-init-script\";\n\nexport type ThemeMode = \"light\" | \"dark\" | \"system\";\n\nexport { THEME_STORAGE_KEY };\n\n/**\n * Default brand color — must match `brand.css` `:root { --atlas-brand }` and\n * the `ATLAS_BRAND_COLOR` default in `packages/api/src/lib/settings.ts`.\n */\nexport const DEFAULT_BRAND_COLOR = \"oklch(0.759 0.148 167.71)\";\n\n/** Basic oklch format check — prevents obviously invalid values from breaking the theme. */\nexport const OKLCH_RE = /^oklch\\(\\s*[\\d.]+\\s+[\\d.]+\\s+[\\d.]+\\s*(?:\\/\\s*[\\d.%]+\\s*)?\\)$/;\n\n// ---------------------------------------------------------------------------\n// Shared state — single source of truth for the chosen mode.\n// Listeners are notified on change so useSyncExternalStore re-renders.\n// ---------------------------------------------------------------------------\n\nlet _mode: ThemeMode = \"system\";\nconst _listeners = new Set<() => void>();\n\nfunction notify() {\n for (const fn of _listeners) fn();\n}\n\n/** Read stored preference (called once on module load in the browser). */\nfunction init() {\n try {\n const stored = localStorage.getItem(THEME_STORAGE_KEY);\n if (stored === \"light\" || stored === \"dark\" || stored === \"system\") {\n _mode = stored;\n }\n } catch (err) {\n console.warn(\"Could not read theme preference from localStorage:\", err);\n }\n}\n\nif (typeof window !== \"undefined\") init();\n\n// ---------------------------------------------------------------------------\n// Derived boolean: is the effective theme dark?\n// ---------------------------------------------------------------------------\n\nfunction systemPrefersDark(): boolean {\n return typeof window !== \"undefined\" && window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n\nfunction resolveIsDark(mode: ThemeMode): boolean {\n if (mode === \"dark\") return true;\n if (mode === \"light\") return false;\n return systemPrefersDark();\n}\n\nfunction applyClass(isDark: boolean) {\n if (typeof document === \"undefined\") return;\n document.documentElement.classList.toggle(\"dark\", isDark);\n}\n\n// ---------------------------------------------------------------------------\n// External store for isDark (reacts to both mode changes AND system changes)\n// ---------------------------------------------------------------------------\n\nfunction subscribeIsDark(onChange: () => void) {\n _listeners.add(onChange);\n\n // Also listen for system preference changes (relevant when mode === \"system\").\n // Apply dark class immediately on OS change so the DOM stays in sync before React re-renders.\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = () => {\n applyClass(resolveIsDark(_mode));\n onChange();\n };\n mq.addEventListener(\"change\", handler);\n\n return () => {\n _listeners.delete(onChange);\n mq.removeEventListener(\"change\", handler);\n };\n}\n\nfunction getSnapshotIsDark() {\n return resolveIsDark(_mode);\n}\n\nfunction getServerSnapshotIsDark() {\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// External store for mode\n// ---------------------------------------------------------------------------\n\nfunction subscribeMode(onChange: () => void) {\n _listeners.add(onChange);\n return () => {\n _listeners.delete(onChange);\n };\n}\n\nfunction getSnapshotMode() {\n return _mode;\n}\n\nfunction getServerSnapshotMode(): ThemeMode {\n return \"system\";\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function setTheme(mode: ThemeMode) {\n _mode = mode;\n const isDark = resolveIsDark(mode);\n applyClass(isDark);\n try {\n localStorage.setItem(THEME_STORAGE_KEY, mode);\n } catch (err) {\n console.warn(\"Could not persist theme preference to localStorage:\", err);\n }\n notify();\n}\n\n/** Apply --atlas-brand on :root so theme tokens update without reload. */\nexport function applyBrandColor(color: string) {\n if (typeof document === \"undefined\") return;\n document.documentElement.style.setProperty(\"--atlas-brand\", color);\n}\n\nexport const DarkModeContext = createContext(false);\n\n/** Returns whether the effective theme is dark. */\nexport function useDarkMode(): boolean {\n return useSyncExternalStore(subscribeIsDark, getSnapshotIsDark, getServerSnapshotIsDark);\n}\n\n/** Returns the current ThemeMode (\"light\" | \"dark\" | \"system\"). */\nexport function useThemeMode(): ThemeMode {\n return useSyncExternalStore(subscribeMode, getSnapshotMode, getServerSnapshotMode);\n}\n","\"use client\";\n\nimport { useState, useCallback, useRef } from \"react\";\nimport type { Conversation, ConversationWithMessages, Message } from \"../lib/types\";\nimport type { UIMessage } from \"@ai-sdk/react\";\n\nexport interface UseConversationsOptions {\n apiUrl: string;\n enabled: boolean;\n getHeaders: () => Record<string, string>;\n getCredentials: () => RequestCredentials;\n}\n\nexport interface UseConversationsReturn {\n conversations: Conversation[];\n total: number;\n loading: boolean;\n available: boolean;\n selectedId: string | null;\n setSelectedId: (id: string | null) => void;\n fetchList: () => Promise<void>;\n loadConversation: (id: string) => Promise<UIMessage[] | null>;\n deleteConversation: (id: string) => Promise<boolean>;\n starConversation: (id: string, starred: boolean) => Promise<boolean>;\n refresh: () => Promise<void>;\n}\n\nexport function transformMessages(messages: Message[]): UIMessage[] {\n return messages\n .filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .map((m) => {\n const content = typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content);\n\n return {\n id: m.id,\n role: m.role as \"user\" | \"assistant\",\n parts: [{ type: \"text\" as const, text: content }],\n };\n });\n}\n\nexport function useConversations(opts: UseConversationsOptions): UseConversationsReturn {\n const [conversations, setConversations] = useState<Conversation[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(false);\n const [available, setAvailable] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const fetchedRef = useRef(false);\n const networkFailRef = useRef(0);\n\n const fetchList = useCallback(async () => {\n if (!opts.enabled || !available) return;\n setLoading(true);\n try {\n const res = await fetch(`${opts.apiUrl}/api/v1/conversations?limit=50`, {\n headers: opts.getHeaders(),\n credentials: opts.getCredentials(),\n });\n\n if (res.status === 404) {\n setAvailable(false);\n return;\n }\n\n if (!res.ok) {\n const errorBody = await res.json().catch(() => null);\n if (errorBody?.code === \"not_available\") {\n setAvailable(false);\n return;\n }\n console.warn(`fetchList: HTTP ${res.status}`, errorBody);\n return;\n }\n\n const data = await res.json();\n setConversations(data.conversations ?? []);\n setTotal(data.total ?? 0);\n fetchedRef.current = true;\n } catch (err) {\n console.warn(\"fetchList error:\", err);\n // Network error before any successful fetch — disable temporarily.\n // A subsequent explicit fetchList() call can retry.\n if (!fetchedRef.current) {\n networkFailRef.current += 1;\n if (networkFailRef.current >= 3) setAvailable(false);\n }\n } finally {\n setLoading(false);\n }\n }, [opts.apiUrl, opts.enabled, opts.getHeaders, opts.getCredentials, available]);\n\n const loadConversation = useCallback(async (id: string): Promise<UIMessage[] | null> => {\n try {\n const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}`, {\n headers: opts.getHeaders(),\n credentials: opts.getCredentials(),\n });\n\n if (!res.ok) {\n console.warn(`loadConversation: HTTP ${res.status} for ${id}`);\n return null;\n }\n\n const data: ConversationWithMessages = await res.json();\n return transformMessages(data.messages);\n } catch (err) {\n console.warn(\"loadConversation error:\", err);\n return null;\n }\n }, [opts.apiUrl, opts.getHeaders, opts.getCredentials]);\n\n const deleteConversation = useCallback(async (id: string): Promise<boolean> => {\n try {\n const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}`, {\n method: \"DELETE\",\n headers: opts.getHeaders(),\n credentials: opts.getCredentials(),\n });\n\n if (!res.ok) {\n console.warn(`deleteConversation: HTTP ${res.status} for ${id}`);\n return false;\n }\n\n setConversations((prev) => prev.filter((c) => c.id !== id));\n setTotal((prev) => Math.max(0, prev - 1));\n\n if (selectedId === id) setSelectedId(null);\n\n return true;\n } catch (err) {\n console.warn(\"deleteConversation error:\", err);\n return false;\n }\n }, [opts.apiUrl, opts.getHeaders, opts.getCredentials, selectedId]);\n\n const starConversation = useCallback(async (id: string, starred: boolean): Promise<boolean> => {\n // Optimistic update\n setConversations((prev) =>\n prev.map((c) => (c.id === id ? { ...c, starred } : c)),\n );\n try {\n const res = await fetch(`${opts.apiUrl}/api/v1/conversations/${id}/star`, {\n method: \"PATCH\",\n headers: { ...opts.getHeaders(), \"Content-Type\": \"application/json\" },\n credentials: opts.getCredentials(),\n body: JSON.stringify({ starred }),\n });\n\n if (!res.ok) {\n console.warn(`starConversation: HTTP ${res.status} for ${id}`);\n // Rollback\n setConversations((prev) =>\n prev.map((c) => (c.id === id ? { ...c, starred: !starred } : c)),\n );\n return false;\n }\n\n return true;\n } catch (err) {\n console.warn(\"starConversation error:\", err);\n // Rollback\n setConversations((prev) =>\n prev.map((c) => (c.id === id ? { ...c, starred: !starred } : c)),\n );\n return false;\n }\n }, [opts.apiUrl, opts.getHeaders, opts.getCredentials]);\n\n const refresh = useCallback(async () => {\n await fetchList();\n }, [fetchList]);\n\n return {\n conversations,\n total,\n loading,\n available,\n selectedId,\n setSelectedId,\n fetchList,\n loadConversation,\n deleteConversation,\n starConversation,\n refresh,\n };\n}\n"]}