@terryavg/neptune-ai-chatbot 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,2869 @@
1
+ import {
2
+ chatClient,
3
+ configureChatClient
4
+ } from "./chunk-5VL3YPMQ.mjs";
5
+ import {
6
+ ToolExecutionIndicator,
7
+ ToolExecutionWidget
8
+ } from "./chunk-C2VGAYER.mjs";
9
+ import {
10
+ __spreadProps,
11
+ __spreadValues
12
+ } from "./chunk-FWCSY2DS.mjs";
13
+
14
+ // app/components/chat.tsx
15
+ import {
16
+ useState as useState4,
17
+ useEffect as useEffect4,
18
+ useRef as useRef2,
19
+ useLayoutEffect,
20
+ useCallback as useCallback2
21
+ } from "react";
22
+ import {
23
+ Trash2,
24
+ Edit,
25
+ Moon,
26
+ Sun,
27
+ MessageCirclePlus,
28
+ PanelRightClose,
29
+ PanelRightOpen,
30
+ MoreHorizontal
31
+ } from "lucide-react";
32
+
33
+ // app/components/chat-content.tsx
34
+ import {
35
+ useRef,
36
+ useEffect as useEffect2,
37
+ useState as useState2,
38
+ useMemo as useMemo2,
39
+ useCallback,
40
+ lazy,
41
+ Suspense
42
+ } from "react";
43
+ import { X as X2, ArrowDownCircle } from "lucide-react";
44
+
45
+ // app/components/analytics-panel.tsx
46
+ import { useState, useMemo, useEffect } from "react";
47
+ import {
48
+ ChevronDown,
49
+ ChevronRight,
50
+ Filter,
51
+ Download,
52
+ RotateCcw,
53
+ ArrowUp,
54
+ ArrowDown,
55
+ X,
56
+ ArrowUpDown,
57
+ Folder,
58
+ FolderOpen,
59
+ FileText,
60
+ BarChart3,
61
+ Eye,
62
+ EyeOff
63
+ } from "lucide-react";
64
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
65
+ function AnalyticsPanel({
66
+ name,
67
+ csvData,
68
+ theme
69
+ }) {
70
+ const { headers, data } = useMemo(() => {
71
+ var _a;
72
+ const lines = csvData.trim().split("\n");
73
+ const headers2 = ((_a = lines[0]) == null ? void 0 : _a.split(";").map((h) => h.trim())) || [];
74
+ const rows = lines.slice(1).map((line) => {
75
+ const values = line.split(";").map((v) => v.trim());
76
+ const row = {};
77
+ headers2.forEach((header, index) => {
78
+ row[header] = values[index] || "";
79
+ });
80
+ return row;
81
+ });
82
+ return { headers: headers2, data: rows };
83
+ }, [csvData]);
84
+ const [pivotConfig, setPivotConfig] = useState({
85
+ rows: [],
86
+ columns: [],
87
+ values: [],
88
+ aggregation: "count"
89
+ });
90
+ const [filters, setFilters] = useState({});
91
+ const [searchTerm, setSearchTerm] = useState("");
92
+ const [activeFilterDropdown, setActiveFilterDropdown] = useState(null);
93
+ const [sortConfig, setSortConfig] = useState(null);
94
+ const [expandedGroups, setExpandedGroups] = useState(
95
+ /* @__PURE__ */ new Set()
96
+ );
97
+ const [showGroupedView, setShowGroupedView] = useState(false);
98
+ const [setTreeNodes] = useState([]);
99
+ const [animatingNodes, setAnimatingNodes] = useState(
100
+ /* @__PURE__ */ new Set()
101
+ );
102
+ const isNumericValue = (value) => {
103
+ return typeof value === "number" || !isNaN(parseFloat(value)) && isFinite(value);
104
+ };
105
+ const formatNumericValue = (value) => {
106
+ if (typeof value === "number") {
107
+ return value.toLocaleString();
108
+ }
109
+ const parsed = parseFloat(value);
110
+ return !isNaN(parsed) ? parsed.toLocaleString() : value;
111
+ };
112
+ const shouldFormatAsNumber = (value, header) => {
113
+ const isValueColumn = pivotConfig.values.includes(header);
114
+ const isCountColumn = header === "_count";
115
+ return (isValueColumn || isCountColumn) && isNumericValue(value);
116
+ };
117
+ const getCellAlignment = (value, header, isFirstColumn) => {
118
+ if (isFirstColumn) return "text-left";
119
+ const isValueColumn = pivotConfig.values.includes(header);
120
+ const isCountColumn = header === "_count";
121
+ if (isValueColumn || isCountColumn) {
122
+ return "text-right";
123
+ }
124
+ return "text-left";
125
+ };
126
+ const getTreeIcon = (node, hasChildren) => {
127
+ if (!hasChildren) {
128
+ return /* @__PURE__ */ jsx(
129
+ FileText,
130
+ {
131
+ size: 14,
132
+ className: "text-blue-500 dark:text-blue-400"
133
+ }
134
+ );
135
+ }
136
+ if (node.isExpanded) {
137
+ return /* @__PURE__ */ jsx(
138
+ FolderOpen,
139
+ {
140
+ size: 14,
141
+ className: "text-emerald-600 dark:text-emerald-400"
142
+ }
143
+ );
144
+ } else {
145
+ return /* @__PURE__ */ jsx(
146
+ Folder,
147
+ {
148
+ size: 14,
149
+ className: "text-emerald-600 dark:text-emerald-400"
150
+ }
151
+ );
152
+ }
153
+ };
154
+ const getExpandIcon = (isExpanded, hasChildren) => {
155
+ if (!hasChildren) return null;
156
+ return isExpanded ? /* @__PURE__ */ jsx(
157
+ ChevronDown,
158
+ {
159
+ size: 14,
160
+ className: "text-gray-600 dark:text-gray-400 transition-transform duration-200"
161
+ }
162
+ ) : /* @__PURE__ */ jsx(
163
+ ChevronRight,
164
+ {
165
+ size: 14,
166
+ className: "text-gray-600 dark:text-gray-400 transition-transform duration-200"
167
+ }
168
+ );
169
+ };
170
+ const getLevelColor = (level) => {
171
+ const colors = [
172
+ "border-l-emerald-500 bg-emerald-50 dark:bg-emerald-900/10",
173
+ // Level 0
174
+ "border-l-blue-500 bg-blue-50 dark:bg-blue-900/10",
175
+ // Level 1
176
+ "border-l-purple-500 bg-purple-50 dark:bg-purple-900/10",
177
+ // Level 2
178
+ "border-l-orange-500 bg-orange-50 dark:bg-orange-900/10",
179
+ // Level 3
180
+ "border-l-pink-500 bg-pink-50 dark:bg-pink-900/10"
181
+ // Level 4+
182
+ ];
183
+ return colors[Math.min(level, colors.length - 1)];
184
+ };
185
+ const generateTreeId = (path, level) => {
186
+ return `tree-${level}-${path.join("-")}`;
187
+ };
188
+ const calculateAggregations = (rows, valueColumns, aggregation) => {
189
+ const result = {};
190
+ valueColumns.forEach((col) => {
191
+ const numericValues = rows.map((row) => {
192
+ const cleaned = String(row[col] || "").replace(
193
+ /[,\s]/g,
194
+ ""
195
+ );
196
+ const parsed = parseFloat(cleaned);
197
+ return !isNaN(parsed) ? parsed : 0;
198
+ }).filter((val) => !isNaN(val));
199
+ switch (aggregation) {
200
+ case "sum":
201
+ result[col] = numericValues.reduce(
202
+ (sum, val) => sum + val,
203
+ 0
204
+ );
205
+ break;
206
+ case "avg":
207
+ result[col] = numericValues.length > 0 ? numericValues.reduce((sum, val) => sum + val, 0) / numericValues.length : 0;
208
+ break;
209
+ case "min":
210
+ result[col] = numericValues.length > 0 ? Math.min(...numericValues) : 0;
211
+ break;
212
+ case "max":
213
+ result[col] = numericValues.length > 0 ? Math.max(...numericValues) : 0;
214
+ break;
215
+ default:
216
+ result[col] = rows.length;
217
+ }
218
+ });
219
+ return result;
220
+ };
221
+ const buildTreeStructure = (rows, groupColumns, currentPath = [], level = 0) => {
222
+ if (groupColumns.length === 0) {
223
+ return [
224
+ {
225
+ id: generateTreeId([...currentPath, "aggregated"], level),
226
+ level,
227
+ label: `${rows.length} records`,
228
+ value: rows.length,
229
+ count: rows.length,
230
+ aggregatedData: calculateAggregations(
231
+ rows,
232
+ pivotConfig.values,
233
+ pivotConfig.aggregation
234
+ ),
235
+ children: [],
236
+ isExpanded: false,
237
+ originalRows: rows,
238
+ _isTreeNode: true
239
+ }
240
+ ];
241
+ }
242
+ const [currentColumn, ...remainingColumns] = groupColumns;
243
+ const grouped = rows.reduce((acc, row) => {
244
+ const value = row[currentColumn] || "Unknown";
245
+ if (!acc[value]) acc[value] = [];
246
+ acc[value].push(row);
247
+ return acc;
248
+ }, {});
249
+ let treeNodes = Object.entries(grouped).map(([value, groupRows]) => {
250
+ const typedGroupRows = groupRows;
251
+ const nodePath = [...currentPath, value];
252
+ const nodeId = generateTreeId(nodePath, level);
253
+ return {
254
+ id: nodeId,
255
+ level,
256
+ label: value,
257
+ value,
258
+ count: typedGroupRows.length,
259
+ aggregatedData: calculateAggregations(
260
+ typedGroupRows,
261
+ pivotConfig.values,
262
+ pivotConfig.aggregation
263
+ ),
264
+ children: buildTreeStructure(
265
+ typedGroupRows,
266
+ remainingColumns,
267
+ nodePath,
268
+ level + 1
269
+ ),
270
+ isExpanded: expandedGroups.has(nodeId),
271
+ originalRows: typedGroupRows,
272
+ _isTreeNode: true
273
+ };
274
+ });
275
+ if (sortConfig) {
276
+ treeNodes = treeNodes.sort((a, b) => {
277
+ let valueA;
278
+ let valueB;
279
+ if (sortConfig.key === currentColumn) {
280
+ valueA = a.label;
281
+ valueB = b.label;
282
+ } else if (sortConfig.key === "_count") {
283
+ valueA = a.count;
284
+ valueB = b.count;
285
+ } else if (pivotConfig.values.includes(sortConfig.key)) {
286
+ valueA = a.aggregatedData[sortConfig.key] || 0;
287
+ valueB = b.aggregatedData[sortConfig.key] || 0;
288
+ } else {
289
+ valueA = a.label;
290
+ valueB = b.label;
291
+ }
292
+ return compareValues(valueA, valueB, sortConfig.direction);
293
+ });
294
+ } else {
295
+ treeNodes = treeNodes.sort(
296
+ (a, b) => a.label.localeCompare(b.label)
297
+ );
298
+ }
299
+ return treeNodes;
300
+ };
301
+ const compareValues = (valueA, valueB, direction) => {
302
+ const numA = parseFloat(String(valueA).replace(/[,\s]/g, ""));
303
+ const numB = parseFloat(String(valueB).replace(/[,\s]/g, ""));
304
+ let comparison = 0;
305
+ if (!isNaN(numA) && !isNaN(numB)) {
306
+ comparison = numA - numB;
307
+ } else {
308
+ comparison = String(valueA).localeCompare(String(valueB));
309
+ }
310
+ return direction === "desc" ? -comparison : comparison;
311
+ };
312
+ const flattenTreeToRows = (nodes) => {
313
+ const result = [];
314
+ nodes.forEach((node) => {
315
+ result.push(__spreadProps(__spreadValues({}, node), {
316
+ _isTreeNode: true,
317
+ _treeLevel: node.level
318
+ }));
319
+ if (node.isExpanded && node.children.length > 0) {
320
+ result.push(...flattenTreeToRows(node.children));
321
+ }
322
+ });
323
+ return result;
324
+ };
325
+ const pivotData = useMemo(() => {
326
+ if (!data.length || !headers.length)
327
+ return { rows: [], summary: null, filteredData: [], totals: null };
328
+ let filteredData = data.filter((row) => {
329
+ if (searchTerm) {
330
+ const searchLower = searchTerm.toLowerCase();
331
+ const matchesSearch = Object.values(row).some(
332
+ (value) => value.toLowerCase().includes(searchLower)
333
+ );
334
+ if (!matchesSearch) return false;
335
+ }
336
+ return Object.entries(filters).every(([column, allowedValues]) => {
337
+ return allowedValues.length === 0 || allowedValues.includes(row[column]);
338
+ });
339
+ });
340
+ const totals = pivotConfig.values.length > 0 ? calculateAggregations(
341
+ filteredData,
342
+ pivotConfig.values,
343
+ pivotConfig.aggregation
344
+ ) : null;
345
+ if (pivotConfig.rows.length === 0 && pivotConfig.columns.length === 0 && pivotConfig.values.length === 0) {
346
+ return {
347
+ rows: [],
348
+ summary: {
349
+ totalRows: 0,
350
+ originalRows: data.length,
351
+ totalColumns: headers.length
352
+ },
353
+ filteredData: [],
354
+ totals: null
355
+ };
356
+ }
357
+ const pivotRows = [];
358
+ const groups = filteredData.reduce((acc, row) => {
359
+ const rowKey = pivotConfig.rows.map((col) => row[col]).join(" | ") || "Total";
360
+ if (!acc[rowKey]) {
361
+ acc[rowKey] = [];
362
+ }
363
+ acc[rowKey].push(row);
364
+ return acc;
365
+ }, {});
366
+ const useTreeView = pivotConfig.rows.length > 0 && showGroupedView;
367
+ if (useTreeView) {
368
+ const treeStructure = buildTreeStructure(
369
+ filteredData,
370
+ pivotConfig.rows
371
+ );
372
+ const flattenedRows = flattenTreeToRows(treeStructure);
373
+ pivotRows.push(...flattenedRows);
374
+ } else if (pivotConfig.rows.length > 0) {
375
+ Object.entries(groups).forEach(([rowKey, groupData]) => {
376
+ const pivotRow = {
377
+ _rowKey: rowKey,
378
+ _count: groupData.length
379
+ };
380
+ pivotConfig.rows.forEach((rowCol, index) => {
381
+ pivotRow[rowCol] = rowKey.split(" | ")[index] || "";
382
+ });
383
+ pivotConfig.values.forEach((valueCol) => {
384
+ const rawValues = groupData.map((d) => d[valueCol]);
385
+ const numericValues = rawValues.map((val) => {
386
+ const cleaned = String(val).replace(/[,\s]/g, "");
387
+ const parsed = parseFloat(cleaned);
388
+ return !isNaN(parsed) ? parsed : 0;
389
+ });
390
+ const validNumericValues = numericValues.filter(
391
+ (val) => !isNaN(val) && val !== 0 || rawValues[numericValues.indexOf(val)] === "0"
392
+ );
393
+ switch (pivotConfig.aggregation) {
394
+ case "sum":
395
+ pivotRow[valueCol] = numericValues.reduce(
396
+ (sum, val) => sum + val,
397
+ 0
398
+ );
399
+ break;
400
+ case "avg":
401
+ pivotRow[valueCol] = validNumericValues.length > 0 ? validNumericValues.reduce(
402
+ (sum, val) => sum + val,
403
+ 0
404
+ ) / validNumericValues.length : 0;
405
+ break;
406
+ case "min":
407
+ pivotRow[valueCol] = validNumericValues.length > 0 ? Math.min(...validNumericValues) : 0;
408
+ break;
409
+ case "max":
410
+ pivotRow[valueCol] = validNumericValues.length > 0 ? Math.max(...validNumericValues) : 0;
411
+ break;
412
+ default:
413
+ pivotRow[valueCol] = groupData.length;
414
+ }
415
+ });
416
+ pivotRows.push(pivotRow);
417
+ });
418
+ }
419
+ if (sortConfig && useTreeView) {
420
+ } else if (sortConfig && !useTreeView) {
421
+ pivotRows.sort((a, b) => {
422
+ const aValue = a[sortConfig.key];
423
+ const bValue = b[sortConfig.key];
424
+ if (typeof aValue === "number" && typeof bValue === "number") {
425
+ return sortConfig.direction === "asc" ? aValue - bValue : bValue - aValue;
426
+ }
427
+ const aStr = String(aValue).toLowerCase();
428
+ const bStr = String(bValue).toLowerCase();
429
+ if (aStr < bStr) return sortConfig.direction === "asc" ? -1 : 1;
430
+ if (aStr > bStr) return sortConfig.direction === "asc" ? 1 : -1;
431
+ return 0;
432
+ });
433
+ }
434
+ return {
435
+ rows: pivotRows,
436
+ summary: {
437
+ totalRows: pivotRows.length,
438
+ totalGroups: Object.keys(groups).length,
439
+ originalRows: filteredData.length
440
+ },
441
+ filteredData,
442
+ totals
443
+ };
444
+ }, [
445
+ data,
446
+ headers,
447
+ pivotConfig,
448
+ filters,
449
+ searchTerm,
450
+ sortConfig,
451
+ expandedGroups,
452
+ showGroupedView
453
+ ]);
454
+ const getUniqueValues = (column) => {
455
+ return [...new Set(data.map((row) => row[column]))].filter(Boolean).sort();
456
+ };
457
+ const updatePivotConfig = (key, value) => {
458
+ setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), { [key]: value }));
459
+ };
460
+ const addToPivotConfig = (key, value) => {
461
+ setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), {
462
+ [key]: [...prev[key], value]
463
+ }));
464
+ };
465
+ const removeFromPivotConfig = (key, value) => {
466
+ setPivotConfig((prev) => __spreadProps(__spreadValues({}, prev), {
467
+ [key]: prev[key].filter((item) => item !== value)
468
+ }));
469
+ };
470
+ const moveColumnUp = (key, index) => {
471
+ if (index === 0) return;
472
+ setPivotConfig((prev) => {
473
+ const newArray = [...prev[key]];
474
+ [newArray[index - 1], newArray[index]] = [
475
+ newArray[index],
476
+ newArray[index - 1]
477
+ ];
478
+ return __spreadProps(__spreadValues({}, prev), { [key]: newArray });
479
+ });
480
+ };
481
+ const moveColumnDown = (key, index) => {
482
+ setPivotConfig((prev) => {
483
+ if (index >= prev[key].length - 1) return prev;
484
+ const newArray = [...prev[key]];
485
+ [newArray[index], newArray[index + 1]] = [
486
+ newArray[index + 1],
487
+ newArray[index]
488
+ ];
489
+ return __spreadProps(__spreadValues({}, prev), { [key]: newArray });
490
+ });
491
+ };
492
+ const toggleColumnFilter = (column, value) => {
493
+ setFilters((prev) => {
494
+ const currentFilters = prev[column] || [];
495
+ const newFilters = currentFilters.includes(value) ? currentFilters.filter((v) => v !== value) : [...currentFilters, value];
496
+ return __spreadProps(__spreadValues({}, prev), { [column]: newFilters });
497
+ });
498
+ };
499
+ const clearColumnFilter = (column) => {
500
+ setFilters((prev) => {
501
+ const newFilters = __spreadValues({}, prev);
502
+ delete newFilters[column];
503
+ return newFilters;
504
+ });
505
+ };
506
+ const handleSort = (columnKey) => {
507
+ setSortConfig((prev) => {
508
+ if (prev && prev.key === columnKey) {
509
+ if (prev.direction === "asc") {
510
+ return { key: columnKey, direction: "desc" };
511
+ } else {
512
+ return null;
513
+ }
514
+ } else {
515
+ return { key: columnKey, direction: "asc" };
516
+ }
517
+ });
518
+ };
519
+ const toggleTreeNodeExpansion = (nodeId) => {
520
+ setAnimatingNodes((prev) => new Set(prev).add(nodeId));
521
+ setExpandedGroups((prev) => {
522
+ const newSet = new Set(prev);
523
+ if (newSet.has(nodeId)) {
524
+ newSet.delete(nodeId);
525
+ } else {
526
+ newSet.add(nodeId);
527
+ }
528
+ return newSet;
529
+ });
530
+ setTimeout(() => {
531
+ setAnimatingNodes((prev) => {
532
+ const newSet = new Set(prev);
533
+ newSet.delete(nodeId);
534
+ return newSet;
535
+ });
536
+ }, 200);
537
+ };
538
+ const exportToCsv = () => {
539
+ const csvContent = [
540
+ headers.join(";"),
541
+ ...pivotData.rows.map(
542
+ (row) => headers.map((header) => row[header] || "").join(";")
543
+ )
544
+ ].join("\n");
545
+ const blob = new Blob([csvContent], { type: "text/csv" });
546
+ const url = URL.createObjectURL(blob);
547
+ const link = document.createElement("a");
548
+ link.href = url;
549
+ link.download = `${name.replace(/[^a-zA-Z0-9]/g, "_")}_export.csv`;
550
+ link.click();
551
+ URL.revokeObjectURL(url);
552
+ };
553
+ const resetConfig = () => {
554
+ setPivotConfig({
555
+ rows: [],
556
+ columns: [],
557
+ values: [],
558
+ aggregation: "count"
559
+ });
560
+ setFilters({});
561
+ setSearchTerm("");
562
+ setActiveFilterDropdown(null);
563
+ setSortConfig(null);
564
+ setExpandedGroups(/* @__PURE__ */ new Set());
565
+ setShowGroupedView(false);
566
+ setAnimatingNodes(/* @__PURE__ */ new Set());
567
+ };
568
+ useEffect(() => {
569
+ const handleClickOutside = (event) => {
570
+ if (activeFilterDropdown && !event.target.closest(".relative")) {
571
+ setActiveFilterDropdown(null);
572
+ }
573
+ };
574
+ if (activeFilterDropdown) {
575
+ document.addEventListener("mousedown", handleClickOutside);
576
+ return () => document.removeEventListener("mousedown", handleClickOutside);
577
+ }
578
+ }, [activeFilterDropdown]);
579
+ const [isFilterPanelVisible, setIsFilterPanelVisible] = useState(true);
580
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-[calc(100%-70px)] bg-white dark:bg-gray-900 min-h-0", children: [
581
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [
582
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
583
+ /* @__PURE__ */ jsx("div", { className: "flex items-center space-x-4", children: pivotData.summary && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4 text-xs text-gray-600 dark:text-gray-400", children: [
584
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
585
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Rows:" }),
586
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: pivotData.summary.totalRows.toLocaleString() })
587
+ ] }),
588
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
589
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Columns:" }),
590
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: (pivotData.summary.totalColumns || headers.length).toLocaleString() })
591
+ ] }),
592
+ pivotData.summary.originalRows && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
593
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Filtered:" }),
594
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: pivotData.summary.originalRows.toLocaleString() })
595
+ ] })
596
+ ] }) }),
597
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
598
+ /* @__PURE__ */ jsxs(
599
+ "button",
600
+ {
601
+ onClick: exportToCsv,
602
+ className: "flex items-center px-3 py-1.5 bg-emerald-600 text-white rounded text-xs hover:bg-emerald-700 transition-colors",
603
+ title: "Export to CSV",
604
+ children: [
605
+ /* @__PURE__ */ jsx(Download, { size: 14, className: "mr-1" }),
606
+ "Export"
607
+ ]
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsxs(
611
+ "button",
612
+ {
613
+ onClick: resetConfig,
614
+ className: "flex items-center px-3 py-1.5 border border-gray-300 dark:border-gray-600 rounded text-xs hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors text-gray-700 dark:text-gray-300",
615
+ title: "Reset Configuration",
616
+ children: [
617
+ /* @__PURE__ */ jsx(RotateCcw, { size: 14, className: "mr-1" }),
618
+ "Reset"
619
+ ]
620
+ }
621
+ ),
622
+ /* @__PURE__ */ jsxs(
623
+ "button",
624
+ {
625
+ onClick: () => setIsFilterPanelVisible(
626
+ !isFilterPanelVisible
627
+ ),
628
+ className: "flex items-center px-3 py-1.5 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded text-xs hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors",
629
+ title: isFilterPanelVisible ? "Hide Filters" : "Show Filters",
630
+ children: [
631
+ isFilterPanelVisible ? /* @__PURE__ */ jsx(EyeOff, { size: 14, className: "mr-1" }) : /* @__PURE__ */ jsx(Eye, { size: 14, className: "mr-1" }),
632
+ isFilterPanelVisible ? "Hide" : "Show",
633
+ " Filters"
634
+ ]
635
+ }
636
+ )
637
+ ] })
638
+ ] }) }),
639
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col overflow-hidden bg-white dark:bg-gray-900", children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto min-w-0", children: pivotData.rows.length > 0 ? /* @__PURE__ */ jsx("div", { className: "", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm border-collapse bg-white dark:bg-gray-900 analytics-table-container", children: [
640
+ /* @__PURE__ */ jsx("thead", { className: "bg-gray-100 dark:bg-gray-900 sticky top-0", children: /* @__PURE__ */ jsx("tr", { children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
641
+ ...pivotConfig.rows,
642
+ ...pivotConfig.values,
643
+ ...showGroupedView ? [] : ["_count"]
644
+ ] : headers).map((header, index) => /* @__PURE__ */ jsx(
645
+ "th",
646
+ {
647
+ className: "border-0 border-gray-300 dark:border-gray-600 px-3 py-2 text-left font-medium text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-700 relative",
648
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
649
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: header === "_count" ? "Count" : header }),
650
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
651
+ /* @__PURE__ */ jsx(
652
+ "button",
653
+ {
654
+ onClick: () => handleSort(
655
+ header
656
+ ),
657
+ className: "p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors cursor-pointer",
658
+ title: "Sort column",
659
+ children: (sortConfig == null ? void 0 : sortConfig.key) === header ? sortConfig.direction === "asc" ? /* @__PURE__ */ jsx(
660
+ ArrowUp,
661
+ {
662
+ size: 14
663
+ }
664
+ ) : /* @__PURE__ */ jsx(
665
+ ArrowDown,
666
+ {
667
+ size: 14
668
+ }
669
+ ) : /* @__PURE__ */ jsx(
670
+ ArrowUpDown,
671
+ {
672
+ size: 14,
673
+ className: "opacity-50"
674
+ }
675
+ )
676
+ }
677
+ ),
678
+ header !== "_count" && header !== "_rowKey" && /* @__PURE__ */ jsx(
679
+ "button",
680
+ {
681
+ onClick: () => setActiveFilterDropdown(
682
+ activeFilterDropdown === header ? null : header
683
+ ),
684
+ className: `p-1 rounded cursor-pointer ${(filters[header] || []).length > 0 ? "text-emerald-600 bg-emerald-100 dark:bg-emerald-900/30" : "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
685
+ title: "Filter column",
686
+ children: /* @__PURE__ */ jsx(
687
+ Filter,
688
+ {
689
+ size: 14
690
+ }
691
+ )
692
+ }
693
+ )
694
+ ] }),
695
+ activeFilterDropdown === header && header !== "_count" && header !== "_rowKey" && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-8 z-50 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg min-w-96 max-w-96 min-h-96 max-h-96 overflow-auto", children: [
696
+ /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
697
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-gray-700 dark:text-gray-300", children: [
698
+ "Filter",
699
+ " ",
700
+ header
701
+ ] }),
702
+ /* @__PURE__ */ jsx(
703
+ "button",
704
+ {
705
+ onClick: () => clearColumnFilter(
706
+ header
707
+ ),
708
+ className: "text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",
709
+ children: "Clear"
710
+ }
711
+ )
712
+ ] }) }),
713
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto", children: getUniqueValues(
714
+ header
715
+ ).map(
716
+ (value) => /* @__PURE__ */ jsxs(
717
+ "label",
718
+ {
719
+ className: "flex items-center px-3 py-1 hover:bg-gray-100 dark:hover:bg-gray-600 cursor-pointer",
720
+ children: [
721
+ /* @__PURE__ */ jsx(
722
+ "input",
723
+ {
724
+ type: "checkbox",
725
+ checked: (filters[header] || []).includes(
726
+ value
727
+ ),
728
+ onChange: () => toggleColumnFilter(
729
+ header,
730
+ value
731
+ ),
732
+ className: "mr-2 text-emerald-600 focus:ring-emerald-500"
733
+ }
734
+ ),
735
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300 truncate", children: value })
736
+ ]
737
+ },
738
+ value
739
+ )
740
+ ) })
741
+ ] })
742
+ ] })
743
+ },
744
+ header
745
+ )) }) }),
746
+ /* @__PURE__ */ jsx("tbody", { className: "bg-white dark:bg-gray-900", children: pivotData.rows.slice(0, 1e6).map((row, index) => {
747
+ if (row._isTreeNode && showGroupedView) {
748
+ const indentLevel = row._treeLevel || row.level || 0;
749
+ const isLeafNode = row.children && row.children.length === 0;
750
+ const hasChildren = row.children && row.children.length > 0;
751
+ const isAnimating = animatingNodes.has(
752
+ row.id
753
+ );
754
+ if (isLeafNode) {
755
+ return /* @__PURE__ */ jsx(
756
+ "tr",
757
+ {
758
+ className: `transition-all duration-200 bg-white dark:bg-gray-900 hover:bg-blue-50 dark:hover:bg-gray-700 border-l-2 border-l-transparent hover:border-l-blue-400 ${isAnimating ? "animate-pulse" : ""}`,
759
+ children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
760
+ ...pivotConfig.rows,
761
+ ...pivotConfig.values,
762
+ ...showGroupedView ? [] : [
763
+ "_count"
764
+ ]
765
+ ] : headers).map(
766
+ (header, headerIndex) => {
767
+ var _a, _b;
768
+ const cellIndent = headerIndex === 0 ? indentLevel * 24 + 32 : 0;
769
+ const isFirstColumn = headerIndex === 0;
770
+ let cellValue;
771
+ if (header === "_count") {
772
+ cellValue = row.count;
773
+ } else if (((_a = row.aggregatedData) == null ? void 0 : _a[header]) !== void 0) {
774
+ cellValue = row.aggregatedData[header];
775
+ } else if (((_b = row.value) == null ? void 0 : _b[header]) !== void 0) {
776
+ cellValue = row.value[header];
777
+ } else {
778
+ cellValue = isFirstColumn ? row.label : "";
779
+ }
780
+ const alignmentClass = getCellAlignment(
781
+ cellValue,
782
+ header,
783
+ isFirstColumn
784
+ );
785
+ return /* @__PURE__ */ jsxs(
786
+ "td",
787
+ {
788
+ className: `border-0 border-b border-gray-200 dark:border-gray-700 px-3 py-2 text-gray-900 dark:text-gray-100 ${alignmentClass}`,
789
+ style: headerIndex === 0 ? {
790
+ paddingLeft: `${cellIndent}px`
791
+ } : {},
792
+ children: [
793
+ headerIndex === 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
794
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
795
+ Array.from(
796
+ {
797
+ length: indentLevel
798
+ }
799
+ ).map(
800
+ (_, levelIndex) => /* @__PURE__ */ jsx(
801
+ "div",
802
+ {
803
+ className: "w-6 flex justify-center",
804
+ children: /* @__PURE__ */ jsx("div", { className: "w-px h-full bg-gray-300 dark:bg-gray-600" })
805
+ },
806
+ levelIndex
807
+ )
808
+ ),
809
+ /* @__PURE__ */ jsx("div", { className: "w-6 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-3 h-px bg-gray-300 dark:bg-gray-600" }) })
810
+ ] }),
811
+ getTreeIcon(
812
+ row,
813
+ hasChildren
814
+ ),
815
+ /* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: shouldFormatAsNumber(
816
+ cellValue,
817
+ header
818
+ ) ? formatNumericValue(
819
+ cellValue
820
+ ) : cellValue })
821
+ ] }),
822
+ headerIndex !== 0 && /* @__PURE__ */ jsx("span", { children: shouldFormatAsNumber(
823
+ cellValue,
824
+ header
825
+ ) ? formatNumericValue(
826
+ cellValue
827
+ ) : cellValue })
828
+ ]
829
+ },
830
+ header
831
+ );
832
+ }
833
+ )
834
+ },
835
+ row.id || index
836
+ );
837
+ }
838
+ return /* @__PURE__ */ jsx(
839
+ "tr",
840
+ {
841
+ className: `transition-all duration-200 font-medium ${getLevelColor(
842
+ indentLevel
843
+ )} hover:bg-opacity-75 border-l-4 ${isAnimating ? "animate-pulse" : ""}`,
844
+ children: /* @__PURE__ */ jsx(
845
+ "td",
846
+ {
847
+ colSpan: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
848
+ ...pivotConfig.rows,
849
+ ...pivotConfig.values,
850
+ ...showGroupedView ? [] : [
851
+ "_count"
852
+ ]
853
+ ] : headers).length,
854
+ className: "border-0 border-b border-gray-200 dark:border-gray-700 px-3 py-3 text-gray-900 dark:text-gray-100",
855
+ children: /* @__PURE__ */ jsxs(
856
+ "div",
857
+ {
858
+ className: "flex items-center space-x-3 cursor-pointer group",
859
+ style: {
860
+ paddingLeft: `${indentLevel * 24}px`
861
+ },
862
+ onClick: () => toggleTreeNodeExpansion(
863
+ row.id
864
+ ),
865
+ children: [
866
+ indentLevel > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center", children: Array.from(
867
+ {
868
+ length: indentLevel
869
+ }
870
+ ).map(
871
+ (_, levelIndex) => /* @__PURE__ */ jsx(
872
+ "div",
873
+ {
874
+ className: "w-6 flex justify-center",
875
+ children: /* @__PURE__ */ jsx("div", { className: "w-px h-full bg-gray-300 dark:bg-gray-600" })
876
+ },
877
+ levelIndex
878
+ )
879
+ ) }),
880
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-6 h-6 rounded-full bg-white dark:bg-gray-700 shadow-sm border border-gray-300 dark:border-gray-600 group-hover:bg-gray-50 dark:group-hover:bg-gray-600 transition-colors", children: getExpandIcon(
881
+ row.isExpanded,
882
+ hasChildren
883
+ ) }),
884
+ getTreeIcon(
885
+ row,
886
+ hasChildren
887
+ ),
888
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 flex-1", children: [
889
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200", children: row.label }),
890
+ /* @__PURE__ */ jsxs("span", { className: "text-sm px-2 py-1 bg-gray-200 dark:bg-gray-700 rounded-full text-gray-600 dark:text-gray-400", children: [
891
+ row.count,
892
+ " ",
893
+ "records"
894
+ ] })
895
+ ] }),
896
+ pivotConfig.values.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4 text-sm text-gray-600 dark:text-gray-400", children: [
897
+ pivotConfig.values.slice(
898
+ 0,
899
+ 3
900
+ ).map(
901
+ (col) => /* @__PURE__ */ jsxs(
902
+ "div",
903
+ {
904
+ className: "flex items-center space-x-1",
905
+ children: [
906
+ /* @__PURE__ */ jsx(
907
+ BarChart3,
908
+ {
909
+ size: 12,
910
+ className: "text-gray-500"
911
+ }
912
+ ),
913
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
914
+ col,
915
+ ":"
916
+ ] }),
917
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-gray-800 dark:text-gray-200 font-mono", children: shouldFormatAsNumber(
918
+ row.aggregatedData[col],
919
+ col
920
+ ) ? formatNumericValue(
921
+ row.aggregatedData[col]
922
+ ) : row.aggregatedData[col] })
923
+ ]
924
+ },
925
+ col
926
+ )
927
+ ),
928
+ pivotConfig.values.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
929
+ "+",
930
+ pivotConfig.values.length - 3,
931
+ " ",
932
+ "more"
933
+ ] })
934
+ ] })
935
+ ]
936
+ }
937
+ )
938
+ }
939
+ )
940
+ },
941
+ row.id
942
+ );
943
+ }
944
+ return /* @__PURE__ */ jsx(
945
+ "tr",
946
+ {
947
+ className: "bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-700",
948
+ children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
949
+ ...pivotConfig.rows,
950
+ ...pivotConfig.values,
951
+ ...showGroupedView ? [] : [
952
+ "_count"
953
+ ]
954
+ ] : headers).map(
955
+ (header, headerIndex) => {
956
+ const cellValue = header === "_count" ? row._count || "" : row[header] || "";
957
+ const isFirstColumn = headerIndex === 0;
958
+ const alignmentClass = getCellAlignment(
959
+ cellValue,
960
+ header,
961
+ isFirstColumn
962
+ );
963
+ return /* @__PURE__ */ jsx(
964
+ "td",
965
+ {
966
+ className: `border border-gray-300 dark:border-gray-600 px-3 py-2 text-gray-900 dark:text-gray-100 ${alignmentClass}`,
967
+ children: shouldFormatAsNumber(
968
+ cellValue,
969
+ header
970
+ ) ? formatNumericValue(
971
+ cellValue
972
+ ) : cellValue
973
+ },
974
+ header
975
+ );
976
+ }
977
+ )
978
+ },
979
+ row._rowKey || index
980
+ );
981
+ }) }),
982
+ pivotData.totals && pivotConfig.values.length > 0 && /* @__PURE__ */ jsx("tfoot", { className: "bg-gray-100 dark:bg-gray-800 border-t-2 border-gray-400 dark:border-gray-600", children: /* @__PURE__ */ jsx("tr", { children: (pivotConfig.rows.length > 0 || pivotConfig.columns.length > 0 || pivotConfig.values.length > 0 ? [
983
+ ...pivotConfig.rows,
984
+ ...pivotConfig.values,
985
+ ...showGroupedView ? [] : ["_count"]
986
+ ] : headers).map(
987
+ (header, headerIndex) => {
988
+ const isFirstColumn = headerIndex === 0;
989
+ let cellContent = "";
990
+ let alignmentClass = getCellAlignment(
991
+ "",
992
+ header,
993
+ isFirstColumn
994
+ );
995
+ if (isFirstColumn) {
996
+ cellContent = `Total (${pivotConfig.aggregation})`;
997
+ alignmentClass = "text-left";
998
+ } else if (pivotConfig.values.includes(
999
+ header
1000
+ ) && pivotData.totals && pivotData.totals[header] !== void 0) {
1001
+ const totalValue = pivotData.totals[header];
1002
+ cellContent = shouldFormatAsNumber(
1003
+ totalValue,
1004
+ header
1005
+ ) ? formatNumericValue(
1006
+ totalValue
1007
+ ) : String(
1008
+ totalValue
1009
+ );
1010
+ alignmentClass = "text-right";
1011
+ } else if (header === "_count" && !showGroupedView) {
1012
+ cellContent = pivotData.filteredData.length.toLocaleString();
1013
+ alignmentClass = "text-right";
1014
+ }
1015
+ return /* @__PURE__ */ jsx(
1016
+ "td",
1017
+ {
1018
+ className: `border-0 border-gray-300 dark:border-gray-600 px-3 py-3 font-semibold text-gray-900 dark:text-gray-100 ${alignmentClass}`,
1019
+ children: cellContent
1020
+ },
1021
+ header
1022
+ );
1023
+ }
1024
+ ) }) })
1025
+ ] }) }) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-64 text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx("div", { className: "text-center max-w-md", children: pivotConfig.rows.length === 0 && pivotConfig.columns.length === 0 && pivotConfig.values.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1026
+ /* @__PURE__ */ jsx("div", { className: "w-16 h-16 mx-auto mt-16 mb-4 rounded-full bg-emerald-100 dark:bg-emerald-900/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
1027
+ "svg",
1028
+ {
1029
+ className: "w-8 h-8 text-emerald-600 dark:text-emerald-400",
1030
+ fill: "none",
1031
+ stroke: "currentColor",
1032
+ viewBox: "0 0 24 24",
1033
+ children: /* @__PURE__ */ jsx(
1034
+ "path",
1035
+ {
1036
+ strokeLinecap: "round",
1037
+ strokeLinejoin: "round",
1038
+ strokeWidth: 2,
1039
+ d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
1040
+ }
1041
+ )
1042
+ }
1043
+ ) }),
1044
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-medium mb-2", children: "Configure Your Analytics" }),
1045
+ /* @__PURE__ */ jsxs("p", { className: "text-sm mb-4", children: [
1046
+ "Dataset loaded with",
1047
+ " ",
1048
+ data.length.toLocaleString(),
1049
+ " ",
1050
+ "records and ",
1051
+ headers.length,
1052
+ " ",
1053
+ "columns.",
1054
+ /* @__PURE__ */ jsx("br", {}),
1055
+ "Select columns in the filter panel to start analyzing your data."
1056
+ ] }),
1057
+ /* @__PURE__ */ jsxs("div", { className: "text-left bg-gray-50 dark:bg-gray-900 rounded-lg p-4 text-xs", children: [
1058
+ /* @__PURE__ */ jsx("p", { className: "font-medium mb-2", children: "Quick Start:" }),
1059
+ /* @__PURE__ */ jsxs("ol", { className: "list-decimal list-inside space-y-1", children: [
1060
+ /* @__PURE__ */ jsxs("li", { children: [
1061
+ /* @__PURE__ */ jsx("strong", { children: "Group by Rows:" }),
1062
+ " ",
1063
+ "Select dimensions to group your data"
1064
+ ] }),
1065
+ /* @__PURE__ */ jsxs("li", { children: [
1066
+ /* @__PURE__ */ jsx("strong", { children: "Value Columns:" }),
1067
+ " ",
1068
+ "Choose metrics to analyze"
1069
+ ] }),
1070
+ /* @__PURE__ */ jsxs("li", { children: [
1071
+ /* @__PURE__ */ jsx("strong", { children: "Aggregation:" }),
1072
+ " ",
1073
+ "Pick how to summarize values (sum, average, etc.)"
1074
+ ] })
1075
+ ] })
1076
+ ] })
1077
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1078
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-medium mb-2", children: "No data to display" }),
1079
+ /* @__PURE__ */ jsx("p", { className: "text-sm", children: "Try adjusting your filters or configuration" })
1080
+ ] }) }) }) }) })
1081
+ ] }),
1082
+ isFilterPanelVisible && /* @__PURE__ */ jsx("div", { className: "w-80 min-w-80 border-l border-gray-200 dark:border-gray-700 bg-white flex flex-col flex-shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50", children: [
1083
+ /* @__PURE__ */ jsxs("div", { children: [
1084
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Search Data" }),
1085
+ /* @__PURE__ */ jsx(
1086
+ "input",
1087
+ {
1088
+ type: "text",
1089
+ value: searchTerm,
1090
+ onChange: (e) => setSearchTerm(e.target.value),
1091
+ placeholder: "Search across all columns...",
1092
+ className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 placeholder-gray-500 dark:placeholder-gray-400"
1093
+ }
1094
+ )
1095
+ ] }),
1096
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1097
+ /* @__PURE__ */ jsxs("div", { children: [
1098
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Group by Rows" }),
1099
+ /* @__PURE__ */ jsxs(
1100
+ "select",
1101
+ {
1102
+ value: "",
1103
+ onChange: (e) => e.target.value && addToPivotConfig("rows", e.target.value),
1104
+ className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
1105
+ children: [
1106
+ /* @__PURE__ */ jsx("option", { value: "", children: "Add column..." }),
1107
+ headers.filter(
1108
+ (h) => !pivotConfig.rows.includes(h)
1109
+ ).sort().map((header) => /* @__PURE__ */ jsx("option", { value: header, children: header }, header))
1110
+ ]
1111
+ }
1112
+ ),
1113
+ pivotConfig.rows.map((row, index) => /* @__PURE__ */ jsxs(
1114
+ "div",
1115
+ {
1116
+ className: "flex items-center justify-between mt-1 px-2 py-1 bg-emerald-100 dark:bg-emerald-900/30 rounded text-sm",
1117
+ children: [
1118
+ /* @__PURE__ */ jsx("span", { className: "text-emerald-800 dark:text-emerald-200 flex-1", children: row }),
1119
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
1120
+ /* @__PURE__ */ jsx(
1121
+ "button",
1122
+ {
1123
+ onClick: () => moveColumnUp("rows", index),
1124
+ disabled: index === 0,
1125
+ className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200 disabled:opacity-50 disabled:cursor-not-allowed",
1126
+ children: /* @__PURE__ */ jsx(ArrowUp, { size: 14 })
1127
+ }
1128
+ ),
1129
+ /* @__PURE__ */ jsx(
1130
+ "button",
1131
+ {
1132
+ onClick: () => moveColumnDown(
1133
+ "rows",
1134
+ index
1135
+ ),
1136
+ disabled: index === pivotConfig.rows.length - 1,
1137
+ className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200 disabled:opacity-50 disabled:cursor-not-allowed",
1138
+ children: /* @__PURE__ */ jsx(ArrowDown, { size: 14 })
1139
+ }
1140
+ ),
1141
+ /* @__PURE__ */ jsx(
1142
+ "button",
1143
+ {
1144
+ onClick: () => removeFromPivotConfig(
1145
+ "rows",
1146
+ row
1147
+ ),
1148
+ className: "text-emerald-600 hover:text-emerald-800 dark:text-emerald-400 dark:hover:text-emerald-200",
1149
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
1150
+ }
1151
+ )
1152
+ ] })
1153
+ ]
1154
+ },
1155
+ row
1156
+ ))
1157
+ ] }),
1158
+ /* @__PURE__ */ jsxs("div", { children: [
1159
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Value Columns" }),
1160
+ /* @__PURE__ */ jsxs(
1161
+ "select",
1162
+ {
1163
+ value: "",
1164
+ onChange: (e) => e.target.value && addToPivotConfig(
1165
+ "values",
1166
+ e.target.value
1167
+ ),
1168
+ className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
1169
+ children: [
1170
+ /* @__PURE__ */ jsx("option", { value: "", children: "Add column..." }),
1171
+ headers.filter(
1172
+ (h) => !pivotConfig.values.includes(h)
1173
+ ).sort().map((header) => /* @__PURE__ */ jsx("option", { value: header, children: header }, header))
1174
+ ]
1175
+ }
1176
+ ),
1177
+ pivotConfig.values.map((value, index) => /* @__PURE__ */ jsxs(
1178
+ "div",
1179
+ {
1180
+ className: "flex items-center justify-between mt-1 px-2 py-1 bg-blue-100 dark:bg-blue-900/30 rounded text-sm",
1181
+ children: [
1182
+ /* @__PURE__ */ jsx("span", { className: "text-blue-800 dark:text-blue-200 flex-1", children: value }),
1183
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
1184
+ /* @__PURE__ */ jsx(
1185
+ "button",
1186
+ {
1187
+ onClick: () => moveColumnUp(
1188
+ "values",
1189
+ index
1190
+ ),
1191
+ disabled: index === 0,
1192
+ className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 disabled:opacity-50 disabled:cursor-not-allowed",
1193
+ children: /* @__PURE__ */ jsx(ArrowUp, { size: 14 })
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsx(
1197
+ "button",
1198
+ {
1199
+ onClick: () => moveColumnDown(
1200
+ "values",
1201
+ index
1202
+ ),
1203
+ disabled: index === pivotConfig.values.length - 1,
1204
+ className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 disabled:opacity-50 disabled:cursor-not-allowed",
1205
+ children: /* @__PURE__ */ jsx(ArrowDown, { size: 14 })
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ jsx(
1209
+ "button",
1210
+ {
1211
+ onClick: () => removeFromPivotConfig(
1212
+ "values",
1213
+ value
1214
+ ),
1215
+ className: "text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200",
1216
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
1217
+ }
1218
+ )
1219
+ ] })
1220
+ ]
1221
+ },
1222
+ value
1223
+ ))
1224
+ ] }),
1225
+ /* @__PURE__ */ jsxs("div", { children: [
1226
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Aggregation" }),
1227
+ /* @__PURE__ */ jsxs(
1228
+ "select",
1229
+ {
1230
+ value: pivotConfig.aggregation,
1231
+ onChange: (e) => updatePivotConfig(
1232
+ "aggregation",
1233
+ e.target.value
1234
+ ),
1235
+ className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
1236
+ children: [
1237
+ /* @__PURE__ */ jsx("option", { value: "count", children: "Count" }),
1238
+ /* @__PURE__ */ jsx("option", { value: "sum", children: "Sum" }),
1239
+ /* @__PURE__ */ jsx("option", { value: "avg", children: "Average" }),
1240
+ /* @__PURE__ */ jsx("option", { value: "min", children: "Minimum" }),
1241
+ /* @__PURE__ */ jsx("option", { value: "max", children: "Maximum" })
1242
+ ]
1243
+ }
1244
+ )
1245
+ ] }),
1246
+ /* @__PURE__ */ jsxs("div", { children: [
1247
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300", children: "Display Mode" }),
1248
+ /* @__PURE__ */ jsxs(
1249
+ "select",
1250
+ {
1251
+ value: showGroupedView ? "grouped" : "aggregated",
1252
+ onChange: (e) => setShowGroupedView(
1253
+ e.target.value === "grouped"
1254
+ ),
1255
+ className: "w-full text-sm px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100",
1256
+ children: [
1257
+ /* @__PURE__ */ jsx("option", { value: "aggregated", children: "Aggregated View" }),
1258
+ /* @__PURE__ */ jsx("option", { value: "grouped", children: "Grouped View" })
1259
+ ]
1260
+ }
1261
+ )
1262
+ ] })
1263
+ ] })
1264
+ ] }) })
1265
+ ] });
1266
+ }
1267
+
1268
+ // app/components/chat-content.tsx
1269
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1270
+ var ChatInput = lazy(() => import("./chat-input-L5LIF4NP.mjs"));
1271
+ var DynamicChatMessage = lazy(() => import("./chat-message-FTACCRMO.mjs"));
1272
+ var SCROLL_BOTTOM_THRESHOLD = 50;
1273
+ function ChatContent({
1274
+ conversation,
1275
+ agentId,
1276
+ debug = false,
1277
+ onConversationUpdate,
1278
+ theme,
1279
+ onAppStateChange,
1280
+ onSidebarToggle,
1281
+ isReadOnly = false,
1282
+ onThreadCreated
1283
+ }) {
1284
+ const messagesEndRef = useRef(null);
1285
+ const scrollContainerRef = useRef(null);
1286
+ const [messages, setMessages] = useState2(
1287
+ conversation.messages || []
1288
+ );
1289
+ const [isStreaming, setIsStreaming] = useState2(false);
1290
+ const [streamingMessage, setStreamingMessage] = useState2(
1291
+ null
1292
+ );
1293
+ const streamingMessageRef = useRef(null);
1294
+ const [errorDialog, setErrorDialog] = useState2(null);
1295
+ const [showScrollToBottomButton, setShowScrollToBottomButton] = useState2(false);
1296
+ const [userHasScrolledUp, setUserHasScrolledUp] = useState2(false);
1297
+ const [openApp, setOpenApp] = useState2(null);
1298
+ const [openAnalytic, setOpenAnalytic] = useState2(null);
1299
+ const [chatWidth, setChatWidth] = useState2(30);
1300
+ const [isResizingChatApp, setIsResizingChatApp] = useState2(false);
1301
+ const chatAppResizeHandleRef = useRef(null);
1302
+ const chatAppContainerRef = useRef(null);
1303
+ const minChatWidth = 20;
1304
+ const maxChatWidth = 80;
1305
+ const hasMessages = messages.length > 0 || !!streamingMessage;
1306
+ const hasOpenPanel = !!openApp || !!openAnalytic;
1307
+ const isWaitingForInput = useMemo2(() => {
1308
+ return conversation.waiting === true || messages.some((message) => message.waiting === true);
1309
+ }, [conversation.waiting, messages]);
1310
+ const allMessages = useMemo2(() => {
1311
+ const combined = [...messages];
1312
+ if (streamingMessage) {
1313
+ combined.push(streamingMessage);
1314
+ }
1315
+ return combined.sort((a, b) => {
1316
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
1317
+ });
1318
+ }, [messages, streamingMessage]);
1319
+ const scrollToBottom = useCallback(
1320
+ (behavior = "smooth") => {
1321
+ const container = scrollContainerRef.current;
1322
+ const messagesEnd = messagesEndRef.current;
1323
+ if (messagesEnd) {
1324
+ messagesEnd.scrollIntoView({ behavior, block: "end" });
1325
+ }
1326
+ if (container) {
1327
+ container.scrollTop = container.scrollHeight;
1328
+ }
1329
+ setTimeout(() => {
1330
+ const scrollContainer = document.querySelector(
1331
+ '[data-main-chat-scroll="true"]'
1332
+ );
1333
+ if (scrollContainer) {
1334
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
1335
+ }
1336
+ }, 10);
1337
+ setShowScrollToBottomButton(false);
1338
+ setUserHasScrolledUp(false);
1339
+ },
1340
+ []
1341
+ );
1342
+ useEffect2(() => {
1343
+ const container = scrollContainerRef.current;
1344
+ if (!container) return;
1345
+ const handleScroll = () => {
1346
+ const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < SCROLL_BOTTOM_THRESHOLD;
1347
+ const isContentScrollable = container.scrollHeight > container.clientHeight;
1348
+ if (isAtBottom) {
1349
+ setShowScrollToBottomButton(false);
1350
+ setUserHasScrolledUp(false);
1351
+ } else {
1352
+ setUserHasScrolledUp(true);
1353
+ if (isContentScrollable) {
1354
+ setShowScrollToBottomButton(true);
1355
+ } else {
1356
+ setShowScrollToBottomButton(false);
1357
+ }
1358
+ }
1359
+ };
1360
+ container.addEventListener("scroll", handleScroll);
1361
+ handleScroll();
1362
+ return () => container.removeEventListener("scroll", handleScroll);
1363
+ }, [allMessages]);
1364
+ useEffect2(() => {
1365
+ const container = scrollContainerRef.current;
1366
+ if (!container || !hasMessages) {
1367
+ setShowScrollToBottomButton(false);
1368
+ return;
1369
+ }
1370
+ if (!userHasScrolledUp) {
1371
+ scrollToBottom("smooth");
1372
+ } else {
1373
+ const isContentScrollable = container.scrollHeight > container.clientHeight;
1374
+ if (isContentScrollable) {
1375
+ setShowScrollToBottomButton(true);
1376
+ } else {
1377
+ setShowScrollToBottomButton(false);
1378
+ }
1379
+ }
1380
+ }, [allMessages, scrollToBottom, userHasScrolledUp, hasMessages]);
1381
+ useEffect2(() => {
1382
+ setShowScrollToBottomButton(false);
1383
+ setUserHasScrolledUp(false);
1384
+ const timeoutId = setTimeout(() => {
1385
+ scrollToBottom("auto");
1386
+ }, 100);
1387
+ return () => clearTimeout(timeoutId);
1388
+ }, [conversation.id, scrollToBottom]);
1389
+ useEffect2(() => {
1390
+ setMessages(conversation.messages || []);
1391
+ }, [conversation.id, conversation.messages]);
1392
+ useEffect2(() => {
1393
+ if (openApp) {
1394
+ setOpenApp(null);
1395
+ }
1396
+ if (openAnalytic) {
1397
+ setOpenAnalytic(null);
1398
+ }
1399
+ onAppStateChange == null ? void 0 : onAppStateChange(false);
1400
+ }, [conversation.id]);
1401
+ useEffect2(() => {
1402
+ if (messages.length > 0 && !userHasScrolledUp) {
1403
+ const timeoutId = setTimeout(() => {
1404
+ scrollToBottom("smooth");
1405
+ }, 50);
1406
+ return () => clearTimeout(timeoutId);
1407
+ }
1408
+ }, [messages.length, scrollToBottom, userHasScrolledUp]);
1409
+ useEffect2(() => {
1410
+ if (conversation.messages && conversation.messages.length > 0) {
1411
+ const timeouts = [
1412
+ setTimeout(() => scrollToBottom("auto"), 50),
1413
+ setTimeout(() => scrollToBottom("auto"), 200),
1414
+ setTimeout(() => scrollToBottom("auto"), 500)
1415
+ ];
1416
+ return () => timeouts.forEach(clearTimeout);
1417
+ }
1418
+ }, [conversation.messages, conversation.id, scrollToBottom]);
1419
+ useEffect2(() => {
1420
+ if (hasMessages) {
1421
+ const timeoutId = setTimeout(() => {
1422
+ scrollToBottom("auto");
1423
+ }, 100);
1424
+ return () => clearTimeout(timeoutId);
1425
+ }
1426
+ }, [hasMessages, scrollToBottom]);
1427
+ useEffect2(() => {
1428
+ return () => {
1429
+ setIsStreaming(false);
1430
+ setStreamingMessage(null);
1431
+ streamingMessageRef.current = null;
1432
+ };
1433
+ }, []);
1434
+ const handleAddUserMessage = useCallback(
1435
+ (message) => {
1436
+ setMessages((prev) => [...prev, message]);
1437
+ setUserHasScrolledUp(false);
1438
+ setShowScrollToBottomButton(false);
1439
+ setTimeout(() => {
1440
+ scrollToBottom("smooth");
1441
+ }, 50);
1442
+ },
1443
+ [scrollToBottom]
1444
+ );
1445
+ const handleStreamStart = useCallback(() => {
1446
+ const newStreamingMessage = {
1447
+ id: `stream-${Date.now()}`,
1448
+ role: "assistant",
1449
+ content: "",
1450
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1451
+ };
1452
+ streamingMessageRef.current = {
1453
+ id: newStreamingMessage.id,
1454
+ role: newStreamingMessage.role,
1455
+ createdAt: newStreamingMessage.createdAt
1456
+ };
1457
+ setStreamingMessage(newStreamingMessage);
1458
+ setIsStreaming(true);
1459
+ }, []);
1460
+ const handleToolExecutionStart = useCallback((toolName) => {
1461
+ setStreamingMessage((prev) => {
1462
+ if (!prev) return null;
1463
+ return __spreadProps(__spreadValues({}, prev), {
1464
+ isToolExecuting: true,
1465
+ executingToolName: toolName
1466
+ });
1467
+ });
1468
+ }, []);
1469
+ const handleToolExecutionEnd = useCallback(() => {
1470
+ setStreamingMessage((prev) => {
1471
+ if (!prev) return null;
1472
+ return __spreadProps(__spreadValues({}, prev), {
1473
+ isToolExecuting: false,
1474
+ executingToolName: void 0
1475
+ });
1476
+ });
1477
+ }, []);
1478
+ const pendingChunksRef = useRef("");
1479
+ const throttleTimerRef = useRef(null);
1480
+ const handleStreamUpdate = useCallback((chunk) => {
1481
+ pendingChunksRef.current += chunk;
1482
+ if (throttleTimerRef.current) {
1483
+ clearTimeout(throttleTimerRef.current);
1484
+ }
1485
+ throttleTimerRef.current = setTimeout(() => {
1486
+ const chunksToAdd = pendingChunksRef.current;
1487
+ pendingChunksRef.current = "";
1488
+ setStreamingMessage((prev) => {
1489
+ if (!prev) return null;
1490
+ const newContent = (prev.content || "") + chunksToAdd;
1491
+ return __spreadProps(__spreadValues({}, prev), { content: newContent });
1492
+ });
1493
+ }, 50);
1494
+ }, []);
1495
+ const handleStreamEnd = useCallback(
1496
+ async (finalContent, metadata) => {
1497
+ if (throttleTimerRef.current) {
1498
+ clearTimeout(throttleTimerRef.current);
1499
+ throttleTimerRef.current = null;
1500
+ }
1501
+ if (pendingChunksRef.current) {
1502
+ const chunksToAdd = pendingChunksRef.current;
1503
+ pendingChunksRef.current = "";
1504
+ setStreamingMessage((prev) => {
1505
+ if (!prev) return null;
1506
+ return __spreadProps(__spreadValues({}, prev), {
1507
+ content: (prev.content || "") + chunksToAdd
1508
+ });
1509
+ });
1510
+ }
1511
+ const streamDetails = streamingMessageRef.current;
1512
+ let messageToAdd = null;
1513
+ if (streamDetails && (finalContent == null ? void 0 : finalContent.trim())) {
1514
+ const hasFormWidget = /```widget:form\n[\s\S]*?\n```/.test(
1515
+ finalContent
1516
+ );
1517
+ const hasDecisionWidget = /```(?:widget:decision|decision)\n[\s\S]*?\n```/.test(
1518
+ finalContent
1519
+ );
1520
+ messageToAdd = __spreadValues(__spreadValues({
1521
+ id: `assistant-${Date.now()}`,
1522
+ role: streamDetails.role,
1523
+ createdAt: streamDetails.createdAt,
1524
+ content: finalContent.trim()
1525
+ }, (hasFormWidget || hasDecisionWidget) && {
1526
+ waiting: true
1527
+ }), metadata && {
1528
+ metadata: {
1529
+ logId: metadata.logId,
1530
+ steps: metadata.steps
1531
+ }
1532
+ });
1533
+ }
1534
+ if (messageToAdd) {
1535
+ setMessages((prev) => [...prev, messageToAdd]);
1536
+ }
1537
+ setIsStreaming(false);
1538
+ setStreamingMessage(null);
1539
+ streamingMessageRef.current = null;
1540
+ },
1541
+ []
1542
+ );
1543
+ const handleError = useCallback((details) => {
1544
+ console.log("handleError called with:", details);
1545
+ setIsStreaming(false);
1546
+ setStreamingMessage(null);
1547
+ streamingMessageRef.current = null;
1548
+ setErrorDialog(details);
1549
+ setShowScrollToBottomButton(false);
1550
+ }, []);
1551
+ const handleStopStreaming = useCallback(() => {
1552
+ console.log("Stop streaming requested");
1553
+ setIsStreaming(false);
1554
+ setStreamingMessage(null);
1555
+ streamingMessageRef.current = null;
1556
+ }, []);
1557
+ const handleAppOpen = useCallback(
1558
+ (name, url) => {
1559
+ setOpenAnalytic(null);
1560
+ setOpenApp({ name, url });
1561
+ onAppStateChange == null ? void 0 : onAppStateChange(true);
1562
+ },
1563
+ [onAppStateChange]
1564
+ );
1565
+ const handleAnalyticOpen = useCallback(
1566
+ (name, data) => {
1567
+ setOpenApp(null);
1568
+ setOpenAnalytic({ name, data });
1569
+ onAppStateChange == null ? void 0 : onAppStateChange(true);
1570
+ onSidebarToggle == null ? void 0 : onSidebarToggle();
1571
+ },
1572
+ [onAppStateChange, onSidebarToggle]
1573
+ );
1574
+ const handlePanelClose = useCallback(() => {
1575
+ setOpenApp(null);
1576
+ setOpenAnalytic(null);
1577
+ onAppStateChange == null ? void 0 : onAppStateChange(false);
1578
+ }, [onAppStateChange]);
1579
+ const handleWidgetSubmit = useCallback(
1580
+ async (data, actionId, messageId, widgetId) => {
1581
+ try {
1582
+ setMessages(
1583
+ (prev) => prev.map(
1584
+ (message) => message.id === messageId ? __spreadProps(__spreadValues({}, message), { waiting: false }) : message
1585
+ )
1586
+ );
1587
+ if (conversation.waiting === true) {
1588
+ }
1589
+ const stepData = {
1590
+ action: actionId,
1591
+ data,
1592
+ id: widgetId,
1593
+ // Use the actual widget ID passed from the form
1594
+ messageId
1595
+ // Backend will use this to update message.waiting = false
1596
+ };
1597
+ if (agentId) {
1598
+ handleStreamStart();
1599
+ const { stream } = await chatClient.messages.sendMessage(
1600
+ agentId,
1601
+ conversation.id,
1602
+ "",
1603
+ //`Processing form submission...`, // Simple processing message
1604
+ messages,
1605
+ new AbortController().signal,
1606
+ debug,
1607
+ stepData,
1608
+ conversation.isTemporary
1609
+ // Pass isTemporary flag
1610
+ );
1611
+ const reader = stream.getReader();
1612
+ const decoder = new TextDecoder();
1613
+ let done = false;
1614
+ let accumulatedResponse = "";
1615
+ while (!done) {
1616
+ const { value, done: readerDone } = await reader.read();
1617
+ done = readerDone;
1618
+ if (value) {
1619
+ const chunk = decoder.decode(value, {
1620
+ stream: !done
1621
+ });
1622
+ if (chunk) {
1623
+ accumulatedResponse += chunk;
1624
+ handleStreamUpdate(chunk);
1625
+ }
1626
+ }
1627
+ }
1628
+ handleStreamEnd(accumulatedResponse);
1629
+ if (onConversationUpdate) {
1630
+ try {
1631
+ await onConversationUpdate();
1632
+ } catch (error) {
1633
+ console.error(
1634
+ "Failed to refresh conversation after form submission:",
1635
+ error
1636
+ );
1637
+ }
1638
+ }
1639
+ }
1640
+ } catch (error) {
1641
+ console.error("Error submitting form:", error);
1642
+ handleError({
1643
+ title: "Form Submission Error",
1644
+ message: "Failed to submit form data. Please try again."
1645
+ });
1646
+ }
1647
+ },
1648
+ [
1649
+ agentId,
1650
+ conversation.id,
1651
+ messages,
1652
+ debug,
1653
+ handleStreamStart,
1654
+ handleStreamUpdate,
1655
+ handleStreamEnd,
1656
+ handleError
1657
+ ]
1658
+ );
1659
+ const handleWidgetCancel = useCallback(
1660
+ async (messageId, widgetId) => {
1661
+ try {
1662
+ setMessages(
1663
+ (prev) => prev.map(
1664
+ (message) => message.id === messageId ? __spreadProps(__spreadValues({}, message), { waiting: false }) : message
1665
+ )
1666
+ );
1667
+ const stepData = {
1668
+ action: "cancel",
1669
+ data: null,
1670
+ // No form data for cancellation
1671
+ id: widgetId,
1672
+ messageId
1673
+ };
1674
+ if (agentId) {
1675
+ handleStreamStart();
1676
+ const { stream } = await chatClient.messages.sendMessage(
1677
+ agentId,
1678
+ conversation.id,
1679
+ "",
1680
+ //cancelMessage.content,
1681
+ messages,
1682
+ new AbortController().signal,
1683
+ debug,
1684
+ stepData,
1685
+ conversation.isTemporary
1686
+ // Pass isTemporary flag
1687
+ );
1688
+ const reader = stream.getReader();
1689
+ const decoder = new TextDecoder();
1690
+ let done = false;
1691
+ let accumulatedResponse = "";
1692
+ while (!done) {
1693
+ const { value, done: readerDone } = await reader.read();
1694
+ done = readerDone;
1695
+ if (value) {
1696
+ const chunk = decoder.decode(value, {
1697
+ stream: !done
1698
+ });
1699
+ if (chunk) {
1700
+ accumulatedResponse += chunk;
1701
+ handleStreamUpdate(chunk);
1702
+ }
1703
+ }
1704
+ }
1705
+ handleStreamEnd(accumulatedResponse);
1706
+ if (onConversationUpdate) {
1707
+ try {
1708
+ await onConversationUpdate();
1709
+ } catch (error) {
1710
+ console.error(
1711
+ "Failed to refresh conversation after form cancellation:",
1712
+ error
1713
+ );
1714
+ }
1715
+ }
1716
+ }
1717
+ } catch (error) {
1718
+ console.error("Error cancelling form:", error);
1719
+ handleError({
1720
+ title: "Form Cancellation Error",
1721
+ message: "Failed to cancel form. Please try again."
1722
+ });
1723
+ }
1724
+ },
1725
+ [
1726
+ agentId,
1727
+ conversation.id,
1728
+ messages,
1729
+ debug,
1730
+ handleStreamStart,
1731
+ handleStreamUpdate,
1732
+ handleStreamEnd,
1733
+ handleError,
1734
+ onConversationUpdate
1735
+ ]
1736
+ );
1737
+ const startResizingChatApp = useCallback(
1738
+ (e) => {
1739
+ e.preventDefault();
1740
+ setIsResizingChatApp(true);
1741
+ },
1742
+ []
1743
+ );
1744
+ useEffect2(() => {
1745
+ const handleMouseMove = (e) => {
1746
+ if (!isResizingChatApp) return;
1747
+ const mainContainer = chatAppContainerRef.current;
1748
+ if (!mainContainer) return;
1749
+ const containerRect = mainContainer.getBoundingClientRect();
1750
+ const containerWidth = containerRect.width;
1751
+ const relativeX = e.clientX - containerRect.left;
1752
+ const newChatWidthPercent = relativeX / containerWidth * 100;
1753
+ const clampedWidth = Math.max(
1754
+ minChatWidth,
1755
+ Math.min(newChatWidthPercent, maxChatWidth)
1756
+ );
1757
+ setChatWidth(clampedWidth);
1758
+ };
1759
+ const handleMouseUp = () => {
1760
+ setIsResizingChatApp(false);
1761
+ };
1762
+ if (isResizingChatApp) {
1763
+ document.addEventListener("mousemove", handleMouseMove);
1764
+ document.addEventListener("mouseup", handleMouseUp);
1765
+ document.body.classList.add("resize-active");
1766
+ document.body.style.cursor = "col-resize";
1767
+ }
1768
+ return () => {
1769
+ document.removeEventListener("mousemove", handleMouseMove);
1770
+ document.removeEventListener("mouseup", handleMouseUp);
1771
+ document.body.classList.remove("resize-active");
1772
+ document.body.style.cursor = "";
1773
+ };
1774
+ }, [isResizingChatApp, minChatWidth, maxChatWidth]);
1775
+ const ErrorDialog = () => {
1776
+ if (!errorDialog) return null;
1777
+ return /* @__PURE__ */ jsx2("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs2("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 max-w-sm w-full", children: [
1778
+ /* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center mb-4", children: [
1779
+ /* @__PURE__ */ jsx2("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: errorDialog.title }),
1780
+ /* @__PURE__ */ jsx2(
1781
+ "button",
1782
+ {
1783
+ onClick: () => setErrorDialog(null),
1784
+ className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
1785
+ "aria-label": "Close error dialog",
1786
+ children: /* @__PURE__ */ jsx2(X2, { size: 20 })
1787
+ }
1788
+ )
1789
+ ] }),
1790
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-600 dark:text-gray-300 mb-4", children: errorDialog.message }),
1791
+ /* @__PURE__ */ jsx2("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx2(
1792
+ "button",
1793
+ {
1794
+ onClick: () => setErrorDialog(null),
1795
+ className: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800",
1796
+ children: "OK"
1797
+ }
1798
+ ) })
1799
+ ] }) });
1800
+ };
1801
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1802
+ /* @__PURE__ */ jsx2("style", { children: `
1803
+ .resize-active {
1804
+ user-select: none;
1805
+ }
1806
+ .resize-active * {
1807
+ cursor: col-resize !important;
1808
+ }
1809
+ ` }),
1810
+ /* @__PURE__ */ jsxs2(
1811
+ "div",
1812
+ {
1813
+ ref: chatAppContainerRef,
1814
+ className: "flex h-full relative w-full",
1815
+ children: [
1816
+ /* @__PURE__ */ jsx2(ErrorDialog, {}),
1817
+ /* @__PURE__ */ jsxs2(
1818
+ "div",
1819
+ {
1820
+ className: `flex flex-col transition-all duration-300 relative min-w-[500px] ${hasOpenPanel ? "" : "w-full"}`,
1821
+ style: hasOpenPanel ? { width: `${chatWidth}%` } : {},
1822
+ children: [
1823
+ isResizingChatApp && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 z-40 bg-transparent cursor-col-resize" }),
1824
+ hasMessages ? (
1825
+ // Standard layout with messages and input at bottom
1826
+ /* @__PURE__ */ jsxs2(Fragment2, { children: [
1827
+ /* @__PURE__ */ jsx2(
1828
+ "div",
1829
+ {
1830
+ ref: scrollContainerRef,
1831
+ className: `flex-1 overflow-y-auto py-6 relative ${hasOpenPanel ? "px-2 sm:px-4" : "px-4 sm:px-8"}`,
1832
+ "data-ref": "scrollContainerRef",
1833
+ "data-main-chat-scroll": "true",
1834
+ children: /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
1835
+ allMessages.map((message, index) => {
1836
+ const isCurrentlyStreaming = isStreaming && (streamingMessage == null ? void 0 : streamingMessage.id) === message.id;
1837
+ return (
1838
+ // @ts-ignore - Typescript doesn't recognize dynamic imports properly
1839
+ /* @__PURE__ */ jsx2(
1840
+ DynamicChatMessage,
1841
+ {
1842
+ message,
1843
+ isStreaming: isCurrentlyStreaming,
1844
+ theme,
1845
+ onAppOpen: handleAppOpen,
1846
+ onAnalyticOpen: handleAnalyticOpen,
1847
+ onWidgetSubmit: handleWidgetSubmit,
1848
+ onWidgetCancel: handleWidgetCancel
1849
+ },
1850
+ message.id || index
1851
+ )
1852
+ );
1853
+ }),
1854
+ /* @__PURE__ */ jsx2(
1855
+ "div",
1856
+ {
1857
+ ref: messagesEndRef,
1858
+ "data-ref": "messagesEndRef"
1859
+ }
1860
+ )
1861
+ ] })
1862
+ }
1863
+ ),
1864
+ !isReadOnly && /* @__PURE__ */ jsxs2(
1865
+ "div",
1866
+ {
1867
+ className: `border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 ${hasOpenPanel ? "p-2 sm:p-3" : "p-4 sm:p-6"}`,
1868
+ children: [
1869
+ isWaitingForInput && /* @__PURE__ */ jsxs2("div", { className: "mb-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg", children: [
1870
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
1871
+ /* @__PURE__ */ jsxs2("div", { className: "flex space-x-1", children: [
1872
+ /* @__PURE__ */ jsx2("div", { className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse" }),
1873
+ /* @__PURE__ */ jsx2(
1874
+ "div",
1875
+ {
1876
+ className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse",
1877
+ style: {
1878
+ animationDelay: "0.2s"
1879
+ }
1880
+ }
1881
+ ),
1882
+ /* @__PURE__ */ jsx2(
1883
+ "div",
1884
+ {
1885
+ className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse",
1886
+ style: {
1887
+ animationDelay: "0.4s"
1888
+ }
1889
+ }
1890
+ )
1891
+ ] }),
1892
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-amber-800 dark:text-amber-200 font-medium", children: "Waiting for form input..." })
1893
+ ] }),
1894
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-amber-700 dark:text-amber-300 mt-1", children: "Complete the form above or click Cancel to continue without data capture." })
1895
+ ] }),
1896
+ showScrollToBottomButton && /* @__PURE__ */ jsx2("div", { className: "absolute bottom-22 left-1/2 transform -translate-x-1/2 z-10", children: /* @__PURE__ */ jsx2(
1897
+ "button",
1898
+ {
1899
+ onClick: () => scrollToBottom("smooth"),
1900
+ className: "bg-indigo-600 hover:bg-indigo-700 text-white rounded-full p-3 shadow-lg transition-opacity duration-200 ease-in-out animate-bounce",
1901
+ title: "Scroll to bottom",
1902
+ children: /* @__PURE__ */ jsx2(ArrowDownCircle, { size: 24 })
1903
+ }
1904
+ ) }),
1905
+ /* @__PURE__ */ jsx2(Suspense, { fallback: null, children: /* @__PURE__ */ jsx2(
1906
+ ChatInput,
1907
+ {
1908
+ conversationId: conversation.id,
1909
+ agentId,
1910
+ debug,
1911
+ onAddUserMessage: handleAddUserMessage,
1912
+ onStreamStart: handleStreamStart,
1913
+ onStreamUpdate: handleStreamUpdate,
1914
+ onStreamEnd: handleStreamEnd,
1915
+ onError: handleError,
1916
+ messages,
1917
+ isStreaming,
1918
+ onStopStreaming: handleStopStreaming,
1919
+ disabled: isWaitingForInput,
1920
+ onThreadCreated,
1921
+ onToolExecutionStart: handleToolExecutionStart,
1922
+ onToolExecutionEnd: handleToolExecutionEnd
1923
+ }
1924
+ ) })
1925
+ ]
1926
+ }
1927
+ )
1928
+ ] })
1929
+ ) : (
1930
+ // Empty state with centered input - or read-only message for log mode
1931
+ /* @__PURE__ */ jsx2("div", { className: "flex h-full flex-col items-center justify-center p-4", children: isReadOnly ? /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
1932
+ /* @__PURE__ */ jsx2("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: "Log View" }),
1933
+ /* @__PURE__ */ jsx2("p", { className: "text-gray-500 dark:text-gray-400", children: "This log contains no messages." })
1934
+ ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1935
+ /* @__PURE__ */ jsxs2("div", { className: "mb-8 flex flex-col items-center", children: [
1936
+ /* @__PURE__ */ jsx2("div", { className: "mb-4 text-gray-500" }),
1937
+ /* @__PURE__ */ jsx2(
1938
+ "h2",
1939
+ {
1940
+ className: `mb-2 font-bold ${hasOpenPanel ? "text-lg" : "text-2xl"}`,
1941
+ children: "How can I help you today?"
1942
+ }
1943
+ ),
1944
+ /* @__PURE__ */ jsx2(
1945
+ "p",
1946
+ {
1947
+ className: `text-center text-gray-500 ${hasOpenPanel ? "text-sm max-w-xs" : "max-w-md"}`,
1948
+ children: "Feel free to ask any question you like \u2014 just be precise, as if you're speaking to a real person."
1949
+ }
1950
+ )
1951
+ ] }),
1952
+ /* @__PURE__ */ jsx2(
1953
+ "div",
1954
+ {
1955
+ className: `w-full ${hasOpenPanel ? "max-w-sm" : "max-w-2xl"} px-4`,
1956
+ children: /* @__PURE__ */ jsx2(Suspense, { fallback: null, children: /* @__PURE__ */ jsx2(
1957
+ ChatInput,
1958
+ {
1959
+ conversationId: conversation.id,
1960
+ agentId,
1961
+ debug,
1962
+ onAddUserMessage: handleAddUserMessage,
1963
+ onStreamStart: handleStreamStart,
1964
+ onStreamUpdate: handleStreamUpdate,
1965
+ onStreamEnd: handleStreamEnd,
1966
+ onError: handleError,
1967
+ messages: [],
1968
+ isStreaming,
1969
+ onStopStreaming: handleStopStreaming,
1970
+ onThreadCreated,
1971
+ onToolExecutionStart: handleToolExecutionStart,
1972
+ onToolExecutionEnd: handleToolExecutionEnd
1973
+ }
1974
+ ) })
1975
+ }
1976
+ )
1977
+ ] }) })
1978
+ )
1979
+ ]
1980
+ }
1981
+ ),
1982
+ hasOpenPanel && /* @__PURE__ */ jsx2(
1983
+ "div",
1984
+ {
1985
+ ref: chatAppResizeHandleRef,
1986
+ onMouseDown: startResizingChatApp,
1987
+ className: `h-full cursor-col-resize transition-all z-50 relative flex items-center justify-center ${isResizingChatApp ? "w-2 bg-indigo-500 dark:bg-indigo-400" : "w-1 hover:w-2 bg-gray-300 dark:bg-gray-700 hover:bg-indigo-500 dark:hover:bg-indigo-400"}`,
1988
+ title: "Drag to resize chat and app areas",
1989
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col space-y-1 opacity-60", children: [
1990
+ /* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" }),
1991
+ /* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" }),
1992
+ /* @__PURE__ */ jsx2("div", { className: "w-0.5 h-0.5 bg-white rounded-full" })
1993
+ ] })
1994
+ }
1995
+ ),
1996
+ hasOpenPanel && /* @__PURE__ */ jsxs2("div", { className: "flex-1 bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700 flex flex-col w-full", children: [
1997
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between p-3 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: [
1998
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center space-x-3", children: [
1999
+ /* @__PURE__ */ jsx2(
2000
+ "div",
2001
+ {
2002
+ className: `w-8 h-8 rounded-lg flex items-center justify-center ${openApp ? "bg-blue-500" : "bg-emerald-500"}`,
2003
+ children: openApp ? /* @__PURE__ */ jsx2(
2004
+ "svg",
2005
+ {
2006
+ className: "w-5 h-5 text-white",
2007
+ fill: "none",
2008
+ stroke: "currentColor",
2009
+ viewBox: "0 0 24 24",
2010
+ xmlns: "http://www.w3.org/2000/svg",
2011
+ children: /* @__PURE__ */ jsx2(
2012
+ "path",
2013
+ {
2014
+ strokeLinecap: "round",
2015
+ strokeLinejoin: "round",
2016
+ strokeWidth: 2,
2017
+ d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
2018
+ }
2019
+ )
2020
+ }
2021
+ ) : /* @__PURE__ */ jsx2(
2022
+ "svg",
2023
+ {
2024
+ className: "w-5 h-5 text-white",
2025
+ fill: "none",
2026
+ stroke: "currentColor",
2027
+ viewBox: "0 0 24 24",
2028
+ xmlns: "http://www.w3.org/2000/svg",
2029
+ children: /* @__PURE__ */ jsx2(
2030
+ "path",
2031
+ {
2032
+ strokeLinecap: "round",
2033
+ strokeLinejoin: "round",
2034
+ strokeWidth: 2,
2035
+ d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
2036
+ }
2037
+ )
2038
+ }
2039
+ )
2040
+ }
2041
+ ),
2042
+ /* @__PURE__ */ jsxs2("div", { children: [
2043
+ /* @__PURE__ */ jsx2("h3", { className: "font-semibold text-gray-800 dark:text-gray-200", children: openApp ? openApp.name : "Analytics Dashboard" }),
2044
+ openAnalytic && /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: openAnalytic.name })
2045
+ ] })
2046
+ ] }),
2047
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsx2(
2048
+ "button",
2049
+ {
2050
+ onClick: handlePanelClose,
2051
+ className: "p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors",
2052
+ "aria-label": "Close panel",
2053
+ children: /* @__PURE__ */ jsx2(
2054
+ X2,
2055
+ {
2056
+ size: 20,
2057
+ className: "text-gray-600 dark:text-gray-400"
2058
+ }
2059
+ )
2060
+ }
2061
+ ) })
2062
+ ] }),
2063
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 relative h-full", children: [
2064
+ isResizingChatApp && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 z-50 bg-transparent cursor-col-resize" }),
2065
+ openApp ? /* @__PURE__ */ jsx2(
2066
+ "iframe",
2067
+ {
2068
+ src: openApp.url,
2069
+ className: "w-full h-full border-0",
2070
+ title: openApp.name,
2071
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
2072
+ sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-top-navigation-by-user-activation"
2073
+ }
2074
+ ) : openAnalytic ? /* @__PURE__ */ jsx2(
2075
+ AnalyticsPanel,
2076
+ {
2077
+ name: openAnalytic.name,
2078
+ csvData: openAnalytic.data,
2079
+ theme
2080
+ }
2081
+ ) : null
2082
+ ] })
2083
+ ] })
2084
+ ]
2085
+ }
2086
+ )
2087
+ ] });
2088
+ }
2089
+
2090
+ // app/components/confirmation-dialog.tsx
2091
+ import { createPortal } from "react-dom";
2092
+ import { useState as useState3, useEffect as useEffect3 } from "react";
2093
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2094
+ function ConfirmationDialog({
2095
+ isOpen,
2096
+ onClose,
2097
+ onConfirm,
2098
+ title,
2099
+ message,
2100
+ confirmText = "Confirm",
2101
+ cancelText = "Cancel",
2102
+ confirmButtonClass = "bg-red-500 hover:bg-red-600"
2103
+ }) {
2104
+ const [isMounted, setIsMounted] = useState3(false);
2105
+ useEffect3(() => {
2106
+ setIsMounted(true);
2107
+ return () => setIsMounted(false);
2108
+ }, []);
2109
+ if (!isOpen || !isMounted) return null;
2110
+ return createPortal(
2111
+ /* @__PURE__ */ jsxs3("div", { className: "fixed inset-0 z-[9999] flex items-center justify-center", children: [
2112
+ /* @__PURE__ */ jsx3(
2113
+ "div",
2114
+ {
2115
+ className: "fixed inset-0 bg-black/50",
2116
+ onClick: onClose,
2117
+ "aria-hidden": "true"
2118
+ }
2119
+ ),
2120
+ /* @__PURE__ */ jsxs3("div", { className: "relative bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-80 max-w-[90vw] z-10", children: [
2121
+ /* @__PURE__ */ jsx3("h3", { className: "text-lg font-medium mb-2", children: title }),
2122
+ /* @__PURE__ */ jsx3("p", { className: "text-gray-500 dark:text-gray-400 mb-4", children: message }),
2123
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-3", children: [
2124
+ /* @__PURE__ */ jsx3(
2125
+ "button",
2126
+ {
2127
+ onClick: onClose,
2128
+ className: "px-4 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer",
2129
+ children: cancelText
2130
+ }
2131
+ ),
2132
+ /* @__PURE__ */ jsx3(
2133
+ "button",
2134
+ {
2135
+ onClick: onConfirm,
2136
+ className: `px-4 py-2 text-sm text-white rounded-md cursor-pointer ${confirmButtonClass}`,
2137
+ children: confirmText
2138
+ }
2139
+ )
2140
+ ] })
2141
+ ] })
2142
+ ] }),
2143
+ document.body
2144
+ );
2145
+ }
2146
+
2147
+ // app/components/chat.tsx
2148
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
2149
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect4;
2150
+ function NeptuneChatBot({
2151
+ agentId: propAgentId,
2152
+ debug: propDebug = false,
2153
+ theme: propTheme = "light",
2154
+ localDebug: propLocalDebug
2155
+ }) {
2156
+ const [conversations, setConversations] = useState4([]);
2157
+ const [selectedConversationId, setSelectedConversationId] = useState4(null);
2158
+ const [selectedConversation, setSelectedConversation] = useState4(null);
2159
+ const [isLoading, setIsLoading] = useState4(true);
2160
+ const [isLoadingMessages, setIsLoadingMessages] = useState4(false);
2161
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState4(false);
2162
+ const [pendingDeleteId, setPendingDeleteId] = useState4(null);
2163
+ const [sidebarOpen, setSidebarOpen] = useState4(true);
2164
+ const [titleInput, setTitleInput] = useState4("");
2165
+ const [isSavingTitle, setIsSavingTitle] = useState4(false);
2166
+ const [isRenameDialogOpen, setIsRenameDialogOpen] = useState4(false);
2167
+ const [conversationToRename, setConversationToRename] = useState4(null);
2168
+ const [theme, setTheme] = useState4(propTheme);
2169
+ const [agentId] = useState4(propAgentId);
2170
+ const [debug] = useState4(propDebug);
2171
+ const [initialAssistantIdChecked] = useState4(true);
2172
+ const [initialLoadComplete, setInitialLoadComplete] = useState4(false);
2173
+ const [openMenuId, setOpenMenuId] = useState4(null);
2174
+ const [isCreatingConversation, setIsCreatingConversation] = useState4(false);
2175
+ const [sidebarWidth, setSidebarWidth] = useState4(250);
2176
+ const [isResizing, setIsResizing] = useState4(false);
2177
+ const [hasOpenApp, setHasOpenApp] = useState4(false);
2178
+ const titleInputRef = useRef2(null);
2179
+ const resizeHandleRef = useRef2(null);
2180
+ const hasFetchedRef = useRef2(false);
2181
+ const minSidebarWidth = 200;
2182
+ const maxSidebarWidth = 600;
2183
+ useEffect4(() => {
2184
+ if (propLocalDebug) {
2185
+ configureChatClient(propLocalDebug);
2186
+ }
2187
+ }, [propLocalDebug]);
2188
+ const applyTheme = useCallback2((newTheme) => {
2189
+ document.documentElement.classList.remove("dark");
2190
+ document.documentElement.classList.remove("light");
2191
+ if (newTheme === "dark") {
2192
+ document.documentElement.classList.add("dark");
2193
+ }
2194
+ localStorage.setItem("theme", newTheme);
2195
+ }, []);
2196
+ useEffect4(() => {
2197
+ applyTheme(theme);
2198
+ }, [theme, applyTheme]);
2199
+ const handleAppStateChange = useCallback2((hasApp) => {
2200
+ setHasOpenApp(hasApp);
2201
+ }, []);
2202
+ const toggleTheme = () => {
2203
+ const newTheme = theme === "dark" ? "light" : "dark";
2204
+ setTheme(newTheme);
2205
+ applyTheme(newTheme);
2206
+ document.body.style.opacity = "0.99";
2207
+ setTimeout(() => {
2208
+ document.body.style.opacity = "1";
2209
+ }, 1);
2210
+ };
2211
+ const startResizing = (e) => {
2212
+ e.preventDefault();
2213
+ setIsResizing(true);
2214
+ };
2215
+ useEffect4(() => {
2216
+ const handleMouseMove = (e) => {
2217
+ if (!isResizing) return;
2218
+ const newWidth = e.clientX;
2219
+ const clampedWidth = Math.max(
2220
+ minSidebarWidth,
2221
+ Math.min(newWidth, maxSidebarWidth)
2222
+ );
2223
+ setSidebarWidth(clampedWidth);
2224
+ };
2225
+ const handleMouseUp = () => {
2226
+ setIsResizing(false);
2227
+ };
2228
+ if (isResizing) {
2229
+ document.addEventListener("mousemove", handleMouseMove);
2230
+ document.addEventListener("mouseup", handleMouseUp);
2231
+ document.body.classList.add("resize-active");
2232
+ }
2233
+ return () => {
2234
+ document.removeEventListener("mousemove", handleMouseMove);
2235
+ document.removeEventListener("mouseup", handleMouseUp);
2236
+ document.body.classList.remove("resize-active");
2237
+ };
2238
+ }, [isResizing]);
2239
+ const categorizeConversationsByDate = useCallback2(
2240
+ (conversations2) => {
2241
+ const now = /* @__PURE__ */ new Date();
2242
+ const today = new Date(
2243
+ now.getFullYear(),
2244
+ now.getMonth(),
2245
+ now.getDate()
2246
+ );
2247
+ const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1e3);
2248
+ const last7Days = new Date(
2249
+ today.getTime() - 7 * 24 * 60 * 60 * 1e3
2250
+ );
2251
+ const last30Days = new Date(
2252
+ today.getTime() - 30 * 24 * 60 * 60 * 1e3
2253
+ );
2254
+ const groups = {
2255
+ Today: [],
2256
+ Yesterday: [],
2257
+ "Last 7 Days": [],
2258
+ "Last 30 Days": [],
2259
+ Older: []
2260
+ };
2261
+ conversations2.forEach((conversation) => {
2262
+ try {
2263
+ const convDate = new Date(parseInt(conversation.updatedAt));
2264
+ if (convDate >= today) {
2265
+ groups["Today"].push(conversation);
2266
+ } else if (convDate >= yesterday) {
2267
+ groups["Yesterday"].push(conversation);
2268
+ } else if (convDate >= last7Days) {
2269
+ groups["Last 7 Days"].push(conversation);
2270
+ } else if (convDate >= last30Days) {
2271
+ groups["Last 30 Days"].push(conversation);
2272
+ } else {
2273
+ groups["Older"].push(conversation);
2274
+ }
2275
+ } catch (error) {
2276
+ groups["Older"].push(conversation);
2277
+ }
2278
+ });
2279
+ Object.keys(groups).forEach((key) => {
2280
+ groups[key].sort(
2281
+ (a, b) => new Date(parseInt(b.updatedAt)).getTime() - new Date(parseInt(a.updatedAt)).getTime()
2282
+ );
2283
+ });
2284
+ return groups;
2285
+ },
2286
+ []
2287
+ );
2288
+ const isJson = useCallback2((content) => {
2289
+ if (typeof content === "object" && content !== null) {
2290
+ return Array.isArray(content) || Object.prototype.toString.call(content) === "[object Object]";
2291
+ }
2292
+ if (typeof content === "string") {
2293
+ try {
2294
+ const parsed = JSON.parse(content);
2295
+ return typeof parsed === "object" && parsed !== null;
2296
+ } catch (e) {
2297
+ return false;
2298
+ }
2299
+ }
2300
+ return false;
2301
+ }, []);
2302
+ useEffect4(() => {
2303
+ const fetchConversations = async () => {
2304
+ if (!initialAssistantIdChecked || hasFetchedRef.current) {
2305
+ return;
2306
+ }
2307
+ hasFetchedRef.current = true;
2308
+ setIsLoading(true);
2309
+ try {
2310
+ const data = await chatClient.conversations.getAll(agentId);
2311
+ if (!Array.isArray(data)) {
2312
+ console.error(
2313
+ "Expected array of conversations but got:",
2314
+ data
2315
+ );
2316
+ setConversations([]);
2317
+ return;
2318
+ }
2319
+ setConversations(data);
2320
+ if (data.length > 0) {
2321
+ const validConversations = data.filter(
2322
+ (conv) => conv && conv.updatedAt
2323
+ );
2324
+ if (validConversations.length > 0) {
2325
+ try {
2326
+ const mostRecent = [...validConversations].sort(
2327
+ (a, b) => new Date(parseInt(b.updatedAt)).getTime() - new Date(parseInt(a.updatedAt)).getTime()
2328
+ )[0];
2329
+ selectConversation(mostRecent.id);
2330
+ } catch (sortError) {
2331
+ console.error(
2332
+ "Error sorting conversations:",
2333
+ sortError
2334
+ );
2335
+ selectConversation(validConversations[0].id);
2336
+ }
2337
+ }
2338
+ } else {
2339
+ if (agentId && !isCreatingConversation) {
2340
+ try {
2341
+ setIsCreatingConversation(true);
2342
+ const newConversation = await chatClient.conversations.create(
2343
+ "New Chat",
2344
+ agentId
2345
+ );
2346
+ setConversations([newConversation]);
2347
+ selectConversation(newConversation.id);
2348
+ } catch (createError) {
2349
+ } finally {
2350
+ setIsCreatingConversation(false);
2351
+ }
2352
+ } else {
2353
+ console.warn(
2354
+ "Cannot create conversation: agentId not available or already creating"
2355
+ );
2356
+ }
2357
+ }
2358
+ } catch (error) {
2359
+ console.error("Failed to fetch conversations:", error);
2360
+ } finally {
2361
+ setIsLoading(false);
2362
+ setInitialLoadComplete(true);
2363
+ }
2364
+ };
2365
+ fetchConversations();
2366
+ }, [agentId, initialAssistantIdChecked]);
2367
+ const loadConversationData = async (id) => {
2368
+ setIsLoadingMessages(true);
2369
+ try {
2370
+ const fullConversation = await chatClient.conversations.get(id);
2371
+ setSelectedConversation(fullConversation);
2372
+ } catch (error) {
2373
+ console.error(
2374
+ `Failed to load conversation data for ID ${id}:`,
2375
+ error
2376
+ );
2377
+ } finally {
2378
+ setIsLoadingMessages(false);
2379
+ }
2380
+ };
2381
+ const refreshSelectedConversation = async () => {
2382
+ if (!selectedConversationId) return;
2383
+ try {
2384
+ const updatedConversation = await chatClient.conversations.get(
2385
+ selectedConversationId
2386
+ );
2387
+ setConversations(
2388
+ (prev) => prev.map(
2389
+ (conv) => conv.id === selectedConversationId ? updatedConversation : conv
2390
+ )
2391
+ );
2392
+ setSelectedConversation(updatedConversation);
2393
+ } catch (error) {
2394
+ console.error("Failed to refresh selected conversation:", error);
2395
+ }
2396
+ };
2397
+ const selectConversation = useCallback2((id) => {
2398
+ setSelectedConversationId(id);
2399
+ if (id.startsWith("temp-")) {
2400
+ setConversations((prev) => {
2401
+ const tempConv = prev.find((conv) => conv.id === id);
2402
+ if (tempConv) {
2403
+ setSelectedConversation(tempConv);
2404
+ }
2405
+ return prev;
2406
+ });
2407
+ } else {
2408
+ loadConversationData(id);
2409
+ }
2410
+ setHasOpenApp(false);
2411
+ }, []);
2412
+ const createNewConversation = async () => {
2413
+ try {
2414
+ const existingTempConv = conversations.find(
2415
+ (conv) => conv.isTemporary
2416
+ );
2417
+ if (existingTempConv) {
2418
+ selectConversation(existingTempConv.id);
2419
+ return;
2420
+ }
2421
+ const tempConversation = {
2422
+ id: `temp-${Date.now()}-${Math.random().toString(36).substring(7)}`,
2423
+ title: "New Chat",
2424
+ messages: [],
2425
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2426
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2427
+ isTemporary: true
2428
+ // Mark as temporary
2429
+ };
2430
+ setSelectedConversationId(tempConversation.id);
2431
+ setSelectedConversation(tempConversation);
2432
+ setConversations((prev) => [tempConversation, ...prev]);
2433
+ setHasOpenApp(false);
2434
+ } catch (error) {
2435
+ console.error("Failed to create new conversation:", error);
2436
+ }
2437
+ };
2438
+ const handleThreadCreated = useCallback2(
2439
+ async (oldId, newId) => {
2440
+ console.log(`Thread created: ${oldId} -> ${newId}`);
2441
+ setConversations(
2442
+ (prev) => prev.map(
2443
+ (conv) => conv.id === oldId ? __spreadProps(__spreadValues({}, conv), { id: newId, isTemporary: false }) : conv
2444
+ )
2445
+ );
2446
+ if (selectedConversationId === oldId) {
2447
+ setSelectedConversationId(newId);
2448
+ try {
2449
+ const updatedConv = await chatClient.conversations.get(
2450
+ newId
2451
+ );
2452
+ setSelectedConversation(updatedConv);
2453
+ } catch (error) {
2454
+ console.error(
2455
+ "Failed to fetch updated conversation:",
2456
+ error
2457
+ );
2458
+ }
2459
+ }
2460
+ },
2461
+ [selectedConversationId]
2462
+ );
2463
+ const deleteConversation = async (id) => {
2464
+ try {
2465
+ await chatClient.conversations.delete(id);
2466
+ const updatedConversations = conversations.filter(
2467
+ (conv) => conv.id !== id
2468
+ );
2469
+ setConversations(updatedConversations);
2470
+ if (selectedConversationId === id) {
2471
+ if (updatedConversations.length > 0) {
2472
+ selectConversation(updatedConversations[0].id);
2473
+ } else {
2474
+ setSelectedConversationId(null);
2475
+ setSelectedConversation(null);
2476
+ if (agentId) {
2477
+ try {
2478
+ const newConversation = await chatClient.conversations.create(
2479
+ "New Chat",
2480
+ agentId
2481
+ );
2482
+ setConversations([newConversation]);
2483
+ selectConversation(newConversation.id);
2484
+ } catch (error) {
2485
+ console.error(
2486
+ "Failed to create new conversation after deletion:",
2487
+ error
2488
+ );
2489
+ }
2490
+ }
2491
+ }
2492
+ }
2493
+ } catch (error) {
2494
+ console.error("Failed to delete conversation:", error);
2495
+ }
2496
+ };
2497
+ const handleDeleteClick = (e, id) => {
2498
+ e.stopPropagation();
2499
+ setPendingDeleteId(id);
2500
+ setDeleteDialogOpen(true);
2501
+ setOpenMenuId(null);
2502
+ };
2503
+ const toggleConversationMenu = (e, id) => {
2504
+ e.stopPropagation();
2505
+ setOpenMenuId(openMenuId === id ? null : id);
2506
+ };
2507
+ const handleRenameClick = (e, conversation) => {
2508
+ e.stopPropagation();
2509
+ setConversationToRename(conversation);
2510
+ setTitleInput(conversation.title || "New Chat");
2511
+ setIsRenameDialogOpen(true);
2512
+ setOpenMenuId(null);
2513
+ };
2514
+ const handleConfirmRename = async () => {
2515
+ if (!conversationToRename || !titleInput.trim()) {
2516
+ setIsRenameDialogOpen(false);
2517
+ setConversationToRename(null);
2518
+ return;
2519
+ }
2520
+ setIsSavingTitle(true);
2521
+ try {
2522
+ await chatClient.conversations.update(conversationToRename.id, {
2523
+ title: titleInput.trim()
2524
+ });
2525
+ const updatedConv = __spreadProps(__spreadValues({}, conversationToRename), {
2526
+ title: titleInput.trim()
2527
+ });
2528
+ setConversations(
2529
+ (prev) => prev.map(
2530
+ (conv) => conv.id === conversationToRename.id ? updatedConv : conv
2531
+ )
2532
+ );
2533
+ if (selectedConversationId === conversationToRename.id) {
2534
+ setSelectedConversation(updatedConv);
2535
+ }
2536
+ } catch (error) {
2537
+ console.error("Failed to update conversation title:", error);
2538
+ } finally {
2539
+ setIsSavingTitle(false);
2540
+ setIsRenameDialogOpen(false);
2541
+ setConversationToRename(null);
2542
+ }
2543
+ };
2544
+ const handleCancelRename = () => {
2545
+ setIsRenameDialogOpen(false);
2546
+ setConversationToRename(null);
2547
+ setTitleInput("");
2548
+ };
2549
+ const confirmDelete = async () => {
2550
+ if (pendingDeleteId) {
2551
+ await deleteConversation(pendingDeleteId);
2552
+ setDeleteDialogOpen(false);
2553
+ setPendingDeleteId(null);
2554
+ }
2555
+ };
2556
+ useIsomorphicLayoutEffect(() => {
2557
+ const handleResize = () => {
2558
+ if (window.innerWidth < 1024) setSidebarOpen(false);
2559
+ else setSidebarOpen(true);
2560
+ };
2561
+ handleResize();
2562
+ window.addEventListener("resize", handleResize);
2563
+ return () => window.removeEventListener("resize", handleResize);
2564
+ }, []);
2565
+ useEffect4(() => {
2566
+ const handleClickOutside = () => setOpenMenuId(null);
2567
+ if (openMenuId) {
2568
+ document.addEventListener("click", handleClickOutside);
2569
+ return () => document.removeEventListener("click", handleClickOutside);
2570
+ }
2571
+ }, [openMenuId]);
2572
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
2573
+ !initialLoadComplete && /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 bg-white dark:bg-gray-900 flex items-center justify-center z-50", children: /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
2574
+ /* @__PURE__ */ jsx4("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600 mx-auto mb-4" }),
2575
+ /* @__PURE__ */ jsx4("p", { className: "text-gray-500 dark:text-gray-400", children: "Loading..." })
2576
+ ] }) }),
2577
+ /* @__PURE__ */ jsxs4("div", { className: "flex h-screen overflow-hidden bg-white dark:bg-gray-900", children: [
2578
+ /* @__PURE__ */ jsx4("style", { children: `
2579
+ .resize-active {
2580
+ cursor: col-resize;
2581
+ user-select: none;
2582
+ }
2583
+ ` }),
2584
+ /* @__PURE__ */ jsx4(
2585
+ ConfirmationDialog,
2586
+ {
2587
+ isOpen: deleteDialogOpen,
2588
+ onClose: () => setDeleteDialogOpen(false),
2589
+ onConfirm: confirmDelete,
2590
+ title: "Delete Conversation",
2591
+ message: "Are you sure you want to delete this conversation? This action cannot be undone.",
2592
+ confirmText: "Delete",
2593
+ cancelText: "Cancel",
2594
+ confirmButtonClass: "bg-red-500 hover:bg-red-600"
2595
+ }
2596
+ ),
2597
+ isRenameDialogOpen && /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 bg-black/50 bg-opacity-50 flex items-center justify-center z-50", children: /* @__PURE__ */ jsxs4("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 w-120 max-w-[90vw]", children: [
2598
+ /* @__PURE__ */ jsx4("h3", { className: "text-lg font-medium mb-4 text-gray-900 dark:text-gray-100", children: "Rename Conversation" }),
2599
+ /* @__PURE__ */ jsx4(
2600
+ "input",
2601
+ {
2602
+ ref: titleInputRef,
2603
+ type: "text",
2604
+ value: titleInput,
2605
+ onChange: (e) => setTitleInput(e.target.value),
2606
+ onKeyDown: (e) => {
2607
+ if (e.key === "Enter")
2608
+ handleConfirmRename();
2609
+ else if (e.key === "Escape")
2610
+ handleCancelRename();
2611
+ },
2612
+ className: "w-full bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md py-2 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400",
2613
+ placeholder: "Enter conversation title",
2614
+ disabled: isSavingTitle,
2615
+ autoFocus: true
2616
+ }
2617
+ ),
2618
+ /* @__PURE__ */ jsxs4("div", { className: "flex justify-end gap-2 mt-4", children: [
2619
+ /* @__PURE__ */ jsx4(
2620
+ "button",
2621
+ {
2622
+ onClick: handleCancelRename,
2623
+ disabled: isSavingTitle,
2624
+ className: "px-4 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50",
2625
+ children: "Cancel"
2626
+ }
2627
+ ),
2628
+ /* @__PURE__ */ jsx4(
2629
+ "button",
2630
+ {
2631
+ onClick: handleConfirmRename,
2632
+ disabled: isSavingTitle || !titleInput.trim(),
2633
+ className: "px-4 py-2 text-sm text-white rounded-md bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50",
2634
+ children: isSavingTitle ? "Saving..." : "Rename"
2635
+ }
2636
+ )
2637
+ ] })
2638
+ ] }) }),
2639
+ sidebarOpen && /* @__PURE__ */ jsx4(
2640
+ "div",
2641
+ {
2642
+ className: "lg:hidden fixed inset-0 bg-black bg-opacity-50 z-10",
2643
+ onClick: () => setSidebarOpen(false),
2644
+ "aria-hidden": "true"
2645
+ }
2646
+ ),
2647
+ /* @__PURE__ */ jsx4(
2648
+ "div",
2649
+ {
2650
+ className: `bg-[#f9f9f9] dark:bg-gray-900 dark:!bg-gray-900 border-r border-gray-200 dark:border-gray-700 h-full relative z-20 ${sidebarOpen ? "block" : "hidden"}`,
2651
+ style: {
2652
+ width: sidebarOpen ? `${sidebarWidth}px` : "0"
2653
+ },
2654
+ children: /* @__PURE__ */ jsx4("div", { className: "flex flex-col h-full", children: /* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-y-auto p-2 bg-[#f9f9f9] dark:bg-[#171717]", children: /* @__PURE__ */ jsx4("div", { className: "flex-1", children: isLoading ? /* @__PURE__ */ jsx4("div", { className: "space-y-4" }) : conversations.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "space-y-0", children: (() => {
2655
+ const groupedConversations = categorizeConversationsByDate(
2656
+ conversations
2657
+ );
2658
+ return Object.entries(
2659
+ groupedConversations
2660
+ ).map(
2661
+ ([
2662
+ groupName,
2663
+ groupConversations
2664
+ ]) => {
2665
+ if (groupConversations.length === 0)
2666
+ return null;
2667
+ return /* @__PURE__ */ jsxs4(
2668
+ "div",
2669
+ {
2670
+ className: "mb-4",
2671
+ children: [
2672
+ /* @__PURE__ */ jsx4("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 px-2 py-2 uppercase tracking-wide", children: groupName }),
2673
+ /* @__PURE__ */ jsx4("div", { className: "space-y-0", children: groupConversations.map(
2674
+ (conversation) => {
2675
+ var _a;
2676
+ const hasWaitingMessages = conversation.waiting === true || ((_a = conversation.messages) == null ? void 0 : _a.some(
2677
+ (message) => message.waiting === true
2678
+ )) || false;
2679
+ return /* @__PURE__ */ jsxs4(
2680
+ "div",
2681
+ {
2682
+ onClick: () => {
2683
+ selectConversation(
2684
+ conversation.id
2685
+ );
2686
+ if (window.innerWidth < 1024) {
2687
+ setSidebarOpen(
2688
+ false
2689
+ );
2690
+ }
2691
+ },
2692
+ className: `flex items-center w-full pl-4 pr-2 py-2 mb-1 rounded-xl text-left group cursor-pointer ${selectedConversationId === conversation.id ? "bg-naia-200 text-gray-800 dark:text-gray-200" : "text-gray-800 dark:text-gray-100 conversation-item-background"}`,
2693
+ children: [
2694
+ /* @__PURE__ */ jsx4("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
2695
+ /* @__PURE__ */ jsx4("div", { className: "font-medium overflow-hidden text-ellipsis whitespace-nowrap", children: conversation.title || "New Chat" }),
2696
+ hasWaitingMessages && /* @__PURE__ */ jsx4("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx4(
2697
+ "div",
2698
+ {
2699
+ className: "w-2 h-2 bg-amber-500 dark:bg-amber-400 rounded-full animate-pulse",
2700
+ title: "Waiting for input"
2701
+ }
2702
+ ) })
2703
+ ] }) }),
2704
+ !conversation.isTemporary && /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
2705
+ /* @__PURE__ */ jsx4(
2706
+ "button",
2707
+ {
2708
+ onClick: (e) => toggleConversationMenu(
2709
+ e,
2710
+ conversation.id
2711
+ ),
2712
+ className: "p-1.5 cursor-pointer rounded-full text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 opacity-0 hover:bg-gray-300 dark:hover:bg-gray-700 group-hover:opacity-100 transition-opacity",
2713
+ "aria-label": "More options",
2714
+ children: /* @__PURE__ */ jsx4(
2715
+ MoreHorizontal,
2716
+ {
2717
+ size: 16
2718
+ }
2719
+ )
2720
+ }
2721
+ ),
2722
+ openMenuId === conversation.id && /* @__PURE__ */ jsxs4("div", { className: "absolute right-0 top-10 w-40 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-lg z-50 p-2", children: [
2723
+ /* @__PURE__ */ jsxs4(
2724
+ "button",
2725
+ {
2726
+ onClick: (e) => handleRenameClick(
2727
+ e,
2728
+ conversation
2729
+ ),
2730
+ className: "w-full text-left px-3 py-2 text-md flex cursor-pointer items-center gap-2 hover:bg-gray-300 dark:hover:bg-gray-700 rounded-md",
2731
+ children: [
2732
+ /* @__PURE__ */ jsx4(
2733
+ Edit,
2734
+ {
2735
+ size: 14
2736
+ }
2737
+ ),
2738
+ "Rename"
2739
+ ]
2740
+ }
2741
+ ),
2742
+ /* @__PURE__ */ jsxs4(
2743
+ "button",
2744
+ {
2745
+ onClick: (e) => handleDeleteClick(
2746
+ e,
2747
+ conversation.id
2748
+ ),
2749
+ className: "w-full text-left px-3 py-2 text-md text-red-600 cursor-pointer flex items-center gap-2 hover:bg-gray-300 dark:hover:bg-gray-700 rounded-md",
2750
+ children: [
2751
+ /* @__PURE__ */ jsx4(
2752
+ Trash2,
2753
+ {
2754
+ size: 14
2755
+ }
2756
+ ),
2757
+ "Delete"
2758
+ ]
2759
+ }
2760
+ )
2761
+ ] })
2762
+ ] })
2763
+ ]
2764
+ },
2765
+ conversation.id
2766
+ );
2767
+ }
2768
+ ) })
2769
+ ]
2770
+ },
2771
+ groupName
2772
+ );
2773
+ }
2774
+ ).filter(Boolean);
2775
+ })() }) : /* @__PURE__ */ jsx4("div", { className: "text-center py-8 text-gray-500", children: "No conversations yet" }) }) }) })
2776
+ }
2777
+ ),
2778
+ sidebarOpen && /* @__PURE__ */ jsx4(
2779
+ "div",
2780
+ {
2781
+ ref: resizeHandleRef,
2782
+ onMouseDown: startResizing,
2783
+ className: "w-1 hover:w-2 bg-gray-300 dark:bg-gray-700 h-full cursor-col-resize transition-all hover:bg-indigo-500 dark:hover:bg-indigo-400 z-30 relative",
2784
+ title: "Drag to resize"
2785
+ }
2786
+ ),
2787
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
2788
+ /* @__PURE__ */ jsxs4("header", { className: "h-14 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between px-4 bg-white dark:bg-gray-900", children: [
2789
+ /* @__PURE__ */ jsx4("div", { className: "flex items-center", children: /* @__PURE__ */ jsxs4(Fragment3, { children: [
2790
+ /* @__PURE__ */ jsx4(
2791
+ "button",
2792
+ {
2793
+ onClick: () => setSidebarOpen(!sidebarOpen),
2794
+ className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover disabled:opacity-50 transition-colors",
2795
+ title: "Toggle Sidebar",
2796
+ "aria-label": "Toggle Sidebar",
2797
+ children: sidebarOpen ? /* @__PURE__ */ jsx4(PanelRightOpen, { size: 20 }) : /* @__PURE__ */ jsx4(PanelRightClose, { size: 20 })
2798
+ }
2799
+ ),
2800
+ /* @__PURE__ */ jsx4(
2801
+ "button",
2802
+ {
2803
+ onClick: createNewConversation,
2804
+ className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover disabled:opacity-50 transition-colors",
2805
+ title: "New Chat",
2806
+ "aria-label": "New Chat",
2807
+ children: /* @__PURE__ */ jsx4(MessageCirclePlus, { size: 20 })
2808
+ }
2809
+ )
2810
+ ] }) }),
2811
+ /* @__PURE__ */ jsx4("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "relative flex items-center", children: /* @__PURE__ */ jsx4("span", { className: "text-2xl font-bold mr-1", children: "Naia" }) }) }),
2812
+ /* @__PURE__ */ jsx4("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx4(
2813
+ "button",
2814
+ {
2815
+ onClick: toggleTheme,
2816
+ className: "p-2 cursor-pointer rounded-lg text-gray-500 hover:bg-naia-hover transition-colors",
2817
+ title: `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
2818
+ "aria-label": `Switch to ${theme === "dark" ? "light" : "dark"} mode`,
2819
+ children: theme === "dark" ? /* @__PURE__ */ jsx4(Sun, { size: 20 }) : /* @__PURE__ */ jsx4(Moon, { size: 20 })
2820
+ }
2821
+ ) })
2822
+ ] }),
2823
+ /* @__PURE__ */ jsx4(
2824
+ "div",
2825
+ {
2826
+ className: `flex-1 overflow-hidden bg-white dark:bg-gray-900 text-lg transition-all duration-300 ${hasOpenApp ? "flex" : "flex justify-center"}`,
2827
+ children: /* @__PURE__ */ jsx4(
2828
+ "div",
2829
+ {
2830
+ className: `flex flex-col transition-all duration-300 ${hasOpenApp ? "w-full" : "w-full max-w-6xl"}`,
2831
+ children: isLoading || !initialAssistantIdChecked ? /* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: isLoading ? "Loading conversations..." : "Initializing Assistant..." }) }) : !agentId ? /* @__PURE__ */ jsxs4("div", { className: "flex h-full flex-col items-center justify-center p-4 text-center", children: [
2832
+ /* @__PURE__ */ jsx4("h2", { className: "text-xl font-semibold text-red-600 dark:text-red-400 mb-2", children: "Agent Not Configured" }),
2833
+ /* @__PURE__ */ jsxs4("p", { className: "text-gray-700 dark:text-gray-300 max-w-md", children: [
2834
+ "The Agent ID is missing or invalid. Please ensure it is provided in the URL parameters (e.g.,",
2835
+ " ",
2836
+ /* @__PURE__ */ jsx4("code", { children: "?agentId=your-id-here" }),
2837
+ ")."
2838
+ ] }),
2839
+ /* @__PURE__ */ jsx4("p", { className: "text-gray-500 dark:text-gray-400 mt-4 text-sm", children: "If you continue to see this message, please contact support." })
2840
+ ] }) : isLoadingMessages && selectedConversationId ? /* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: "Loading messages..." }) }) : selectedConversation ? /* @__PURE__ */ jsx4(
2841
+ ChatContent,
2842
+ {
2843
+ conversation: selectedConversation,
2844
+ agentId,
2845
+ debug,
2846
+ onConversationUpdate: refreshSelectedConversation,
2847
+ theme,
2848
+ onAppStateChange: handleAppStateChange,
2849
+ onSidebarToggle: () => setSidebarOpen(false),
2850
+ onThreadCreated: handleThreadCreated
2851
+ }
2852
+ ) : (
2853
+ // Show loading while the useEffect handles conversation creation
2854
+ /* @__PURE__ */ jsx4("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx4("div", { className: "animate-pulse text-gray-500", children: isCreatingConversation ? "Creating new conversation..." : conversations.length === 0 ? "Creating new conversation..." : "Loading conversation..." }) })
2855
+ )
2856
+ }
2857
+ )
2858
+ }
2859
+ )
2860
+ ] })
2861
+ ] })
2862
+ ] });
2863
+ }
2864
+ export {
2865
+ NeptuneChatBot,
2866
+ ToolExecutionIndicator,
2867
+ ToolExecutionWidget,
2868
+ configureChatClient
2869
+ };