bolt-table 0.1.36 → 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");
@@ -677,6 +690,280 @@ var DraggableHeader = import_react.default.memo(
677
690
  DraggableHeader.displayName = "DraggableHeader";
678
691
  var DraggableHeader_default = DraggableHeader;
679
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
+
680
967
  // src/ResizeOverlay.tsx
681
968
  var import_react2 = require("react");
682
969
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -898,9 +1185,11 @@ var Cell = import_react3.default.memo(
898
1185
  isLoading,
899
1186
  onEdit,
900
1187
  isEditing,
901
- onEditComplete
1188
+ onEditComplete,
1189
+ cellStyleFn
902
1190
  }) => {
903
1191
  const isPinned = Boolean(column.pinned);
1192
+ const extraCellStyle = cellStyleFn?.(record, column.dataIndex ?? column.key);
904
1193
  if (isLoading && column.key !== "__select__" && column.key !== "__expand__") {
905
1194
  const shimmerContent = column.shimmerRender ? column.shimmerRender() : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
906
1195
  "div",
@@ -1052,7 +1341,8 @@ var Cell = import_react3.default.memo(
1052
1341
  minWidth: 0,
1053
1342
  ...column.style,
1054
1343
  ...isPinned ? styles?.pinnedCell : void 0,
1055
- ...styles?.cell
1344
+ ...styles?.cell,
1345
+ ...extraCellStyle
1056
1346
  },
1057
1347
  children: isSystem ? content : showEditor ? content : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1058
1348
  "div",
@@ -1079,6 +1369,7 @@ var Cell = import_react3.default.memo(
1079
1369
  if (prev.onEdit !== next.onEdit) return false;
1080
1370
  if (prev.isEditing !== next.isEditing) return false;
1081
1371
  if (prev.onEditComplete !== next.onEditComplete) return false;
1372
+ if (prev.cellStyleFn !== next.cellStyleFn) return false;
1082
1373
  if (prev.column.key === "__select__") {
1083
1374
  return prev.isSelected === next.isSelected && prev.normalizedSelectedKeys === next.normalizedSelectedKeys;
1084
1375
  }
@@ -1180,7 +1471,8 @@ var TableBody = ({
1180
1471
  onEditComplete,
1181
1472
  enableDynamicRowHeight = false,
1182
1473
  onRowHeightChange,
1183
- columnGridIndexMap
1474
+ columnGridIndexMap,
1475
+ cellStyleFn
1184
1476
  }) => {
1185
1477
  const virtualItems = rowVirtualizer.getVirtualItems();
1186
1478
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1309,7 +1601,8 @@ var TableBody = ({
1309
1601
  recordFingerprint,
1310
1602
  onEdit,
1311
1603
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1312
- onEditComplete
1604
+ onEditComplete,
1605
+ cellStyleFn
1313
1606
  }
1314
1607
  )
1315
1608
  }
@@ -1346,7 +1639,8 @@ var TableBody = ({
1346
1639
  recordFingerprint,
1347
1640
  onEdit,
1348
1641
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1349
- onEditComplete
1642
+ onEditComplete,
1643
+ cellStyleFn
1350
1644
  }
1351
1645
  )
1352
1646
  }
@@ -1536,7 +1830,8 @@ var TableBody = ({
1536
1830
  recordFingerprint,
1537
1831
  onEdit,
1538
1832
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1539
- onEditComplete
1833
+ onEditComplete,
1834
+ cellStyleFn
1540
1835
  }
1541
1836
  )
1542
1837
  }
@@ -1664,7 +1959,8 @@ var TableBody = ({
1664
1959
  recordFingerprint,
1665
1960
  onEdit,
1666
1961
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1667
- onEditComplete
1962
+ onEditComplete,
1963
+ cellStyleFn
1668
1964
  }
1669
1965
  )
1670
1966
  }
@@ -1759,7 +2055,13 @@ function BoltTable({
1759
2055
  globalSearchValue,
1760
2056
  onGlobalSearchChange,
1761
2057
  toolbarContent,
1762
- columnSettingsLabel
2058
+ columnSettingsLabel,
2059
+ aiMode = false,
2060
+ aiConfig,
2061
+ onAIQuery,
2062
+ onAIResponse,
2063
+ aiPlaceholder = "Ask AI anything about your data...",
2064
+ aiButtonLabel
1763
2065
  }) {
1764
2066
  const data = (0, import_react4.useMemo)(() => {
1765
2067
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -1933,6 +2235,96 @@ function BoltTable({
1933
2235
  document.removeEventListener("keydown", onKey);
1934
2236
  };
1935
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]);
1936
2328
  const columnsWithPersistedWidths = (0, import_react4.useMemo)(
1937
2329
  () => columns.map((col) => ({
1938
2330
  ...col,
@@ -2574,6 +2966,47 @@ function BoltTable({
2574
2966
  }
2575
2967
  return result;
2576
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
+ );
2577
3010
  const pinnedRowCacheRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
2578
3011
  const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = (0, import_react4.useMemo)(() => {
2579
3012
  if (!resolvedRowPinning || !resolvedRowPinning.top?.length && !resolvedRowPinning.bottom?.length) {
@@ -2581,7 +3014,7 @@ function BoltTable({
2581
3014
  return {
2582
3015
  pinnedTopRows: [],
2583
3016
  pinnedBottomRows: [],
2584
- unpinnedProcessedData: processedData
3017
+ unpinnedProcessedData: aiProcessedData
2585
3018
  };
2586
3019
  }
2587
3020
  const topKeySet = new Set((resolvedRowPinning.top ?? []).map(String));
@@ -2591,7 +3024,7 @@ function BoltTable({
2591
3024
  const topMap = /* @__PURE__ */ new Map();
2592
3025
  const bottomMap = /* @__PURE__ */ new Map();
2593
3026
  const rest = [];
2594
- processedData.forEach((row, idx) => {
3027
+ aiProcessedData.forEach((row, idx) => {
2595
3028
  if (row == null) return;
2596
3029
  const key = getSafeRowKey(row, idx);
2597
3030
  if (topKeySet.has(key)) {
@@ -2631,7 +3064,7 @@ function BoltTable({
2631
3064
  unpinnedProcessedData: rest
2632
3065
  };
2633
3066
  }, [
2634
- processedData,
3067
+ aiProcessedData,
2635
3068
  resolvedRowPinning,
2636
3069
  getSafeRowKey,
2637
3070
  keepPinnedRowsAcrossPages
@@ -2700,7 +3133,7 @@ function BoltTable({
2700
3133
  return unpinnedProcessedData.slice(start, start + pgSize);
2701
3134
  }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
2702
3135
  const shimmerCount = pgEnabled ? pgSize : 15;
2703
- const showShimmer = isLoading && processedData.length === 0;
3136
+ const showShimmer = isLoading && aiProcessedData.length === 0;
2704
3137
  const shimmerRowKeyField = typeof rowKey === "string" ? rowKey : "id";
2705
3138
  const shimmerData = (0, import_react4.useMemo)(() => {
2706
3139
  if (!showShimmer) return null;
@@ -2984,49 +3417,291 @@ function BoltTable({
2984
3417
  border: 1px dashed ${accentColor} !important;
2985
3418
  }
2986
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
+ }
2987
3425
  ` }),
2988
- (!hideGlobalSearch || showColumnSettings) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3426
+ (!hideGlobalSearch || showColumnSettings || aiMode) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2989
3427
  "div",
2990
3428
  {
2991
3429
  style: {
3430
+ position: "relative",
2992
3431
  display: "flex",
2993
3432
  alignItems: "center",
2994
3433
  gap: 8,
2995
3434
  padding: "6px 8px",
2996
3435
  borderBottom: "1px solid rgba(128,128,128,0.2)",
2997
3436
  fontSize: 12,
2998
- flexShrink: 0
3437
+ flexShrink: 0,
3438
+ overflow: "hidden"
2999
3439
  },
3000
3440
  children: [
3001
- !hideGlobalSearch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3441
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3002
3442
  "div",
3003
3443
  {
3004
3444
  style: {
3005
3445
  display: "flex",
3006
3446
  alignItems: "center",
3007
- gap: 4,
3447
+ gap: 8,
3008
3448
  flex: "1 1 0%",
3009
- 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)"
3010
3679
  },
3011
3680
  children: [
3012
3681
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3013
3682
  "span",
3014
3683
  {
3015
- style: { display: "flex", color: "GrayText", flexShrink: 0 },
3016
- 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 } })
3017
3690
  }
3018
3691
  ),
3019
3692
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3020
3693
  "input",
3021
3694
  {
3695
+ ref: aiInputRef,
3022
3696
  type: "text",
3023
- placeholder: "Search all columns...",
3024
- value: globalSearchValue ?? internalGlobalSearch,
3025
- onChange: (e) => {
3026
- const v = e.target.value;
3027
- if (onGlobalSearchChange) onGlobalSearchChange(v);
3028
- 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();
3029
3703
  },
3704
+ disabled: aiLoading,
3030
3705
  style: {
3031
3706
  flex: "1 1 0%",
3032
3707
  border: "none",
@@ -3035,18 +3710,51 @@ function BoltTable({
3035
3710
  font: "inherit",
3036
3711
  color: "inherit",
3037
3712
  padding: "4px 6px",
3038
- minWidth: 0
3713
+ minWidth: 0,
3714
+ fontSize: 12
3039
3715
  }
3040
3716
  }
3041
3717
  ),
3042
- (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3043
3719
  "button",
3044
3720
  {
3045
3721
  type: "button",
3046
- onClick: () => {
3047
- if (onGlobalSearchChange) onGlobalSearchChange("");
3048
- 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
3049
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,
3050
3758
  style: {
3051
3759
  display: "flex",
3052
3760
  alignItems: "center",
@@ -3058,114 +3766,96 @@ function BoltTable({
3058
3766
  color: "GrayText",
3059
3767
  flexShrink: 0
3060
3768
  },
3061
- 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 } })
3062
3771
  }
3063
3772
  )
3064
3773
  ]
3065
3774
  }
3066
- ),
3067
- toolbarContent,
3068
- showColumnSettings && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", flexShrink: 0 }, children: [
3069
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3070
- "button",
3071
- {
3072
- type: "button",
3073
- onClick: () => setShowColumnPicker((p) => !p),
3074
- style: {
3075
- display: "flex",
3076
- alignItems: "center",
3077
- justifyContent: "center",
3078
- background: "none",
3079
- border: "1px solid rgba(128,128,128,0.2)",
3080
- borderRadius: 4,
3081
- cursor: "pointer",
3082
- padding: "4px 6px",
3083
- color: "inherit",
3084
- gap: 4,
3085
- fontSize: 12
3086
- },
3087
- title: "Column settings",
3088
- children: [
3089
- icons?.columns ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ColumnsIcon, { style: { width: 14, height: 14 } }),
3090
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: columnSettingsLabel ?? "Columns" })
3091
- ]
3092
- }
3093
- ),
3094
- showColumnPicker && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3095
- "div",
3096
- {
3097
- ref: columnPickerRef,
3098
- style: {
3099
- position: "absolute",
3100
- top: "100%",
3101
- right: 0,
3102
- zIndex: 99999,
3103
- minWidth: 200,
3104
- maxHeight: 320,
3105
- overflowY: "auto",
3106
- borderRadius: 8,
3107
- border: "1px solid rgba(128,128,128,0.2)",
3108
- boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3109
- backdropFilter: "blur(16px)",
3110
- WebkitBackdropFilter: "blur(16px)",
3111
- backgroundColor: "rgba(128,128,128,0.08)",
3112
- padding: "4px 0",
3113
- marginTop: 4
3114
- },
3115
- children: initialColumns.filter(
3116
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3117
- ).map((col) => {
3118
- const current = columns.find((c) => c.key === col.key);
3119
- const isHidden = current?.hidden ?? false;
3120
- const isPinned = !!current?.pinned;
3121
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3122
- "label",
3123
- {
3124
- style: {
3125
- display: "flex",
3126
- alignItems: "center",
3127
- gap: 8,
3128
- padding: "6px 12px",
3129
- cursor: isPinned ? "not-allowed" : "pointer",
3130
- opacity: isPinned ? 0.5 : 1,
3131
- fontSize: 12
3132
- },
3133
- children: [
3134
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3135
- "input",
3136
- {
3137
- type: "checkbox",
3138
- checked: !isHidden,
3139
- disabled: isPinned,
3140
- onChange: () => {
3141
- if (isPinned) return;
3142
- handleToggleHide(col.key);
3143
- },
3144
- style: {
3145
- cursor: isPinned ? "not-allowed" : "pointer",
3146
- accentColor
3147
- }
3148
- }
3149
- ),
3150
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3151
- "span",
3152
- {
3153
- style: {
3154
- overflow: "hidden",
3155
- textOverflow: "ellipsis",
3156
- whiteSpace: "nowrap"
3157
- },
3158
- children: typeof col.title === "string" ? col.title : col.key
3159
- }
3160
- )
3161
- ]
3162
- },
3163
- col.key
3164
- );
3165
- })
3166
- }
3167
- )
3168
- ] })
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
+ )
3169
3859
  ]
3170
3860
  }
3171
3861
  ),
@@ -3667,14 +4357,20 @@ function BoltTable({
3667
4357
  gridTemplateColumns,
3668
4358
  headerHeight: HEADER_HEIGHT,
3669
4359
  rowClassName,
3670
- 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,
3671
4366
  bodyGridRow: hasColumnGroups ? 3 : 2,
3672
4367
  onEdit,
3673
4368
  editingCell,
3674
4369
  onEditComplete: handleEditComplete,
3675
4370
  enableDynamicRowHeight,
3676
4371
  onRowHeightChange: handleRowHeightChange,
3677
- columnGridIndexMap
4372
+ columnGridIndexMap,
4373
+ cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
3678
4374
  }
3679
4375
  )
3680
4376
  ]
@@ -4292,10 +4988,16 @@ function BoltTable({
4292
4988
  })()
4293
4989
  ] });
4294
4990
  }
4991
+
4992
+ // src/types.ts
4993
+ function defineConfig(config) {
4994
+ return config;
4995
+ }
4295
4996
  // Annotate the CommonJS export names for ESM import in node:
4296
4997
  0 && (module.exports = {
4297
4998
  BoltTable,
4298
4999
  DraggableHeader,
4299
5000
  ResizeOverlay,
4300
- TableBody
5001
+ TableBody,
5002
+ defineConfig
4301
5003
  });