bolt-table 0.1.35 → 0.1.37

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.js CHANGED
@@ -33,7 +33,8 @@ __export(index_exports, {
33
33
  BoltTable: () => BoltTable,
34
34
  DraggableHeader: () => DraggableHeader_default,
35
35
  ResizeOverlay: () => ResizeOverlay_default,
36
- TableBody: () => TableBody_default
36
+ TableBody: () => TableBody_default,
37
+ defineConfig: () => defineConfig
37
38
  });
38
39
  module.exports = __toCommonJS(index_exports);
39
40
 
@@ -136,6 +137,18 @@ var XIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx
136
137
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18" }),
137
138
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 6 12 12" })
138
139
  ] });
140
+ var SparklesIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }),
142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 3v4" }),
143
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 17v4" }),
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3 5h4" }),
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 19h4" })
146
+ ] });
147
+ var SendIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { ...svgBase, style, className, children: [
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m22 2-7 20-4-9-9-4Z" }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M22 2 11 13" })
150
+ ] });
151
+ var LoaderIcon = ({ style, className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { ...svgBase, style, className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
139
152
 
140
153
  // src/DraggableHeader.tsx
141
154
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -623,40 +636,44 @@ var DraggableHeader = import_react.default.memo(
623
636
  ] }),
624
637
  customContextMenuItems && customContextMenuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
625
638
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
626
- customContextMenuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
627
- "button",
628
- {
629
- type: "button",
630
- "data-bt-ctx-item": "",
631
- disabled: item.disabled,
632
- style: {
633
- display: "flex",
634
- width: "100%",
635
- alignItems: "center",
636
- gap: 8,
637
- paddingLeft: 12,
638
- paddingRight: 12,
639
- paddingTop: 6,
640
- paddingBottom: 6,
641
- textAlign: "left",
642
- background: "none",
643
- border: "none",
644
- fontSize: "inherit",
645
- cursor: item.disabled ? "not-allowed" : "pointer",
646
- opacity: item.disabled ? 0.5 : 1,
647
- color: item.danger ? "#ef4444" : "inherit"
648
- },
649
- onClick: () => {
650
- item.onClick(column.key);
651
- setContextMenu(null);
639
+ customContextMenuItems.map((item) => {
640
+ const resolvedIcon = typeof item.icon === "function" ? item.icon(column.key) : item.icon;
641
+ const resolvedLabel = typeof item.label === "function" ? item.label(column.key) : item.label;
642
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
643
+ "button",
644
+ {
645
+ type: "button",
646
+ "data-bt-ctx-item": "",
647
+ disabled: item.disabled,
648
+ style: {
649
+ display: "flex",
650
+ width: "100%",
651
+ alignItems: "center",
652
+ gap: 8,
653
+ paddingLeft: 12,
654
+ paddingRight: 12,
655
+ paddingTop: 6,
656
+ paddingBottom: 6,
657
+ textAlign: "left",
658
+ background: "none",
659
+ border: "none",
660
+ fontSize: "inherit",
661
+ cursor: item.disabled ? "not-allowed" : "pointer",
662
+ opacity: item.disabled ? 0.5 : 1,
663
+ color: item.danger ? "#ef4444" : "inherit"
664
+ },
665
+ onClick: () => {
666
+ item.onClick?.(column.key);
667
+ setContextMenu(null);
668
+ },
669
+ children: [
670
+ resolvedIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: resolvedIcon }),
671
+ resolvedLabel
672
+ ]
652
673
  },
653
- children: [
654
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: item.icon }),
655
- item.label
656
- ]
657
- },
658
- item.key
659
- ))
674
+ item.key
675
+ );
676
+ })
660
677
  ] })
661
678
  ] });
662
679
  })()
@@ -673,6 +690,280 @@ var DraggableHeader = import_react.default.memo(
673
690
  DraggableHeader.displayName = "DraggableHeader";
674
691
  var DraggableHeader_default = DraggableHeader;
675
692
 
693
+ // src/ai.ts
694
+ function detectColumnType(key, data) {
695
+ const values = [];
696
+ for (let i = 0; i < Math.min(data.length, 20); i++) {
697
+ const v = data[i]?.[key];
698
+ if (v != null) values.push(v);
699
+ }
700
+ if (values.length === 0) return { type: "unknown", sample: "" };
701
+ const allNumbers = values.every((v) => typeof v === "number");
702
+ const allBooleans = values.every((v) => typeof v === "boolean");
703
+ const uniqueVals = [...new Set(values.map(String))];
704
+ const sampleStr = uniqueVals.length <= 8 ? uniqueVals.join(", ") : uniqueVals.slice(0, 6).join(", ") + "...";
705
+ if (allBooleans) return { type: "boolean", sample: sampleStr };
706
+ if (allNumbers) {
707
+ const nums = values;
708
+ const min = Math.min(...nums);
709
+ const max = Math.max(...nums);
710
+ return { type: "number", sample: `range ${min}\u2013${max}` };
711
+ }
712
+ return { type: "string", sample: sampleStr };
713
+ }
714
+ function buildSystemPrompt(columns, data) {
715
+ const schemaLines = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => {
716
+ const key = c.dataIndex ?? c.key;
717
+ const title = typeof c.title === "string" ? c.title : c.key;
718
+ const info = detectColumnType(key, data);
719
+ return ` - key: "${c.key}", title: "${title}", dataIndex: "${key}", type: ${info.type}${info.sample ? ` (values: ${info.sample})` : ""}`;
720
+ }).join("\n");
721
+ const sample = data.slice(0, 5).map((row) => {
722
+ const obj = {};
723
+ for (const col of columns) {
724
+ if (col.key === "__select__" || col.key === "__expand__") continue;
725
+ const di = col.dataIndex ?? col.key;
726
+ obj[di] = row[di];
727
+ }
728
+ return obj;
729
+ });
730
+ return `You are a data table assistant. You help users query, filter, sort, and style tabular data.
731
+ You MUST respond with ONLY a valid JSON object \u2014 no markdown fences, no explanation, no extra text.
732
+
733
+ ## Table Schema
734
+ ${schemaLines}
735
+
736
+ ## Sample Data (first ${sample.length} of ${data.length} rows)
737
+ ${JSON.stringify(sample, null, 2)}
738
+
739
+ ## Available Operations
740
+ Combine any of these in a single response:
741
+
742
+ 1. **filter** \u2014 show only rows matching conditions
743
+ { "type": "filter", "conditions": [{ "column": "<dataIndex>", "op": "<op>", "value": <val> }], "logic": "and" | "or" }
744
+
745
+ 2. **rowStyle** \u2014 apply CSS styles to entire rows matching conditions
746
+ { "type": "rowStyle", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
747
+
748
+ 3. **cellStyle** \u2014 apply CSS styles to a specific column's cells matching conditions
749
+ { "type": "cellStyle", "column": "<dataIndex>", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
750
+
751
+ 4. **sort** \u2014 sort data by a column
752
+ { "type": "sort", "column": "<dataIndex>", "direction": "asc" | "desc" }
753
+
754
+ 5. **hideColumns** / **showColumns** \u2014 toggle column visibility
755
+ { "type": "hideColumns" | "showColumns", "columns": ["<key>", ...] }
756
+
757
+ ## Operators
758
+ eq, neq, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn
759
+
760
+ ## Response format
761
+ {
762
+ "operations": [ ... ],
763
+ "message": "Brief user-friendly description of what was applied"
764
+ }
765
+
766
+ ## Rules
767
+ - Use the dataIndex values from the schema, not display titles.
768
+ - For colors use semi-transparent values like "rgba(255,0,0,0.15)" so text stays readable.
769
+ - CSS property names must be camelCase (e.g. "backgroundColor", "color", "fontWeight").
770
+ - If the user asks to highlight / color / mark specific rows, use rowStyle or cellStyle.
771
+ - If the user asks to show only certain rows, use filter.
772
+ - You can combine filter + rowStyle + sort etc. in one response.
773
+ - The "message" should be concise: what was done, in plain English.`;
774
+ }
775
+ async function callAI(config, systemPrompt, userQuery) {
776
+ const { provider, apiKey, model, baseUrl, maxTokens = 1024, temperature = 0.1 } = config;
777
+ if (provider === "openai" || provider === "custom") {
778
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/chat/completions` : "https://api.openai.com/v1/chat/completions";
779
+ const res = await fetch(url, {
780
+ method: "POST",
781
+ headers: {
782
+ "Content-Type": "application/json",
783
+ Authorization: `Bearer ${apiKey}`
784
+ },
785
+ body: JSON.stringify({
786
+ model: model ?? "gpt-4o-mini",
787
+ messages: [
788
+ { role: "system", content: systemPrompt },
789
+ { role: "user", content: userQuery }
790
+ ],
791
+ max_tokens: maxTokens,
792
+ temperature
793
+ })
794
+ });
795
+ if (!res.ok) {
796
+ const body = await res.text().catch(() => "");
797
+ throw new Error(`AI request failed (${res.status}): ${body}`);
798
+ }
799
+ const json = await res.json();
800
+ return json.choices?.[0]?.message?.content ?? "";
801
+ }
802
+ if (provider === "anthropic") {
803
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/messages` : "https://api.anthropic.com/v1/messages";
804
+ const res = await fetch(url, {
805
+ method: "POST",
806
+ headers: {
807
+ "Content-Type": "application/json",
808
+ "x-api-key": apiKey,
809
+ "anthropic-version": "2023-06-01",
810
+ "anthropic-dangerous-direct-browser-access": "true"
811
+ },
812
+ body: JSON.stringify({
813
+ model: model ?? "claude-sonnet-4-20250514",
814
+ system: systemPrompt,
815
+ messages: [{ role: "user", content: userQuery }],
816
+ max_tokens: maxTokens,
817
+ temperature
818
+ })
819
+ });
820
+ if (!res.ok) {
821
+ const body = await res.text().catch(() => "");
822
+ throw new Error(`AI request failed (${res.status}): ${body}`);
823
+ }
824
+ const json = await res.json();
825
+ const textBlock = json.content?.find(
826
+ (b) => b.type === "text"
827
+ );
828
+ return textBlock?.text ?? "";
829
+ }
830
+ throw new Error(`Unsupported AI provider: ${provider}`);
831
+ }
832
+ function parseAIResponse(raw) {
833
+ let text = raw.trim();
834
+ const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
835
+ if (fenceMatch) text = fenceMatch[1].trim();
836
+ const start = text.indexOf("{");
837
+ const end = text.lastIndexOf("}");
838
+ if (start !== -1 && end > start) {
839
+ text = text.slice(start, end + 1);
840
+ }
841
+ const parsed = JSON.parse(text);
842
+ if (!parsed.operations || !Array.isArray(parsed.operations)) {
843
+ throw new Error("Invalid AI response: missing operations array");
844
+ }
845
+ return {
846
+ operations: parsed.operations,
847
+ message: parsed.message ?? "AI operations applied."
848
+ };
849
+ }
850
+ function evaluateCondition(condition, row) {
851
+ const rawVal = row[condition.column];
852
+ const target = condition.value;
853
+ switch (condition.op) {
854
+ case "eq":
855
+ return rawVal == target;
856
+ case "neq":
857
+ return rawVal != target;
858
+ case "gt":
859
+ return Number(rawVal) > Number(target);
860
+ case "gte":
861
+ return Number(rawVal) >= Number(target);
862
+ case "lt":
863
+ return Number(rawVal) < Number(target);
864
+ case "lte":
865
+ return Number(rawVal) <= Number(target);
866
+ case "contains":
867
+ return String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
868
+ case "notContains":
869
+ return !String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
870
+ case "startsWith":
871
+ return String(rawVal ?? "").toLowerCase().startsWith(String(target).toLowerCase());
872
+ case "endsWith":
873
+ return String(rawVal ?? "").toLowerCase().endsWith(String(target).toLowerCase());
874
+ case "in":
875
+ if (Array.isArray(target)) {
876
+ return target.some((t) => rawVal == t);
877
+ }
878
+ return false;
879
+ case "notIn":
880
+ if (Array.isArray(target)) {
881
+ return !target.some((t) => rawVal == t);
882
+ }
883
+ return true;
884
+ default:
885
+ return true;
886
+ }
887
+ }
888
+ function matchesConditions(conditions, logic, row) {
889
+ if (!conditions || conditions.length === 0) return true;
890
+ if (logic === "or") {
891
+ return conditions.some((c) => evaluateCondition(c, row));
892
+ }
893
+ return conditions.every((c) => evaluateCondition(c, row));
894
+ }
895
+ function applyAIFilter(data, op) {
896
+ return data.filter((row) => matchesConditions(op.conditions, op.logic, row));
897
+ }
898
+ function applyAISort(data, op) {
899
+ const dir = op.direction === "asc" ? 1 : -1;
900
+ const col = op.column;
901
+ return [...data].sort((a, b) => {
902
+ const aVal = a[col];
903
+ const bVal = b[col];
904
+ if (aVal == null && bVal == null) return 0;
905
+ if (aVal == null) return 1;
906
+ if (bVal == null) return -1;
907
+ if (typeof aVal === "number" && typeof bVal === "number")
908
+ return (aVal - bVal) * dir;
909
+ return String(aVal).localeCompare(String(bVal)) * dir;
910
+ });
911
+ }
912
+ function getAIRowStyle(row, ops) {
913
+ let merged;
914
+ for (const op of ops) {
915
+ if (matchesConditions(op.conditions, op.logic, row)) {
916
+ if (!merged) merged = {};
917
+ Object.assign(merged, op.style);
918
+ }
919
+ }
920
+ return merged;
921
+ }
922
+ function getAICellStyle(row, columnKey, ops) {
923
+ let merged;
924
+ for (const op of ops) {
925
+ if (op.column === columnKey && matchesConditions(op.conditions, op.logic, row)) {
926
+ if (!merged) merged = {};
927
+ Object.assign(merged, op.style);
928
+ }
929
+ }
930
+ return merged;
931
+ }
932
+ function applyAIOperations(data, operations) {
933
+ let filteredData = data;
934
+ let sortOp = null;
935
+ const styleOps = [];
936
+ const cellStyleOps = [];
937
+ const hideColumns = [];
938
+ const showColumns = [];
939
+ for (const op of operations) {
940
+ switch (op.type) {
941
+ case "filter":
942
+ filteredData = applyAIFilter(filteredData, op);
943
+ break;
944
+ case "sort":
945
+ sortOp = op;
946
+ break;
947
+ case "rowStyle":
948
+ styleOps.push(op);
949
+ break;
950
+ case "cellStyle":
951
+ cellStyleOps.push(op);
952
+ break;
953
+ case "hideColumns":
954
+ hideColumns.push(...op.columns);
955
+ break;
956
+ case "showColumns":
957
+ showColumns.push(...op.columns);
958
+ break;
959
+ }
960
+ }
961
+ if (sortOp) {
962
+ filteredData = applyAISort(filteredData, sortOp);
963
+ }
964
+ return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns };
965
+ }
966
+
676
967
  // src/ResizeOverlay.tsx
677
968
  var import_react2 = require("react");
678
969
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -894,9 +1185,11 @@ var Cell = import_react3.default.memo(
894
1185
  isLoading,
895
1186
  onEdit,
896
1187
  isEditing,
897
- onEditComplete
1188
+ onEditComplete,
1189
+ cellStyleFn
898
1190
  }) => {
899
1191
  const isPinned = Boolean(column.pinned);
1192
+ const extraCellStyle = cellStyleFn?.(record, column.dataIndex ?? column.key);
900
1193
  if (isLoading && column.key !== "__select__" && column.key !== "__expand__") {
901
1194
  const shimmerContent = column.shimmerRender ? column.shimmerRender() : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
902
1195
  "div",
@@ -1048,7 +1341,8 @@ var Cell = import_react3.default.memo(
1048
1341
  minWidth: 0,
1049
1342
  ...column.style,
1050
1343
  ...isPinned ? styles?.pinnedCell : void 0,
1051
- ...styles?.cell
1344
+ ...styles?.cell,
1345
+ ...extraCellStyle
1052
1346
  },
1053
1347
  children: isSystem ? content : showEditor ? content : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1054
1348
  "div",
@@ -1075,6 +1369,7 @@ var Cell = import_react3.default.memo(
1075
1369
  if (prev.onEdit !== next.onEdit) return false;
1076
1370
  if (prev.isEditing !== next.isEditing) return false;
1077
1371
  if (prev.onEditComplete !== next.onEditComplete) return false;
1372
+ if (prev.cellStyleFn !== next.cellStyleFn) return false;
1078
1373
  if (prev.column.key === "__select__") {
1079
1374
  return prev.isSelected === next.isSelected && prev.normalizedSelectedKeys === next.normalizedSelectedKeys;
1080
1375
  }
@@ -1176,7 +1471,8 @@ var TableBody = ({
1176
1471
  onEditComplete,
1177
1472
  enableDynamicRowHeight = false,
1178
1473
  onRowHeightChange,
1179
- columnGridIndexMap
1474
+ columnGridIndexMap,
1475
+ cellStyleFn
1180
1476
  }) => {
1181
1477
  const virtualItems = rowVirtualizer.getVirtualItems();
1182
1478
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1305,7 +1601,8 @@ var TableBody = ({
1305
1601
  recordFingerprint,
1306
1602
  onEdit,
1307
1603
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1308
- onEditComplete
1604
+ onEditComplete,
1605
+ cellStyleFn
1309
1606
  }
1310
1607
  )
1311
1608
  }
@@ -1342,7 +1639,8 @@ var TableBody = ({
1342
1639
  recordFingerprint,
1343
1640
  onEdit,
1344
1641
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1345
- onEditComplete
1642
+ onEditComplete,
1643
+ cellStyleFn
1346
1644
  }
1347
1645
  )
1348
1646
  }
@@ -1532,7 +1830,8 @@ var TableBody = ({
1532
1830
  recordFingerprint,
1533
1831
  onEdit,
1534
1832
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1535
- onEditComplete
1833
+ onEditComplete,
1834
+ cellStyleFn
1536
1835
  }
1537
1836
  )
1538
1837
  }
@@ -1660,7 +1959,8 @@ var TableBody = ({
1660
1959
  recordFingerprint,
1661
1960
  onEdit,
1662
1961
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1663
- onEditComplete
1962
+ onEditComplete,
1963
+ cellStyleFn
1664
1964
  }
1665
1965
  )
1666
1966
  }
@@ -1755,7 +2055,13 @@ function BoltTable({
1755
2055
  globalSearchValue,
1756
2056
  onGlobalSearchChange,
1757
2057
  toolbarContent,
1758
- columnSettingsLabel
2058
+ columnSettingsLabel,
2059
+ aiMode = false,
2060
+ aiConfig,
2061
+ onAIQuery,
2062
+ onAIResponse,
2063
+ aiPlaceholder = "Ask AI anything about your data...",
2064
+ aiButtonLabel
1759
2065
  }) {
1760
2066
  const data = (0, import_react4.useMemo)(() => {
1761
2067
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -1929,6 +2235,96 @@ function BoltTable({
1929
2235
  document.removeEventListener("keydown", onKey);
1930
2236
  };
1931
2237
  }, [showColumnPicker]);
2238
+ const [aiBarOpen, setAiBarOpen] = (0, import_react4.useState)(false);
2239
+ const [aiQuery, setAiQuery] = (0, import_react4.useState)("");
2240
+ const [aiLoading, setAiLoading] = (0, import_react4.useState)(false);
2241
+ const [aiResult, setAiResult] = (0, import_react4.useState)(null);
2242
+ const [aiError, setAiError] = (0, import_react4.useState)(null);
2243
+ const aiInputRef = (0, import_react4.useRef)(null);
2244
+ const [aiStyleOps, setAiStyleOps] = (0, import_react4.useState)([]);
2245
+ const [aiCellStyleOps, setAiCellStyleOps] = (0, import_react4.useState)(
2246
+ []
2247
+ );
2248
+ const [aiFilteredDataKeys, setAiFilteredDataKeys] = (0, import_react4.useState)(null);
2249
+ const [aiSortKey, setAiSortKey] = (0, import_react4.useState)(null);
2250
+ const [aiSortDir, setAiSortDir] = (0, import_react4.useState)(null);
2251
+ const onAIResponseRef = (0, import_react4.useRef)(onAIResponse);
2252
+ onAIResponseRef.current = onAIResponse;
2253
+ const handleAISubmit = (0, import_react4.useCallback)(async () => {
2254
+ const query = aiQuery.trim();
2255
+ if (!query) return;
2256
+ setAiLoading(true);
2257
+ setAiError(null);
2258
+ try {
2259
+ let response;
2260
+ if (onAIQuery) {
2261
+ response = await onAIQuery(query, {
2262
+ data,
2263
+ columns: initialColumns
2264
+ });
2265
+ } else if (aiConfig) {
2266
+ const sysPrompt = buildSystemPrompt(initialColumns, data);
2267
+ const raw = await callAI(aiConfig, sysPrompt, query);
2268
+ response = parseAIResponse(raw);
2269
+ } else {
2270
+ throw new Error("AI mode requires either aiConfig or onAIQuery prop");
2271
+ }
2272
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns, showColumns } = applyAIOperations(data, response.operations);
2273
+ setAiStyleOps(sOps);
2274
+ setAiCellStyleOps(csOps);
2275
+ if (response.operations.some((op) => op.type === "filter")) {
2276
+ const keySet = /* @__PURE__ */ new Set();
2277
+ filteredData.forEach((row, idx) => {
2278
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2279
+ keySet.add(k);
2280
+ });
2281
+ setAiFilteredDataKeys(keySet);
2282
+ } else {
2283
+ setAiFilteredDataKeys(null);
2284
+ }
2285
+ if (sortOp) {
2286
+ setAiSortKey(sortOp.column);
2287
+ setAiSortDir(sortOp.direction);
2288
+ } else {
2289
+ setAiSortKey(null);
2290
+ setAiSortDir(null);
2291
+ }
2292
+ if (hideColumns.length > 0 || showColumns.length > 0) {
2293
+ setColumns(
2294
+ (prev) => prev.map((col) => {
2295
+ if (hideColumns.includes(col.key)) return { ...col, hidden: true };
2296
+ if (showColumns.includes(col.key)) return { ...col, hidden: false };
2297
+ return col;
2298
+ })
2299
+ );
2300
+ }
2301
+ setAiResult(response);
2302
+ onAIResponseRef.current?.(response);
2303
+ } catch (err) {
2304
+ setAiError(err instanceof Error ? err.message : "AI query failed");
2305
+ } finally {
2306
+ setAiLoading(false);
2307
+ }
2308
+ }, [aiQuery, aiConfig, onAIQuery, data, initialColumns, rowKey]);
2309
+ const handleAIClear = (0, import_react4.useCallback)(() => {
2310
+ setAiResult(null);
2311
+ setAiError(null);
2312
+ setAiStyleOps([]);
2313
+ setAiCellStyleOps([]);
2314
+ setAiFilteredDataKeys(null);
2315
+ setAiSortKey(null);
2316
+ setAiSortDir(null);
2317
+ setAiQuery("");
2318
+ }, []);
2319
+ const handleAIBarClose = (0, import_react4.useCallback)(() => {
2320
+ setAiBarOpen(false);
2321
+ handleAIClear();
2322
+ }, [handleAIClear]);
2323
+ import_react4.default.useEffect(() => {
2324
+ if (aiBarOpen && aiInputRef.current) {
2325
+ setTimeout(() => aiInputRef.current?.focus(), 300);
2326
+ }
2327
+ }, [aiBarOpen]);
1932
2328
  const columnsWithPersistedWidths = (0, import_react4.useMemo)(
1933
2329
  () => columns.map((col) => ({
1934
2330
  ...col,
@@ -2570,6 +2966,47 @@ function BoltTable({
2570
2966
  }
2571
2967
  return result;
2572
2968
  }, [data, sortState, columnFilters, globalSearchValue, internalGlobalSearch]);
2969
+ const aiProcessedData = (0, import_react4.useMemo)(() => {
2970
+ let result = processedData;
2971
+ if (aiFilteredDataKeys) {
2972
+ result = result.filter((row, idx) => {
2973
+ if (row == null) return false;
2974
+ const k = typeof rowKey === "function" ? rowKey(row) : String(
2975
+ row[typeof rowKey === "string" ? rowKey : "id"] ?? idx
2976
+ );
2977
+ return aiFilteredDataKeys.has(k);
2978
+ });
2979
+ }
2980
+ if (aiSortKey && aiSortDir) {
2981
+ const dir = aiSortDir === "asc" ? 1 : -1;
2982
+ const col = aiSortKey;
2983
+ result = [...result].sort((a, b) => {
2984
+ const aVal = a[col];
2985
+ const bVal = b[col];
2986
+ if (aVal == null && bVal == null) return 0;
2987
+ if (aVal == null) return 1;
2988
+ if (bVal == null) return -1;
2989
+ if (typeof aVal === "number" && typeof bVal === "number")
2990
+ return (aVal - bVal) * dir;
2991
+ return String(aVal).localeCompare(String(bVal)) * dir;
2992
+ });
2993
+ }
2994
+ return result;
2995
+ }, [processedData, aiFilteredDataKeys, aiSortKey, aiSortDir, rowKey]);
2996
+ const getAIRowStyleForRecord = (0, import_react4.useCallback)(
2997
+ (record) => {
2998
+ if (aiStyleOps.length === 0) return void 0;
2999
+ return getAIRowStyle(record, aiStyleOps);
3000
+ },
3001
+ [aiStyleOps]
3002
+ );
3003
+ const getAICellStyleForRecord = (0, import_react4.useCallback)(
3004
+ (record, columnKey) => {
3005
+ if (aiCellStyleOps.length === 0) return void 0;
3006
+ return getAICellStyle(record, columnKey, aiCellStyleOps);
3007
+ },
3008
+ [aiCellStyleOps]
3009
+ );
2573
3010
  const pinnedRowCacheRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
2574
3011
  const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = (0, import_react4.useMemo)(() => {
2575
3012
  if (!resolvedRowPinning || !resolvedRowPinning.top?.length && !resolvedRowPinning.bottom?.length) {
@@ -2577,7 +3014,7 @@ function BoltTable({
2577
3014
  return {
2578
3015
  pinnedTopRows: [],
2579
3016
  pinnedBottomRows: [],
2580
- unpinnedProcessedData: processedData
3017
+ unpinnedProcessedData: aiProcessedData
2581
3018
  };
2582
3019
  }
2583
3020
  const topKeySet = new Set((resolvedRowPinning.top ?? []).map(String));
@@ -2587,7 +3024,7 @@ function BoltTable({
2587
3024
  const topMap = /* @__PURE__ */ new Map();
2588
3025
  const bottomMap = /* @__PURE__ */ new Map();
2589
3026
  const rest = [];
2590
- processedData.forEach((row, idx) => {
3027
+ aiProcessedData.forEach((row, idx) => {
2591
3028
  if (row == null) return;
2592
3029
  const key = getSafeRowKey(row, idx);
2593
3030
  if (topKeySet.has(key)) {
@@ -2627,7 +3064,7 @@ function BoltTable({
2627
3064
  unpinnedProcessedData: rest
2628
3065
  };
2629
3066
  }, [
2630
- processedData,
3067
+ aiProcessedData,
2631
3068
  resolvedRowPinning,
2632
3069
  getSafeRowKey,
2633
3070
  keepPinnedRowsAcrossPages
@@ -2696,7 +3133,7 @@ function BoltTable({
2696
3133
  return unpinnedProcessedData.slice(start, start + pgSize);
2697
3134
  }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
2698
3135
  const shimmerCount = pgEnabled ? pgSize : 15;
2699
- const showShimmer = isLoading && processedData.length === 0;
3136
+ const showShimmer = isLoading && aiProcessedData.length === 0;
2700
3137
  const shimmerRowKeyField = typeof rowKey === "string" ? rowKey : "id";
2701
3138
  const shimmerData = (0, import_react4.useMemo)(() => {
2702
3139
  if (!showShimmer) return null;
@@ -2980,49 +3417,291 @@ function BoltTable({
2980
3417
  border: 1px dashed ${accentColor} !important;
2981
3418
  }
2982
3419
  ${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
3420
+ @keyframes bt-spin { to { transform: rotate(360deg); } }
3421
+ @keyframes bt-ai-shimmer {
3422
+ 0% { background-position: -200% 0; }
3423
+ 100% { background-position: 200% 0; }
3424
+ }
2983
3425
  ` }),
2984
- (!hideGlobalSearch || showColumnSettings) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3426
+ (!hideGlobalSearch || showColumnSettings || aiMode) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2985
3427
  "div",
2986
3428
  {
2987
3429
  style: {
3430
+ position: "relative",
2988
3431
  display: "flex",
2989
3432
  alignItems: "center",
2990
3433
  gap: 8,
2991
3434
  padding: "6px 8px",
2992
3435
  borderBottom: "1px solid rgba(128,128,128,0.2)",
2993
3436
  fontSize: 12,
2994
- flexShrink: 0
3437
+ flexShrink: 0,
3438
+ overflow: "hidden"
2995
3439
  },
2996
3440
  children: [
2997
- !hideGlobalSearch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3441
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2998
3442
  "div",
2999
3443
  {
3000
3444
  style: {
3001
3445
  display: "flex",
3002
3446
  alignItems: "center",
3003
- gap: 4,
3447
+ gap: 8,
3004
3448
  flex: "1 1 0%",
3005
- position: "relative"
3449
+ opacity: aiBarOpen ? 0 : 1,
3450
+ transform: aiBarOpen ? "scale(0.97)" : "scale(1)",
3451
+ transition: "opacity 0.25s ease, transform 0.25s ease",
3452
+ pointerEvents: aiBarOpen ? "none" : "auto",
3453
+ minWidth: 0
3454
+ },
3455
+ children: [
3456
+ !hideGlobalSearch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3457
+ "div",
3458
+ {
3459
+ style: {
3460
+ display: "flex",
3461
+ alignItems: "center",
3462
+ gap: 4,
3463
+ flex: "1 1 0%",
3464
+ position: "relative"
3465
+ },
3466
+ children: [
3467
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3468
+ "span",
3469
+ {
3470
+ style: {
3471
+ display: "flex",
3472
+ color: "GrayText",
3473
+ flexShrink: 0
3474
+ },
3475
+ children: icons?.search ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchIcon, { style: { width: 14, height: 14 } })
3476
+ }
3477
+ ),
3478
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3479
+ "input",
3480
+ {
3481
+ type: "text",
3482
+ placeholder: "Search all columns...",
3483
+ value: globalSearchValue ?? internalGlobalSearch,
3484
+ onChange: (e) => {
3485
+ const v = e.target.value;
3486
+ if (onGlobalSearchChange) onGlobalSearchChange(v);
3487
+ else setInternalGlobalSearch(v);
3488
+ },
3489
+ style: {
3490
+ flex: "1 1 0%",
3491
+ border: "none",
3492
+ outline: "none",
3493
+ background: "transparent",
3494
+ font: "inherit",
3495
+ color: "inherit",
3496
+ padding: "4px 6px",
3497
+ minWidth: 0
3498
+ }
3499
+ }
3500
+ ),
3501
+ (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3502
+ "button",
3503
+ {
3504
+ type: "button",
3505
+ onClick: () => {
3506
+ if (onGlobalSearchChange) onGlobalSearchChange("");
3507
+ else setInternalGlobalSearch("");
3508
+ },
3509
+ style: {
3510
+ display: "flex",
3511
+ alignItems: "center",
3512
+ justifyContent: "center",
3513
+ background: "none",
3514
+ border: "none",
3515
+ cursor: "pointer",
3516
+ padding: 2,
3517
+ color: "GrayText",
3518
+ flexShrink: 0
3519
+ },
3520
+ children: icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 12, height: 12 } })
3521
+ }
3522
+ )
3523
+ ]
3524
+ }
3525
+ ),
3526
+ toolbarContent,
3527
+ showColumnSettings && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", flexShrink: 0 }, children: [
3528
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3529
+ "button",
3530
+ {
3531
+ type: "button",
3532
+ onClick: () => setShowColumnPicker((p) => !p),
3533
+ style: {
3534
+ display: "flex",
3535
+ alignItems: "center",
3536
+ justifyContent: "center",
3537
+ background: "none",
3538
+ border: "1px solid rgba(128,128,128,0.2)",
3539
+ borderRadius: 4,
3540
+ cursor: "pointer",
3541
+ padding: "4px 6px",
3542
+ color: "inherit",
3543
+ gap: 4,
3544
+ fontSize: 12
3545
+ },
3546
+ title: "Column settings",
3547
+ children: [
3548
+ icons?.columns ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ColumnsIcon, { style: { width: 14, height: 14 } }),
3549
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columnSettingsLabel ?? "Columns" })
3550
+ ]
3551
+ }
3552
+ ),
3553
+ showColumnPicker && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3554
+ "div",
3555
+ {
3556
+ ref: columnPickerRef,
3557
+ style: {
3558
+ position: "absolute",
3559
+ top: "100%",
3560
+ right: 0,
3561
+ zIndex: 99999,
3562
+ minWidth: 200,
3563
+ maxHeight: 320,
3564
+ overflowY: "auto",
3565
+ borderRadius: 8,
3566
+ border: "1px solid rgba(128,128,128,0.2)",
3567
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3568
+ backdropFilter: "blur(16px)",
3569
+ WebkitBackdropFilter: "blur(16px)",
3570
+ backgroundColor: "rgba(128,128,128,0.08)",
3571
+ padding: "4px 0",
3572
+ marginTop: 4
3573
+ },
3574
+ children: initialColumns.filter(
3575
+ (c) => c.key !== "__select__" && c.key !== "__expand__"
3576
+ ).map((col) => {
3577
+ const current = columns.find(
3578
+ (c) => c.key === col.key
3579
+ );
3580
+ const isHidden = current?.hidden ?? false;
3581
+ const isPinned = !!current?.pinned;
3582
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3583
+ "label",
3584
+ {
3585
+ style: {
3586
+ display: "flex",
3587
+ alignItems: "center",
3588
+ gap: 8,
3589
+ padding: "6px 12px",
3590
+ cursor: isPinned ? "not-allowed" : "pointer",
3591
+ opacity: isPinned ? 0.5 : 1,
3592
+ fontSize: 12
3593
+ },
3594
+ children: [
3595
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3596
+ "input",
3597
+ {
3598
+ type: "checkbox",
3599
+ checked: !isHidden,
3600
+ disabled: isPinned,
3601
+ onChange: () => {
3602
+ if (isPinned) return;
3603
+ handleToggleHide(col.key);
3604
+ },
3605
+ style: {
3606
+ cursor: isPinned ? "not-allowed" : "pointer",
3607
+ accentColor
3608
+ }
3609
+ }
3610
+ ),
3611
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3612
+ "span",
3613
+ {
3614
+ style: {
3615
+ overflow: "hidden",
3616
+ textOverflow: "ellipsis",
3617
+ whiteSpace: "nowrap"
3618
+ },
3619
+ children: typeof col.title === "string" ? col.title : col.key
3620
+ }
3621
+ )
3622
+ ]
3623
+ },
3624
+ col.key
3625
+ );
3626
+ })
3627
+ }
3628
+ )
3629
+ ] }),
3630
+ aiMode && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3631
+ "button",
3632
+ {
3633
+ type: "button",
3634
+ onClick: () => setAiBarOpen(true),
3635
+ style: {
3636
+ display: "flex",
3637
+ alignItems: "center",
3638
+ justifyContent: "center",
3639
+ gap: 4,
3640
+ background: `linear-gradient(135deg, ${accentColor}18, ${accentColor}08)`,
3641
+ border: `1px solid ${accentColor}40`,
3642
+ borderRadius: 4,
3643
+ cursor: "pointer",
3644
+ padding: "4px 8px",
3645
+ color: accentColor,
3646
+ fontSize: 12,
3647
+ fontWeight: 500,
3648
+ flexShrink: 0,
3649
+ transition: "all 0.2s ease"
3650
+ },
3651
+ title: "Ask AI",
3652
+ children: [
3653
+ icons?.sparkles ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparklesIcon, { style: { width: 14, height: 14 } }),
3654
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: aiButtonLabel ?? "Ask AI" })
3655
+ ]
3656
+ }
3657
+ )
3658
+ ]
3659
+ }
3660
+ ),
3661
+ aiMode && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3662
+ "div",
3663
+ {
3664
+ style: {
3665
+ position: "absolute",
3666
+ inset: 0,
3667
+ display: "flex",
3668
+ alignItems: "center",
3669
+ gap: 8,
3670
+ padding: "4px 8px",
3671
+ opacity: aiBarOpen ? 1 : 0,
3672
+ transform: aiBarOpen ? "translateX(0)" : "translateX(40px)",
3673
+ transition: "opacity 0.3s cubic-bezier(0.4,0,0.2,1), transform 0.3s cubic-bezier(0.4,0,0.2,1)",
3674
+ pointerEvents: aiBarOpen ? "auto" : "none",
3675
+ zIndex: 2,
3676
+ background: "inherit",
3677
+ backdropFilter: "blur(12px)",
3678
+ WebkitBackdropFilter: "blur(12px)"
3006
3679
  },
3007
3680
  children: [
3008
3681
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3009
3682
  "span",
3010
3683
  {
3011
- style: { display: "flex", color: "GrayText", flexShrink: 0 },
3012
- children: icons?.search ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchIcon, { style: { width: 14, height: 14 } })
3684
+ style: {
3685
+ display: "flex",
3686
+ color: accentColor,
3687
+ flexShrink: 0
3688
+ },
3689
+ children: icons?.sparkles ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparklesIcon, { style: { width: 16, height: 16 } })
3013
3690
  }
3014
3691
  ),
3015
3692
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3016
3693
  "input",
3017
3694
  {
3695
+ ref: aiInputRef,
3018
3696
  type: "text",
3019
- placeholder: "Search all columns...",
3020
- value: globalSearchValue ?? internalGlobalSearch,
3021
- onChange: (e) => {
3022
- const v = e.target.value;
3023
- if (onGlobalSearchChange) onGlobalSearchChange(v);
3024
- else setInternalGlobalSearch(v);
3697
+ placeholder: aiPlaceholder,
3698
+ value: aiQuery,
3699
+ onChange: (e) => setAiQuery(e.target.value),
3700
+ onKeyDown: (e) => {
3701
+ if (e.key === "Enter" && !aiLoading) handleAISubmit();
3702
+ if (e.key === "Escape") handleAIBarClose();
3025
3703
  },
3704
+ disabled: aiLoading,
3026
3705
  style: {
3027
3706
  flex: "1 1 0%",
3028
3707
  border: "none",
@@ -3031,18 +3710,51 @@ function BoltTable({
3031
3710
  font: "inherit",
3032
3711
  color: "inherit",
3033
3712
  padding: "4px 6px",
3034
- minWidth: 0
3713
+ minWidth: 0,
3714
+ fontSize: 12
3035
3715
  }
3036
3716
  }
3037
3717
  ),
3038
- (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3039
3719
  "button",
3040
3720
  {
3041
3721
  type: "button",
3042
- onClick: () => {
3043
- if (onGlobalSearchChange) onGlobalSearchChange("");
3044
- else setInternalGlobalSearch("");
3722
+ onClick: handleAISubmit,
3723
+ disabled: aiLoading || !aiQuery.trim(),
3724
+ style: {
3725
+ display: "flex",
3726
+ alignItems: "center",
3727
+ justifyContent: "center",
3728
+ background: aiQuery.trim() ? accentColor : "rgba(128,128,128,0.15)",
3729
+ border: "none",
3730
+ borderRadius: 4,
3731
+ cursor: aiLoading || !aiQuery.trim() ? "not-allowed" : "pointer",
3732
+ padding: "4px 8px",
3733
+ color: aiQuery.trim() ? "#fff" : "GrayText",
3734
+ transition: "all 0.2s ease",
3735
+ flexShrink: 0,
3736
+ gap: 4,
3737
+ fontSize: 12,
3738
+ opacity: aiLoading ? 0.7 : 1
3045
3739
  },
3740
+ title: "Send",
3741
+ children: aiLoading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: icons?.loader ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3742
+ LoaderIcon,
3743
+ {
3744
+ style: {
3745
+ width: 14,
3746
+ height: 14,
3747
+ animation: "bt-spin 1s linear infinite"
3748
+ }
3749
+ }
3750
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: icons?.send ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SendIcon, { style: { width: 14, height: 14 } }) })
3751
+ }
3752
+ ),
3753
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3754
+ "button",
3755
+ {
3756
+ type: "button",
3757
+ onClick: handleAIBarClose,
3046
3758
  style: {
3047
3759
  display: "flex",
3048
3760
  alignItems: "center",
@@ -3054,114 +3766,96 @@ function BoltTable({
3054
3766
  color: "GrayText",
3055
3767
  flexShrink: 0
3056
3768
  },
3057
- children: icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 12, height: 12 } })
3769
+ title: "Close AI",
3770
+ children: icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 14, height: 14 } })
3058
3771
  }
3059
3772
  )
3060
3773
  ]
3061
3774
  }
3062
- ),
3063
- toolbarContent,
3064
- showColumnSettings && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", flexShrink: 0 }, children: [
3065
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3066
- "button",
3067
- {
3068
- type: "button",
3069
- onClick: () => setShowColumnPicker((p) => !p),
3070
- style: {
3071
- display: "flex",
3072
- alignItems: "center",
3073
- justifyContent: "center",
3074
- background: "none",
3075
- border: "1px solid rgba(128,128,128,0.2)",
3076
- borderRadius: 4,
3077
- cursor: "pointer",
3078
- padding: "4px 6px",
3079
- color: "inherit",
3080
- gap: 4,
3081
- fontSize: 12
3082
- },
3083
- title: "Column settings",
3084
- children: [
3085
- icons?.columns ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ColumnsIcon, { style: { width: 14, height: 14 } }),
3086
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columnSettingsLabel ?? "Columns" })
3087
- ]
3088
- }
3089
- ),
3090
- showColumnPicker && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3091
- "div",
3092
- {
3093
- ref: columnPickerRef,
3094
- style: {
3095
- position: "absolute",
3096
- top: "100%",
3097
- right: 0,
3098
- zIndex: 99999,
3099
- minWidth: 200,
3100
- maxHeight: 320,
3101
- overflowY: "auto",
3102
- borderRadius: 8,
3103
- border: "1px solid rgba(128,128,128,0.2)",
3104
- boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3105
- backdropFilter: "blur(16px)",
3106
- WebkitBackdropFilter: "blur(16px)",
3107
- backgroundColor: "rgba(128,128,128,0.08)",
3108
- padding: "4px 0",
3109
- marginTop: 4
3110
- },
3111
- children: initialColumns.filter(
3112
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3113
- ).map((col) => {
3114
- const current = columns.find((c) => c.key === col.key);
3115
- const isHidden = current?.hidden ?? false;
3116
- const isPinned = !!current?.pinned;
3117
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3118
- "label",
3119
- {
3120
- style: {
3121
- display: "flex",
3122
- alignItems: "center",
3123
- gap: 8,
3124
- padding: "6px 12px",
3125
- cursor: isPinned ? "not-allowed" : "pointer",
3126
- opacity: isPinned ? 0.5 : 1,
3127
- fontSize: 12
3128
- },
3129
- children: [
3130
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3131
- "input",
3132
- {
3133
- type: "checkbox",
3134
- checked: !isHidden,
3135
- disabled: isPinned,
3136
- onChange: () => {
3137
- if (isPinned) return;
3138
- handleToggleHide(col.key);
3139
- },
3140
- style: {
3141
- cursor: isPinned ? "not-allowed" : "pointer",
3142
- accentColor
3143
- }
3144
- }
3145
- ),
3146
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3147
- "span",
3148
- {
3149
- style: {
3150
- overflow: "hidden",
3151
- textOverflow: "ellipsis",
3152
- whiteSpace: "nowrap"
3153
- },
3154
- children: typeof col.title === "string" ? col.title : col.key
3155
- }
3156
- )
3157
- ]
3158
- },
3159
- col.key
3160
- );
3161
- })
3162
- }
3163
- )
3164
- ] })
3775
+ )
3776
+ ]
3777
+ }
3778
+ ),
3779
+ aiResult && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3780
+ "div",
3781
+ {
3782
+ style: {
3783
+ display: "flex",
3784
+ alignItems: "center",
3785
+ gap: 8,
3786
+ padding: "6px 12px",
3787
+ fontSize: 12,
3788
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
3789
+ background: `linear-gradient(90deg, ${accentColor}08, transparent)`,
3790
+ flexShrink: 0
3791
+ },
3792
+ children: [
3793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: accentColor, display: "flex", flexShrink: 0 }, children: icons?.sparkles ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SparklesIcon, { style: { width: 14, height: 14 } }) }),
3794
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { flex: "1 1 0%", opacity: 0.85 }, children: aiResult.message }),
3795
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3796
+ "button",
3797
+ {
3798
+ type: "button",
3799
+ onClick: handleAIClear,
3800
+ style: {
3801
+ display: "flex",
3802
+ alignItems: "center",
3803
+ gap: 4,
3804
+ background: "none",
3805
+ border: "1px solid rgba(128,128,128,0.2)",
3806
+ borderRadius: 4,
3807
+ cursor: "pointer",
3808
+ padding: "2px 8px",
3809
+ color: "inherit",
3810
+ fontSize: 11,
3811
+ flexShrink: 0,
3812
+ opacity: 0.7
3813
+ },
3814
+ title: "Clear AI results",
3815
+ children: [
3816
+ icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 10, height: 10 } }),
3817
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Clear" })
3818
+ ]
3819
+ }
3820
+ )
3821
+ ]
3822
+ }
3823
+ ),
3824
+ aiError && !aiResult && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3825
+ "div",
3826
+ {
3827
+ style: {
3828
+ display: "flex",
3829
+ alignItems: "center",
3830
+ gap: 8,
3831
+ padding: "6px 12px",
3832
+ fontSize: 12,
3833
+ borderBottom: "1px solid rgba(239,68,68,0.2)",
3834
+ background: "rgba(239,68,68,0.06)",
3835
+ color: "#ef4444",
3836
+ flexShrink: 0
3837
+ },
3838
+ children: [
3839
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { flex: "1 1 0%" }, children: aiError }),
3840
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3841
+ "button",
3842
+ {
3843
+ type: "button",
3844
+ onClick: () => setAiError(null),
3845
+ style: {
3846
+ display: "flex",
3847
+ alignItems: "center",
3848
+ justifyContent: "center",
3849
+ background: "none",
3850
+ border: "none",
3851
+ cursor: "pointer",
3852
+ padding: 2,
3853
+ color: "#ef4444",
3854
+ flexShrink: 0
3855
+ },
3856
+ children: icons?.close ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XIcon, { style: { width: 12, height: 12 } })
3857
+ }
3858
+ )
3165
3859
  ]
3166
3860
  }
3167
3861
  ),
@@ -3663,14 +4357,20 @@ function BoltTable({
3663
4357
  gridTemplateColumns,
3664
4358
  headerHeight: HEADER_HEIGHT,
3665
4359
  rowClassName,
3666
- rowStyle,
4360
+ rowStyle: aiStyleOps.length > 0 ? (record, index) => {
4361
+ const base = rowStyle ? rowStyle(record, index) : void 0;
4362
+ const ai = getAIRowStyleForRecord(record);
4363
+ if (!base && !ai) return {};
4364
+ return { ...base, ...ai };
4365
+ } : rowStyle,
3667
4366
  bodyGridRow: hasColumnGroups ? 3 : 2,
3668
4367
  onEdit,
3669
4368
  editingCell,
3670
4369
  onEditComplete: handleEditComplete,
3671
4370
  enableDynamicRowHeight,
3672
4371
  onRowHeightChange: handleRowHeightChange,
3673
- columnGridIndexMap
4372
+ columnGridIndexMap,
4373
+ cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
3674
4374
  }
3675
4375
  )
3676
4376
  ]
@@ -4225,44 +4925,60 @@ function BoltTable({
4225
4925
  }
4226
4926
  }
4227
4927
  ),
4228
- menuCol.columnCellContextMenuItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4229
- "button",
4230
- {
4231
- type: "button",
4232
- "data-bt-ctx-item": "",
4233
- disabled: item.disabled,
4234
- style: {
4235
- ...btnStyle,
4236
- cursor: item.disabled ? "not-allowed" : "pointer",
4237
- opacity: item.disabled ? 0.5 : 1,
4238
- color: item.danger ? "#ef4444" : "inherit"
4239
- },
4240
- onClick: () => {
4241
- if (menuRecord) {
4242
- item.onClick(menuCol.key, menuRecord, menuRowIndex);
4243
- }
4244
- setCellContextMenu(null);
4245
- },
4246
- children: [
4247
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4248
- "span",
4249
- {
4250
- style: {
4251
- display: "flex",
4252
- width: 14,
4253
- height: 14,
4254
- alignItems: "center",
4255
- justifyContent: "center",
4256
- flexShrink: 0
4257
- },
4258
- children: item.icon
4928
+ menuCol.columnCellContextMenuItems.map((item) => {
4929
+ const resolvedIcon = typeof item.icon === "function" ? menuRecord ? item.icon(
4930
+ menuCol.key,
4931
+ menuRecord,
4932
+ menuRowIndex
4933
+ ) : null : item.icon;
4934
+ const resolvedLabel = typeof item.label === "function" ? menuRecord ? item.label(
4935
+ menuCol.key,
4936
+ menuRecord,
4937
+ menuRowIndex
4938
+ ) : null : item.label;
4939
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4940
+ "button",
4941
+ {
4942
+ type: "button",
4943
+ "data-bt-ctx-item": "",
4944
+ disabled: item.disabled,
4945
+ style: {
4946
+ ...btnStyle,
4947
+ cursor: item.disabled ? "not-allowed" : "pointer",
4948
+ opacity: item.disabled ? 0.5 : 1,
4949
+ color: item.danger ? "#ef4444" : "inherit"
4950
+ },
4951
+ onClick: () => {
4952
+ if (menuRecord && item.onClick) {
4953
+ item.onClick(
4954
+ menuCol.key,
4955
+ menuRecord,
4956
+ menuRowIndex
4957
+ );
4259
4958
  }
4260
- ),
4261
- item.label
4262
- ]
4263
- },
4264
- item.key
4265
- ))
4959
+ setCellContextMenu(null);
4960
+ },
4961
+ children: [
4962
+ resolvedIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4963
+ "span",
4964
+ {
4965
+ style: {
4966
+ display: "flex",
4967
+ width: 14,
4968
+ height: 14,
4969
+ alignItems: "center",
4970
+ justifyContent: "center",
4971
+ flexShrink: 0
4972
+ },
4973
+ children: resolvedIcon
4974
+ }
4975
+ ),
4976
+ resolvedLabel
4977
+ ]
4978
+ },
4979
+ item.key
4980
+ );
4981
+ })
4266
4982
  ] })
4267
4983
  ]
4268
4984
  }
@@ -4272,10 +4988,16 @@ function BoltTable({
4272
4988
  })()
4273
4989
  ] });
4274
4990
  }
4991
+
4992
+ // src/types.ts
4993
+ function defineConfig(config) {
4994
+ return config;
4995
+ }
4275
4996
  // Annotate the CommonJS export names for ESM import in node:
4276
4997
  0 && (module.exports = {
4277
4998
  BoltTable,
4278
4999
  DraggableHeader,
4279
5000
  ResizeOverlay,
4280
- TableBody
5001
+ TableBody,
5002
+ defineConfig
4281
5003
  });