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.mjs CHANGED
@@ -102,6 +102,18 @@ var XIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgBase,
102
102
  /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
103
103
  /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
104
104
  ] });
105
+ var SparklesIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgBase, style, className, children: [
106
+ /* @__PURE__ */ 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" }),
107
+ /* @__PURE__ */ jsx("path", { d: "M5 3v4" }),
108
+ /* @__PURE__ */ jsx("path", { d: "M19 17v4" }),
109
+ /* @__PURE__ */ jsx("path", { d: "M3 5h4" }),
110
+ /* @__PURE__ */ jsx("path", { d: "M17 19h4" })
111
+ ] });
112
+ var SendIcon = ({ style, className }) => /* @__PURE__ */ jsxs("svg", { ...svgBase, style, className, children: [
113
+ /* @__PURE__ */ jsx("path", { d: "m22 2-7 20-4-9-9-4Z" }),
114
+ /* @__PURE__ */ jsx("path", { d: "M22 2 11 13" })
115
+ ] });
116
+ var LoaderIcon = ({ style, className }) => /* @__PURE__ */ jsx("svg", { ...svgBase, style, className, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
105
117
 
106
118
  // src/DraggableHeader.tsx
107
119
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -589,40 +601,44 @@ var DraggableHeader = React.memo(
589
601
  ] }),
590
602
  customContextMenuItems && customContextMenuItems.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
591
603
  /* @__PURE__ */ jsx2("div", { style: { marginTop: 4, marginBottom: 4, borderTop: "1px solid rgba(128,128,128,0.2)" } }),
592
- customContextMenuItems.map((item) => /* @__PURE__ */ jsxs2(
593
- "button",
594
- {
595
- type: "button",
596
- "data-bt-ctx-item": "",
597
- disabled: item.disabled,
598
- style: {
599
- display: "flex",
600
- width: "100%",
601
- alignItems: "center",
602
- gap: 8,
603
- paddingLeft: 12,
604
- paddingRight: 12,
605
- paddingTop: 6,
606
- paddingBottom: 6,
607
- textAlign: "left",
608
- background: "none",
609
- border: "none",
610
- fontSize: "inherit",
611
- cursor: item.disabled ? "not-allowed" : "pointer",
612
- opacity: item.disabled ? 0.5 : 1,
613
- color: item.danger ? "#ef4444" : "inherit"
614
- },
615
- onClick: () => {
616
- item.onClick(column.key);
617
- setContextMenu(null);
604
+ customContextMenuItems.map((item) => {
605
+ const resolvedIcon = typeof item.icon === "function" ? item.icon(column.key) : item.icon;
606
+ const resolvedLabel = typeof item.label === "function" ? item.label(column.key) : item.label;
607
+ return /* @__PURE__ */ jsxs2(
608
+ "button",
609
+ {
610
+ type: "button",
611
+ "data-bt-ctx-item": "",
612
+ disabled: item.disabled,
613
+ style: {
614
+ display: "flex",
615
+ width: "100%",
616
+ alignItems: "center",
617
+ gap: 8,
618
+ paddingLeft: 12,
619
+ paddingRight: 12,
620
+ paddingTop: 6,
621
+ paddingBottom: 6,
622
+ textAlign: "left",
623
+ background: "none",
624
+ border: "none",
625
+ fontSize: "inherit",
626
+ cursor: item.disabled ? "not-allowed" : "pointer",
627
+ opacity: item.disabled ? 0.5 : 1,
628
+ color: item.danger ? "#ef4444" : "inherit"
629
+ },
630
+ onClick: () => {
631
+ item.onClick?.(column.key);
632
+ setContextMenu(null);
633
+ },
634
+ children: [
635
+ resolvedIcon && /* @__PURE__ */ jsx2("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: resolvedIcon }),
636
+ resolvedLabel
637
+ ]
618
638
  },
619
- children: [
620
- item.icon && /* @__PURE__ */ jsx2("span", { style: { display: "flex", width: 12, height: 12, alignItems: "center", justifyContent: "center" }, children: item.icon }),
621
- item.label
622
- ]
623
- },
624
- item.key
625
- ))
639
+ item.key
640
+ );
641
+ })
626
642
  ] })
627
643
  ] });
628
644
  })()
@@ -639,6 +655,280 @@ var DraggableHeader = React.memo(
639
655
  DraggableHeader.displayName = "DraggableHeader";
640
656
  var DraggableHeader_default = DraggableHeader;
641
657
 
658
+ // src/ai.ts
659
+ function detectColumnType(key, data) {
660
+ const values = [];
661
+ for (let i = 0; i < Math.min(data.length, 20); i++) {
662
+ const v = data[i]?.[key];
663
+ if (v != null) values.push(v);
664
+ }
665
+ if (values.length === 0) return { type: "unknown", sample: "" };
666
+ const allNumbers = values.every((v) => typeof v === "number");
667
+ const allBooleans = values.every((v) => typeof v === "boolean");
668
+ const uniqueVals = [...new Set(values.map(String))];
669
+ const sampleStr = uniqueVals.length <= 8 ? uniqueVals.join(", ") : uniqueVals.slice(0, 6).join(", ") + "...";
670
+ if (allBooleans) return { type: "boolean", sample: sampleStr };
671
+ if (allNumbers) {
672
+ const nums = values;
673
+ const min = Math.min(...nums);
674
+ const max = Math.max(...nums);
675
+ return { type: "number", sample: `range ${min}\u2013${max}` };
676
+ }
677
+ return { type: "string", sample: sampleStr };
678
+ }
679
+ function buildSystemPrompt(columns, data) {
680
+ const schemaLines = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => {
681
+ const key = c.dataIndex ?? c.key;
682
+ const title = typeof c.title === "string" ? c.title : c.key;
683
+ const info = detectColumnType(key, data);
684
+ return ` - key: "${c.key}", title: "${title}", dataIndex: "${key}", type: ${info.type}${info.sample ? ` (values: ${info.sample})` : ""}`;
685
+ }).join("\n");
686
+ const sample = data.slice(0, 5).map((row) => {
687
+ const obj = {};
688
+ for (const col of columns) {
689
+ if (col.key === "__select__" || col.key === "__expand__") continue;
690
+ const di = col.dataIndex ?? col.key;
691
+ obj[di] = row[di];
692
+ }
693
+ return obj;
694
+ });
695
+ return `You are a data table assistant. You help users query, filter, sort, and style tabular data.
696
+ You MUST respond with ONLY a valid JSON object \u2014 no markdown fences, no explanation, no extra text.
697
+
698
+ ## Table Schema
699
+ ${schemaLines}
700
+
701
+ ## Sample Data (first ${sample.length} of ${data.length} rows)
702
+ ${JSON.stringify(sample, null, 2)}
703
+
704
+ ## Available Operations
705
+ Combine any of these in a single response:
706
+
707
+ 1. **filter** \u2014 show only rows matching conditions
708
+ { "type": "filter", "conditions": [{ "column": "<dataIndex>", "op": "<op>", "value": <val> }], "logic": "and" | "or" }
709
+
710
+ 2. **rowStyle** \u2014 apply CSS styles to entire rows matching conditions
711
+ { "type": "rowStyle", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
712
+
713
+ 3. **cellStyle** \u2014 apply CSS styles to a specific column's cells matching conditions
714
+ { "type": "cellStyle", "column": "<dataIndex>", "conditions": [...], "logic": "and"|"or", "style": { "<cssProp>": "<value>" } }
715
+
716
+ 4. **sort** \u2014 sort data by a column
717
+ { "type": "sort", "column": "<dataIndex>", "direction": "asc" | "desc" }
718
+
719
+ 5. **hideColumns** / **showColumns** \u2014 toggle column visibility
720
+ { "type": "hideColumns" | "showColumns", "columns": ["<key>", ...] }
721
+
722
+ ## Operators
723
+ eq, neq, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn
724
+
725
+ ## Response format
726
+ {
727
+ "operations": [ ... ],
728
+ "message": "Brief user-friendly description of what was applied"
729
+ }
730
+
731
+ ## Rules
732
+ - Use the dataIndex values from the schema, not display titles.
733
+ - For colors use semi-transparent values like "rgba(255,0,0,0.15)" so text stays readable.
734
+ - CSS property names must be camelCase (e.g. "backgroundColor", "color", "fontWeight").
735
+ - If the user asks to highlight / color / mark specific rows, use rowStyle or cellStyle.
736
+ - If the user asks to show only certain rows, use filter.
737
+ - You can combine filter + rowStyle + sort etc. in one response.
738
+ - The "message" should be concise: what was done, in plain English.`;
739
+ }
740
+ async function callAI(config, systemPrompt, userQuery) {
741
+ const { provider, apiKey, model, baseUrl, maxTokens = 1024, temperature = 0.1 } = config;
742
+ if (provider === "openai" || provider === "custom") {
743
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/chat/completions` : "https://api.openai.com/v1/chat/completions";
744
+ const res = await fetch(url, {
745
+ method: "POST",
746
+ headers: {
747
+ "Content-Type": "application/json",
748
+ Authorization: `Bearer ${apiKey}`
749
+ },
750
+ body: JSON.stringify({
751
+ model: model ?? "gpt-4o-mini",
752
+ messages: [
753
+ { role: "system", content: systemPrompt },
754
+ { role: "user", content: userQuery }
755
+ ],
756
+ max_tokens: maxTokens,
757
+ temperature
758
+ })
759
+ });
760
+ if (!res.ok) {
761
+ const body = await res.text().catch(() => "");
762
+ throw new Error(`AI request failed (${res.status}): ${body}`);
763
+ }
764
+ const json = await res.json();
765
+ return json.choices?.[0]?.message?.content ?? "";
766
+ }
767
+ if (provider === "anthropic") {
768
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/messages` : "https://api.anthropic.com/v1/messages";
769
+ const res = await fetch(url, {
770
+ method: "POST",
771
+ headers: {
772
+ "Content-Type": "application/json",
773
+ "x-api-key": apiKey,
774
+ "anthropic-version": "2023-06-01",
775
+ "anthropic-dangerous-direct-browser-access": "true"
776
+ },
777
+ body: JSON.stringify({
778
+ model: model ?? "claude-sonnet-4-20250514",
779
+ system: systemPrompt,
780
+ messages: [{ role: "user", content: userQuery }],
781
+ max_tokens: maxTokens,
782
+ temperature
783
+ })
784
+ });
785
+ if (!res.ok) {
786
+ const body = await res.text().catch(() => "");
787
+ throw new Error(`AI request failed (${res.status}): ${body}`);
788
+ }
789
+ const json = await res.json();
790
+ const textBlock = json.content?.find(
791
+ (b) => b.type === "text"
792
+ );
793
+ return textBlock?.text ?? "";
794
+ }
795
+ throw new Error(`Unsupported AI provider: ${provider}`);
796
+ }
797
+ function parseAIResponse(raw) {
798
+ let text = raw.trim();
799
+ const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
800
+ if (fenceMatch) text = fenceMatch[1].trim();
801
+ const start = text.indexOf("{");
802
+ const end = text.lastIndexOf("}");
803
+ if (start !== -1 && end > start) {
804
+ text = text.slice(start, end + 1);
805
+ }
806
+ const parsed = JSON.parse(text);
807
+ if (!parsed.operations || !Array.isArray(parsed.operations)) {
808
+ throw new Error("Invalid AI response: missing operations array");
809
+ }
810
+ return {
811
+ operations: parsed.operations,
812
+ message: parsed.message ?? "AI operations applied."
813
+ };
814
+ }
815
+ function evaluateCondition(condition, row) {
816
+ const rawVal = row[condition.column];
817
+ const target = condition.value;
818
+ switch (condition.op) {
819
+ case "eq":
820
+ return rawVal == target;
821
+ case "neq":
822
+ return rawVal != target;
823
+ case "gt":
824
+ return Number(rawVal) > Number(target);
825
+ case "gte":
826
+ return Number(rawVal) >= Number(target);
827
+ case "lt":
828
+ return Number(rawVal) < Number(target);
829
+ case "lte":
830
+ return Number(rawVal) <= Number(target);
831
+ case "contains":
832
+ return String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
833
+ case "notContains":
834
+ return !String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
835
+ case "startsWith":
836
+ return String(rawVal ?? "").toLowerCase().startsWith(String(target).toLowerCase());
837
+ case "endsWith":
838
+ return String(rawVal ?? "").toLowerCase().endsWith(String(target).toLowerCase());
839
+ case "in":
840
+ if (Array.isArray(target)) {
841
+ return target.some((t) => rawVal == t);
842
+ }
843
+ return false;
844
+ case "notIn":
845
+ if (Array.isArray(target)) {
846
+ return !target.some((t) => rawVal == t);
847
+ }
848
+ return true;
849
+ default:
850
+ return true;
851
+ }
852
+ }
853
+ function matchesConditions(conditions, logic, row) {
854
+ if (!conditions || conditions.length === 0) return true;
855
+ if (logic === "or") {
856
+ return conditions.some((c) => evaluateCondition(c, row));
857
+ }
858
+ return conditions.every((c) => evaluateCondition(c, row));
859
+ }
860
+ function applyAIFilter(data, op) {
861
+ return data.filter((row) => matchesConditions(op.conditions, op.logic, row));
862
+ }
863
+ function applyAISort(data, op) {
864
+ const dir = op.direction === "asc" ? 1 : -1;
865
+ const col = op.column;
866
+ return [...data].sort((a, b) => {
867
+ const aVal = a[col];
868
+ const bVal = b[col];
869
+ if (aVal == null && bVal == null) return 0;
870
+ if (aVal == null) return 1;
871
+ if (bVal == null) return -1;
872
+ if (typeof aVal === "number" && typeof bVal === "number")
873
+ return (aVal - bVal) * dir;
874
+ return String(aVal).localeCompare(String(bVal)) * dir;
875
+ });
876
+ }
877
+ function getAIRowStyle(row, ops) {
878
+ let merged;
879
+ for (const op of ops) {
880
+ if (matchesConditions(op.conditions, op.logic, row)) {
881
+ if (!merged) merged = {};
882
+ Object.assign(merged, op.style);
883
+ }
884
+ }
885
+ return merged;
886
+ }
887
+ function getAICellStyle(row, columnKey, ops) {
888
+ let merged;
889
+ for (const op of ops) {
890
+ if (op.column === columnKey && matchesConditions(op.conditions, op.logic, row)) {
891
+ if (!merged) merged = {};
892
+ Object.assign(merged, op.style);
893
+ }
894
+ }
895
+ return merged;
896
+ }
897
+ function applyAIOperations(data, operations) {
898
+ let filteredData = data;
899
+ let sortOp = null;
900
+ const styleOps = [];
901
+ const cellStyleOps = [];
902
+ const hideColumns = [];
903
+ const showColumns = [];
904
+ for (const op of operations) {
905
+ switch (op.type) {
906
+ case "filter":
907
+ filteredData = applyAIFilter(filteredData, op);
908
+ break;
909
+ case "sort":
910
+ sortOp = op;
911
+ break;
912
+ case "rowStyle":
913
+ styleOps.push(op);
914
+ break;
915
+ case "cellStyle":
916
+ cellStyleOps.push(op);
917
+ break;
918
+ case "hideColumns":
919
+ hideColumns.push(...op.columns);
920
+ break;
921
+ case "showColumns":
922
+ showColumns.push(...op.columns);
923
+ break;
924
+ }
925
+ }
926
+ if (sortOp) {
927
+ filteredData = applyAISort(filteredData, sortOp);
928
+ }
929
+ return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns };
930
+ }
931
+
642
932
  // src/ResizeOverlay.tsx
643
933
  import { forwardRef, useImperativeHandle, useRef as useRef2 } from "react";
644
934
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -866,9 +1156,11 @@ var Cell = React3.memo(
866
1156
  isLoading,
867
1157
  onEdit,
868
1158
  isEditing,
869
- onEditComplete
1159
+ onEditComplete,
1160
+ cellStyleFn
870
1161
  }) => {
871
1162
  const isPinned = Boolean(column.pinned);
1163
+ const extraCellStyle = cellStyleFn?.(record, column.dataIndex ?? column.key);
872
1164
  if (isLoading && column.key !== "__select__" && column.key !== "__expand__") {
873
1165
  const shimmerContent = column.shimmerRender ? column.shimmerRender() : /* @__PURE__ */ jsx4(
874
1166
  "div",
@@ -1020,7 +1312,8 @@ var Cell = React3.memo(
1020
1312
  minWidth: 0,
1021
1313
  ...column.style,
1022
1314
  ...isPinned ? styles?.pinnedCell : void 0,
1023
- ...styles?.cell
1315
+ ...styles?.cell,
1316
+ ...extraCellStyle
1024
1317
  },
1025
1318
  children: isSystem ? content : showEditor ? content : /* @__PURE__ */ jsx4(
1026
1319
  "div",
@@ -1047,6 +1340,7 @@ var Cell = React3.memo(
1047
1340
  if (prev.onEdit !== next.onEdit) return false;
1048
1341
  if (prev.isEditing !== next.isEditing) return false;
1049
1342
  if (prev.onEditComplete !== next.onEditComplete) return false;
1343
+ if (prev.cellStyleFn !== next.cellStyleFn) return false;
1050
1344
  if (prev.column.key === "__select__") {
1051
1345
  return prev.isSelected === next.isSelected && prev.normalizedSelectedKeys === next.normalizedSelectedKeys;
1052
1346
  }
@@ -1148,7 +1442,8 @@ var TableBody = ({
1148
1442
  onEditComplete,
1149
1443
  enableDynamicRowHeight = false,
1150
1444
  onRowHeightChange,
1151
- columnGridIndexMap
1445
+ columnGridIndexMap,
1446
+ cellStyleFn
1152
1447
  }) => {
1153
1448
  const virtualItems = rowVirtualizer.getVirtualItems();
1154
1449
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1277,7 +1572,8 @@ var TableBody = ({
1277
1572
  recordFingerprint,
1278
1573
  onEdit,
1279
1574
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1280
- onEditComplete
1575
+ onEditComplete,
1576
+ cellStyleFn
1281
1577
  }
1282
1578
  )
1283
1579
  }
@@ -1314,7 +1610,8 @@ var TableBody = ({
1314
1610
  recordFingerprint,
1315
1611
  onEdit,
1316
1612
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1317
- onEditComplete
1613
+ onEditComplete,
1614
+ cellStyleFn
1318
1615
  }
1319
1616
  )
1320
1617
  }
@@ -1504,7 +1801,8 @@ var TableBody = ({
1504
1801
  recordFingerprint,
1505
1802
  onEdit,
1506
1803
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1507
- onEditComplete
1804
+ onEditComplete,
1805
+ cellStyleFn
1508
1806
  }
1509
1807
  )
1510
1808
  }
@@ -1632,7 +1930,8 @@ var TableBody = ({
1632
1930
  recordFingerprint,
1633
1931
  onEdit,
1634
1932
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1635
- onEditComplete
1933
+ onEditComplete,
1934
+ cellStyleFn
1636
1935
  }
1637
1936
  )
1638
1937
  }
@@ -1727,7 +2026,13 @@ function BoltTable({
1727
2026
  globalSearchValue,
1728
2027
  onGlobalSearchChange,
1729
2028
  toolbarContent,
1730
- columnSettingsLabel
2029
+ columnSettingsLabel,
2030
+ aiMode = false,
2031
+ aiConfig,
2032
+ onAIQuery,
2033
+ onAIResponse,
2034
+ aiPlaceholder = "Ask AI anything about your data...",
2035
+ aiButtonLabel
1731
2036
  }) {
1732
2037
  const data = useMemo2(() => {
1733
2038
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -1901,6 +2206,96 @@ function BoltTable({
1901
2206
  document.removeEventListener("keydown", onKey);
1902
2207
  };
1903
2208
  }, [showColumnPicker]);
2209
+ const [aiBarOpen, setAiBarOpen] = useState3(false);
2210
+ const [aiQuery, setAiQuery] = useState3("");
2211
+ const [aiLoading, setAiLoading] = useState3(false);
2212
+ const [aiResult, setAiResult] = useState3(null);
2213
+ const [aiError, setAiError] = useState3(null);
2214
+ const aiInputRef = useRef4(null);
2215
+ const [aiStyleOps, setAiStyleOps] = useState3([]);
2216
+ const [aiCellStyleOps, setAiCellStyleOps] = useState3(
2217
+ []
2218
+ );
2219
+ const [aiFilteredDataKeys, setAiFilteredDataKeys] = useState3(null);
2220
+ const [aiSortKey, setAiSortKey] = useState3(null);
2221
+ const [aiSortDir, setAiSortDir] = useState3(null);
2222
+ const onAIResponseRef = useRef4(onAIResponse);
2223
+ onAIResponseRef.current = onAIResponse;
2224
+ const handleAISubmit = useCallback2(async () => {
2225
+ const query = aiQuery.trim();
2226
+ if (!query) return;
2227
+ setAiLoading(true);
2228
+ setAiError(null);
2229
+ try {
2230
+ let response;
2231
+ if (onAIQuery) {
2232
+ response = await onAIQuery(query, {
2233
+ data,
2234
+ columns: initialColumns
2235
+ });
2236
+ } else if (aiConfig) {
2237
+ const sysPrompt = buildSystemPrompt(initialColumns, data);
2238
+ const raw = await callAI(aiConfig, sysPrompt, query);
2239
+ response = parseAIResponse(raw);
2240
+ } else {
2241
+ throw new Error("AI mode requires either aiConfig or onAIQuery prop");
2242
+ }
2243
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns, showColumns } = applyAIOperations(data, response.operations);
2244
+ setAiStyleOps(sOps);
2245
+ setAiCellStyleOps(csOps);
2246
+ if (response.operations.some((op) => op.type === "filter")) {
2247
+ const keySet = /* @__PURE__ */ new Set();
2248
+ filteredData.forEach((row, idx) => {
2249
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2250
+ keySet.add(k);
2251
+ });
2252
+ setAiFilteredDataKeys(keySet);
2253
+ } else {
2254
+ setAiFilteredDataKeys(null);
2255
+ }
2256
+ if (sortOp) {
2257
+ setAiSortKey(sortOp.column);
2258
+ setAiSortDir(sortOp.direction);
2259
+ } else {
2260
+ setAiSortKey(null);
2261
+ setAiSortDir(null);
2262
+ }
2263
+ if (hideColumns.length > 0 || showColumns.length > 0) {
2264
+ setColumns(
2265
+ (prev) => prev.map((col) => {
2266
+ if (hideColumns.includes(col.key)) return { ...col, hidden: true };
2267
+ if (showColumns.includes(col.key)) return { ...col, hidden: false };
2268
+ return col;
2269
+ })
2270
+ );
2271
+ }
2272
+ setAiResult(response);
2273
+ onAIResponseRef.current?.(response);
2274
+ } catch (err) {
2275
+ setAiError(err instanceof Error ? err.message : "AI query failed");
2276
+ } finally {
2277
+ setAiLoading(false);
2278
+ }
2279
+ }, [aiQuery, aiConfig, onAIQuery, data, initialColumns, rowKey]);
2280
+ const handleAIClear = useCallback2(() => {
2281
+ setAiResult(null);
2282
+ setAiError(null);
2283
+ setAiStyleOps([]);
2284
+ setAiCellStyleOps([]);
2285
+ setAiFilteredDataKeys(null);
2286
+ setAiSortKey(null);
2287
+ setAiSortDir(null);
2288
+ setAiQuery("");
2289
+ }, []);
2290
+ const handleAIBarClose = useCallback2(() => {
2291
+ setAiBarOpen(false);
2292
+ handleAIClear();
2293
+ }, [handleAIClear]);
2294
+ React4.useEffect(() => {
2295
+ if (aiBarOpen && aiInputRef.current) {
2296
+ setTimeout(() => aiInputRef.current?.focus(), 300);
2297
+ }
2298
+ }, [aiBarOpen]);
1904
2299
  const columnsWithPersistedWidths = useMemo2(
1905
2300
  () => columns.map((col) => ({
1906
2301
  ...col,
@@ -2542,6 +2937,47 @@ function BoltTable({
2542
2937
  }
2543
2938
  return result;
2544
2939
  }, [data, sortState, columnFilters, globalSearchValue, internalGlobalSearch]);
2940
+ const aiProcessedData = useMemo2(() => {
2941
+ let result = processedData;
2942
+ if (aiFilteredDataKeys) {
2943
+ result = result.filter((row, idx) => {
2944
+ if (row == null) return false;
2945
+ const k = typeof rowKey === "function" ? rowKey(row) : String(
2946
+ row[typeof rowKey === "string" ? rowKey : "id"] ?? idx
2947
+ );
2948
+ return aiFilteredDataKeys.has(k);
2949
+ });
2950
+ }
2951
+ if (aiSortKey && aiSortDir) {
2952
+ const dir = aiSortDir === "asc" ? 1 : -1;
2953
+ const col = aiSortKey;
2954
+ result = [...result].sort((a, b) => {
2955
+ const aVal = a[col];
2956
+ const bVal = b[col];
2957
+ if (aVal == null && bVal == null) return 0;
2958
+ if (aVal == null) return 1;
2959
+ if (bVal == null) return -1;
2960
+ if (typeof aVal === "number" && typeof bVal === "number")
2961
+ return (aVal - bVal) * dir;
2962
+ return String(aVal).localeCompare(String(bVal)) * dir;
2963
+ });
2964
+ }
2965
+ return result;
2966
+ }, [processedData, aiFilteredDataKeys, aiSortKey, aiSortDir, rowKey]);
2967
+ const getAIRowStyleForRecord = useCallback2(
2968
+ (record) => {
2969
+ if (aiStyleOps.length === 0) return void 0;
2970
+ return getAIRowStyle(record, aiStyleOps);
2971
+ },
2972
+ [aiStyleOps]
2973
+ );
2974
+ const getAICellStyleForRecord = useCallback2(
2975
+ (record, columnKey) => {
2976
+ if (aiCellStyleOps.length === 0) return void 0;
2977
+ return getAICellStyle(record, columnKey, aiCellStyleOps);
2978
+ },
2979
+ [aiCellStyleOps]
2980
+ );
2545
2981
  const pinnedRowCacheRef = useRef4(/* @__PURE__ */ new Map());
2546
2982
  const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = useMemo2(() => {
2547
2983
  if (!resolvedRowPinning || !resolvedRowPinning.top?.length && !resolvedRowPinning.bottom?.length) {
@@ -2549,7 +2985,7 @@ function BoltTable({
2549
2985
  return {
2550
2986
  pinnedTopRows: [],
2551
2987
  pinnedBottomRows: [],
2552
- unpinnedProcessedData: processedData
2988
+ unpinnedProcessedData: aiProcessedData
2553
2989
  };
2554
2990
  }
2555
2991
  const topKeySet = new Set((resolvedRowPinning.top ?? []).map(String));
@@ -2559,7 +2995,7 @@ function BoltTable({
2559
2995
  const topMap = /* @__PURE__ */ new Map();
2560
2996
  const bottomMap = /* @__PURE__ */ new Map();
2561
2997
  const rest = [];
2562
- processedData.forEach((row, idx) => {
2998
+ aiProcessedData.forEach((row, idx) => {
2563
2999
  if (row == null) return;
2564
3000
  const key = getSafeRowKey(row, idx);
2565
3001
  if (topKeySet.has(key)) {
@@ -2599,7 +3035,7 @@ function BoltTable({
2599
3035
  unpinnedProcessedData: rest
2600
3036
  };
2601
3037
  }, [
2602
- processedData,
3038
+ aiProcessedData,
2603
3039
  resolvedRowPinning,
2604
3040
  getSafeRowKey,
2605
3041
  keepPinnedRowsAcrossPages
@@ -2668,7 +3104,7 @@ function BoltTable({
2668
3104
  return unpinnedProcessedData.slice(start, start + pgSize);
2669
3105
  }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
2670
3106
  const shimmerCount = pgEnabled ? pgSize : 15;
2671
- const showShimmer = isLoading && processedData.length === 0;
3107
+ const showShimmer = isLoading && aiProcessedData.length === 0;
2672
3108
  const shimmerRowKeyField = typeof rowKey === "string" ? rowKey : "id";
2673
3109
  const shimmerData = useMemo2(() => {
2674
3110
  if (!showShimmer) return null;
@@ -2952,49 +3388,291 @@ function BoltTable({
2952
3388
  border: 1px dashed ${accentColor} !important;
2953
3389
  }
2954
3390
  ${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
3391
+ @keyframes bt-spin { to { transform: rotate(360deg); } }
3392
+ @keyframes bt-ai-shimmer {
3393
+ 0% { background-position: -200% 0; }
3394
+ 100% { background-position: 200% 0; }
3395
+ }
2955
3396
  ` }),
2956
- (!hideGlobalSearch || showColumnSettings) && /* @__PURE__ */ jsxs5(
3397
+ (!hideGlobalSearch || showColumnSettings || aiMode) && /* @__PURE__ */ jsxs5(
2957
3398
  "div",
2958
3399
  {
2959
3400
  style: {
3401
+ position: "relative",
2960
3402
  display: "flex",
2961
3403
  alignItems: "center",
2962
3404
  gap: 8,
2963
3405
  padding: "6px 8px",
2964
3406
  borderBottom: "1px solid rgba(128,128,128,0.2)",
2965
3407
  fontSize: 12,
2966
- flexShrink: 0
3408
+ flexShrink: 0,
3409
+ overflow: "hidden"
2967
3410
  },
2968
3411
  children: [
2969
- !hideGlobalSearch && /* @__PURE__ */ jsxs5(
3412
+ /* @__PURE__ */ jsxs5(
2970
3413
  "div",
2971
3414
  {
2972
3415
  style: {
2973
3416
  display: "flex",
2974
3417
  alignItems: "center",
2975
- gap: 4,
3418
+ gap: 8,
2976
3419
  flex: "1 1 0%",
2977
- position: "relative"
3420
+ opacity: aiBarOpen ? 0 : 1,
3421
+ transform: aiBarOpen ? "scale(0.97)" : "scale(1)",
3422
+ transition: "opacity 0.25s ease, transform 0.25s ease",
3423
+ pointerEvents: aiBarOpen ? "none" : "auto",
3424
+ minWidth: 0
3425
+ },
3426
+ children: [
3427
+ !hideGlobalSearch && /* @__PURE__ */ jsxs5(
3428
+ "div",
3429
+ {
3430
+ style: {
3431
+ display: "flex",
3432
+ alignItems: "center",
3433
+ gap: 4,
3434
+ flex: "1 1 0%",
3435
+ position: "relative"
3436
+ },
3437
+ children: [
3438
+ /* @__PURE__ */ jsx5(
3439
+ "span",
3440
+ {
3441
+ style: {
3442
+ display: "flex",
3443
+ color: "GrayText",
3444
+ flexShrink: 0
3445
+ },
3446
+ children: icons?.search ?? /* @__PURE__ */ jsx5(SearchIcon, { style: { width: 14, height: 14 } })
3447
+ }
3448
+ ),
3449
+ /* @__PURE__ */ jsx5(
3450
+ "input",
3451
+ {
3452
+ type: "text",
3453
+ placeholder: "Search all columns...",
3454
+ value: globalSearchValue ?? internalGlobalSearch,
3455
+ onChange: (e) => {
3456
+ const v = e.target.value;
3457
+ if (onGlobalSearchChange) onGlobalSearchChange(v);
3458
+ else setInternalGlobalSearch(v);
3459
+ },
3460
+ style: {
3461
+ flex: "1 1 0%",
3462
+ border: "none",
3463
+ outline: "none",
3464
+ background: "transparent",
3465
+ font: "inherit",
3466
+ color: "inherit",
3467
+ padding: "4px 6px",
3468
+ minWidth: 0
3469
+ }
3470
+ }
3471
+ ),
3472
+ (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ jsx5(
3473
+ "button",
3474
+ {
3475
+ type: "button",
3476
+ onClick: () => {
3477
+ if (onGlobalSearchChange) onGlobalSearchChange("");
3478
+ else setInternalGlobalSearch("");
3479
+ },
3480
+ style: {
3481
+ display: "flex",
3482
+ alignItems: "center",
3483
+ justifyContent: "center",
3484
+ background: "none",
3485
+ border: "none",
3486
+ cursor: "pointer",
3487
+ padding: 2,
3488
+ color: "GrayText",
3489
+ flexShrink: 0
3490
+ },
3491
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
3492
+ }
3493
+ )
3494
+ ]
3495
+ }
3496
+ ),
3497
+ toolbarContent,
3498
+ showColumnSettings && /* @__PURE__ */ jsxs5("div", { style: { position: "relative", flexShrink: 0 }, children: [
3499
+ /* @__PURE__ */ jsxs5(
3500
+ "button",
3501
+ {
3502
+ type: "button",
3503
+ onClick: () => setShowColumnPicker((p) => !p),
3504
+ style: {
3505
+ display: "flex",
3506
+ alignItems: "center",
3507
+ justifyContent: "center",
3508
+ background: "none",
3509
+ border: "1px solid rgba(128,128,128,0.2)",
3510
+ borderRadius: 4,
3511
+ cursor: "pointer",
3512
+ padding: "4px 6px",
3513
+ color: "inherit",
3514
+ gap: 4,
3515
+ fontSize: 12
3516
+ },
3517
+ title: "Column settings",
3518
+ children: [
3519
+ icons?.columns ?? /* @__PURE__ */ jsx5(ColumnsIcon, { style: { width: 14, height: 14 } }),
3520
+ /* @__PURE__ */ jsx5("span", { children: columnSettingsLabel ?? "Columns" })
3521
+ ]
3522
+ }
3523
+ ),
3524
+ showColumnPicker && /* @__PURE__ */ jsx5(
3525
+ "div",
3526
+ {
3527
+ ref: columnPickerRef,
3528
+ style: {
3529
+ position: "absolute",
3530
+ top: "100%",
3531
+ right: 0,
3532
+ zIndex: 99999,
3533
+ minWidth: 200,
3534
+ maxHeight: 320,
3535
+ overflowY: "auto",
3536
+ borderRadius: 8,
3537
+ border: "1px solid rgba(128,128,128,0.2)",
3538
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3539
+ backdropFilter: "blur(16px)",
3540
+ WebkitBackdropFilter: "blur(16px)",
3541
+ backgroundColor: "rgba(128,128,128,0.08)",
3542
+ padding: "4px 0",
3543
+ marginTop: 4
3544
+ },
3545
+ children: initialColumns.filter(
3546
+ (c) => c.key !== "__select__" && c.key !== "__expand__"
3547
+ ).map((col) => {
3548
+ const current = columns.find(
3549
+ (c) => c.key === col.key
3550
+ );
3551
+ const isHidden = current?.hidden ?? false;
3552
+ const isPinned = !!current?.pinned;
3553
+ return /* @__PURE__ */ jsxs5(
3554
+ "label",
3555
+ {
3556
+ style: {
3557
+ display: "flex",
3558
+ alignItems: "center",
3559
+ gap: 8,
3560
+ padding: "6px 12px",
3561
+ cursor: isPinned ? "not-allowed" : "pointer",
3562
+ opacity: isPinned ? 0.5 : 1,
3563
+ fontSize: 12
3564
+ },
3565
+ children: [
3566
+ /* @__PURE__ */ jsx5(
3567
+ "input",
3568
+ {
3569
+ type: "checkbox",
3570
+ checked: !isHidden,
3571
+ disabled: isPinned,
3572
+ onChange: () => {
3573
+ if (isPinned) return;
3574
+ handleToggleHide(col.key);
3575
+ },
3576
+ style: {
3577
+ cursor: isPinned ? "not-allowed" : "pointer",
3578
+ accentColor
3579
+ }
3580
+ }
3581
+ ),
3582
+ /* @__PURE__ */ jsx5(
3583
+ "span",
3584
+ {
3585
+ style: {
3586
+ overflow: "hidden",
3587
+ textOverflow: "ellipsis",
3588
+ whiteSpace: "nowrap"
3589
+ },
3590
+ children: typeof col.title === "string" ? col.title : col.key
3591
+ }
3592
+ )
3593
+ ]
3594
+ },
3595
+ col.key
3596
+ );
3597
+ })
3598
+ }
3599
+ )
3600
+ ] }),
3601
+ aiMode && /* @__PURE__ */ jsxs5(
3602
+ "button",
3603
+ {
3604
+ type: "button",
3605
+ onClick: () => setAiBarOpen(true),
3606
+ style: {
3607
+ display: "flex",
3608
+ alignItems: "center",
3609
+ justifyContent: "center",
3610
+ gap: 4,
3611
+ background: `linear-gradient(135deg, ${accentColor}18, ${accentColor}08)`,
3612
+ border: `1px solid ${accentColor}40`,
3613
+ borderRadius: 4,
3614
+ cursor: "pointer",
3615
+ padding: "4px 8px",
3616
+ color: accentColor,
3617
+ fontSize: 12,
3618
+ fontWeight: 500,
3619
+ flexShrink: 0,
3620
+ transition: "all 0.2s ease"
3621
+ },
3622
+ title: "Ask AI",
3623
+ children: [
3624
+ icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 14, height: 14 } }),
3625
+ /* @__PURE__ */ jsx5("span", { children: aiButtonLabel ?? "Ask AI" })
3626
+ ]
3627
+ }
3628
+ )
3629
+ ]
3630
+ }
3631
+ ),
3632
+ aiMode && /* @__PURE__ */ jsxs5(
3633
+ "div",
3634
+ {
3635
+ style: {
3636
+ position: "absolute",
3637
+ inset: 0,
3638
+ display: "flex",
3639
+ alignItems: "center",
3640
+ gap: 8,
3641
+ padding: "4px 8px",
3642
+ opacity: aiBarOpen ? 1 : 0,
3643
+ transform: aiBarOpen ? "translateX(0)" : "translateX(40px)",
3644
+ transition: "opacity 0.3s cubic-bezier(0.4,0,0.2,1), transform 0.3s cubic-bezier(0.4,0,0.2,1)",
3645
+ pointerEvents: aiBarOpen ? "auto" : "none",
3646
+ zIndex: 2,
3647
+ background: "inherit",
3648
+ backdropFilter: "blur(12px)",
3649
+ WebkitBackdropFilter: "blur(12px)"
2978
3650
  },
2979
3651
  children: [
2980
3652
  /* @__PURE__ */ jsx5(
2981
3653
  "span",
2982
3654
  {
2983
- style: { display: "flex", color: "GrayText", flexShrink: 0 },
2984
- children: icons?.search ?? /* @__PURE__ */ jsx5(SearchIcon, { style: { width: 14, height: 14 } })
3655
+ style: {
3656
+ display: "flex",
3657
+ color: accentColor,
3658
+ flexShrink: 0
3659
+ },
3660
+ children: icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 16, height: 16 } })
2985
3661
  }
2986
3662
  ),
2987
3663
  /* @__PURE__ */ jsx5(
2988
3664
  "input",
2989
3665
  {
3666
+ ref: aiInputRef,
2990
3667
  type: "text",
2991
- placeholder: "Search all columns...",
2992
- value: globalSearchValue ?? internalGlobalSearch,
2993
- onChange: (e) => {
2994
- const v = e.target.value;
2995
- if (onGlobalSearchChange) onGlobalSearchChange(v);
2996
- else setInternalGlobalSearch(v);
3668
+ placeholder: aiPlaceholder,
3669
+ value: aiQuery,
3670
+ onChange: (e) => setAiQuery(e.target.value),
3671
+ onKeyDown: (e) => {
3672
+ if (e.key === "Enter" && !aiLoading) handleAISubmit();
3673
+ if (e.key === "Escape") handleAIBarClose();
2997
3674
  },
3675
+ disabled: aiLoading,
2998
3676
  style: {
2999
3677
  flex: "1 1 0%",
3000
3678
  border: "none",
@@ -3003,18 +3681,51 @@ function BoltTable({
3003
3681
  font: "inherit",
3004
3682
  color: "inherit",
3005
3683
  padding: "4px 6px",
3006
- minWidth: 0
3684
+ minWidth: 0,
3685
+ fontSize: 12
3007
3686
  }
3008
3687
  }
3009
3688
  ),
3010
- (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ jsx5(
3689
+ /* @__PURE__ */ jsx5(
3011
3690
  "button",
3012
3691
  {
3013
3692
  type: "button",
3014
- onClick: () => {
3015
- if (onGlobalSearchChange) onGlobalSearchChange("");
3016
- else setInternalGlobalSearch("");
3693
+ onClick: handleAISubmit,
3694
+ disabled: aiLoading || !aiQuery.trim(),
3695
+ style: {
3696
+ display: "flex",
3697
+ alignItems: "center",
3698
+ justifyContent: "center",
3699
+ background: aiQuery.trim() ? accentColor : "rgba(128,128,128,0.15)",
3700
+ border: "none",
3701
+ borderRadius: 4,
3702
+ cursor: aiLoading || !aiQuery.trim() ? "not-allowed" : "pointer",
3703
+ padding: "4px 8px",
3704
+ color: aiQuery.trim() ? "#fff" : "GrayText",
3705
+ transition: "all 0.2s ease",
3706
+ flexShrink: 0,
3707
+ gap: 4,
3708
+ fontSize: 12,
3709
+ opacity: aiLoading ? 0.7 : 1
3017
3710
  },
3711
+ title: "Send",
3712
+ children: aiLoading ? /* @__PURE__ */ jsx5(Fragment4, { children: icons?.loader ?? /* @__PURE__ */ jsx5(
3713
+ LoaderIcon,
3714
+ {
3715
+ style: {
3716
+ width: 14,
3717
+ height: 14,
3718
+ animation: "bt-spin 1s linear infinite"
3719
+ }
3720
+ }
3721
+ ) }) : /* @__PURE__ */ jsx5(Fragment4, { children: icons?.send ?? /* @__PURE__ */ jsx5(SendIcon, { style: { width: 14, height: 14 } }) })
3722
+ }
3723
+ ),
3724
+ /* @__PURE__ */ jsx5(
3725
+ "button",
3726
+ {
3727
+ type: "button",
3728
+ onClick: handleAIBarClose,
3018
3729
  style: {
3019
3730
  display: "flex",
3020
3731
  alignItems: "center",
@@ -3026,114 +3737,96 @@ function BoltTable({
3026
3737
  color: "GrayText",
3027
3738
  flexShrink: 0
3028
3739
  },
3029
- children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
3740
+ title: "Close AI",
3741
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 14, height: 14 } })
3030
3742
  }
3031
3743
  )
3032
3744
  ]
3033
3745
  }
3034
- ),
3035
- toolbarContent,
3036
- showColumnSettings && /* @__PURE__ */ jsxs5("div", { style: { position: "relative", flexShrink: 0 }, children: [
3037
- /* @__PURE__ */ jsxs5(
3038
- "button",
3039
- {
3040
- type: "button",
3041
- onClick: () => setShowColumnPicker((p) => !p),
3042
- style: {
3043
- display: "flex",
3044
- alignItems: "center",
3045
- justifyContent: "center",
3046
- background: "none",
3047
- border: "1px solid rgba(128,128,128,0.2)",
3048
- borderRadius: 4,
3049
- cursor: "pointer",
3050
- padding: "4px 6px",
3051
- color: "inherit",
3052
- gap: 4,
3053
- fontSize: 12
3054
- },
3055
- title: "Column settings",
3056
- children: [
3057
- icons?.columns ?? /* @__PURE__ */ jsx5(ColumnsIcon, { style: { width: 14, height: 14 } }),
3058
- /* @__PURE__ */ jsx5("span", { children: columnSettingsLabel ?? "Columns" })
3059
- ]
3060
- }
3061
- ),
3062
- showColumnPicker && /* @__PURE__ */ jsx5(
3063
- "div",
3064
- {
3065
- ref: columnPickerRef,
3066
- style: {
3067
- position: "absolute",
3068
- top: "100%",
3069
- right: 0,
3070
- zIndex: 99999,
3071
- minWidth: 200,
3072
- maxHeight: 320,
3073
- overflowY: "auto",
3074
- borderRadius: 8,
3075
- border: "1px solid rgba(128,128,128,0.2)",
3076
- boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3077
- backdropFilter: "blur(16px)",
3078
- WebkitBackdropFilter: "blur(16px)",
3079
- backgroundColor: "rgba(128,128,128,0.08)",
3080
- padding: "4px 0",
3081
- marginTop: 4
3082
- },
3083
- children: initialColumns.filter(
3084
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3085
- ).map((col) => {
3086
- const current = columns.find((c) => c.key === col.key);
3087
- const isHidden = current?.hidden ?? false;
3088
- const isPinned = !!current?.pinned;
3089
- return /* @__PURE__ */ jsxs5(
3090
- "label",
3091
- {
3092
- style: {
3093
- display: "flex",
3094
- alignItems: "center",
3095
- gap: 8,
3096
- padding: "6px 12px",
3097
- cursor: isPinned ? "not-allowed" : "pointer",
3098
- opacity: isPinned ? 0.5 : 1,
3099
- fontSize: 12
3100
- },
3101
- children: [
3102
- /* @__PURE__ */ jsx5(
3103
- "input",
3104
- {
3105
- type: "checkbox",
3106
- checked: !isHidden,
3107
- disabled: isPinned,
3108
- onChange: () => {
3109
- if (isPinned) return;
3110
- handleToggleHide(col.key);
3111
- },
3112
- style: {
3113
- cursor: isPinned ? "not-allowed" : "pointer",
3114
- accentColor
3115
- }
3116
- }
3117
- ),
3118
- /* @__PURE__ */ jsx5(
3119
- "span",
3120
- {
3121
- style: {
3122
- overflow: "hidden",
3123
- textOverflow: "ellipsis",
3124
- whiteSpace: "nowrap"
3125
- },
3126
- children: typeof col.title === "string" ? col.title : col.key
3127
- }
3128
- )
3129
- ]
3130
- },
3131
- col.key
3132
- );
3133
- })
3134
- }
3135
- )
3136
- ] })
3746
+ )
3747
+ ]
3748
+ }
3749
+ ),
3750
+ aiResult && /* @__PURE__ */ jsxs5(
3751
+ "div",
3752
+ {
3753
+ style: {
3754
+ display: "flex",
3755
+ alignItems: "center",
3756
+ gap: 8,
3757
+ padding: "6px 12px",
3758
+ fontSize: 12,
3759
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
3760
+ background: `linear-gradient(90deg, ${accentColor}08, transparent)`,
3761
+ flexShrink: 0
3762
+ },
3763
+ children: [
3764
+ /* @__PURE__ */ jsx5("span", { style: { color: accentColor, display: "flex", flexShrink: 0 }, children: icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 14, height: 14 } }) }),
3765
+ /* @__PURE__ */ jsx5("span", { style: { flex: "1 1 0%", opacity: 0.85 }, children: aiResult.message }),
3766
+ /* @__PURE__ */ jsxs5(
3767
+ "button",
3768
+ {
3769
+ type: "button",
3770
+ onClick: handleAIClear,
3771
+ style: {
3772
+ display: "flex",
3773
+ alignItems: "center",
3774
+ gap: 4,
3775
+ background: "none",
3776
+ border: "1px solid rgba(128,128,128,0.2)",
3777
+ borderRadius: 4,
3778
+ cursor: "pointer",
3779
+ padding: "2px 8px",
3780
+ color: "inherit",
3781
+ fontSize: 11,
3782
+ flexShrink: 0,
3783
+ opacity: 0.7
3784
+ },
3785
+ title: "Clear AI results",
3786
+ children: [
3787
+ icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 10, height: 10 } }),
3788
+ /* @__PURE__ */ jsx5("span", { children: "Clear" })
3789
+ ]
3790
+ }
3791
+ )
3792
+ ]
3793
+ }
3794
+ ),
3795
+ aiError && !aiResult && /* @__PURE__ */ jsxs5(
3796
+ "div",
3797
+ {
3798
+ style: {
3799
+ display: "flex",
3800
+ alignItems: "center",
3801
+ gap: 8,
3802
+ padding: "6px 12px",
3803
+ fontSize: 12,
3804
+ borderBottom: "1px solid rgba(239,68,68,0.2)",
3805
+ background: "rgba(239,68,68,0.06)",
3806
+ color: "#ef4444",
3807
+ flexShrink: 0
3808
+ },
3809
+ children: [
3810
+ /* @__PURE__ */ jsx5("span", { style: { flex: "1 1 0%" }, children: aiError }),
3811
+ /* @__PURE__ */ jsx5(
3812
+ "button",
3813
+ {
3814
+ type: "button",
3815
+ onClick: () => setAiError(null),
3816
+ style: {
3817
+ display: "flex",
3818
+ alignItems: "center",
3819
+ justifyContent: "center",
3820
+ background: "none",
3821
+ border: "none",
3822
+ cursor: "pointer",
3823
+ padding: 2,
3824
+ color: "#ef4444",
3825
+ flexShrink: 0
3826
+ },
3827
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
3828
+ }
3829
+ )
3137
3830
  ]
3138
3831
  }
3139
3832
  ),
@@ -3635,14 +4328,20 @@ function BoltTable({
3635
4328
  gridTemplateColumns,
3636
4329
  headerHeight: HEADER_HEIGHT,
3637
4330
  rowClassName,
3638
- rowStyle,
4331
+ rowStyle: aiStyleOps.length > 0 ? (record, index) => {
4332
+ const base = rowStyle ? rowStyle(record, index) : void 0;
4333
+ const ai = getAIRowStyleForRecord(record);
4334
+ if (!base && !ai) return {};
4335
+ return { ...base, ...ai };
4336
+ } : rowStyle,
3639
4337
  bodyGridRow: hasColumnGroups ? 3 : 2,
3640
4338
  onEdit,
3641
4339
  editingCell,
3642
4340
  onEditComplete: handleEditComplete,
3643
4341
  enableDynamicRowHeight,
3644
4342
  onRowHeightChange: handleRowHeightChange,
3645
- columnGridIndexMap
4343
+ columnGridIndexMap,
4344
+ cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
3646
4345
  }
3647
4346
  )
3648
4347
  ]
@@ -4197,44 +4896,60 @@ function BoltTable({
4197
4896
  }
4198
4897
  }
4199
4898
  ),
4200
- menuCol.columnCellContextMenuItems.map((item) => /* @__PURE__ */ jsxs5(
4201
- "button",
4202
- {
4203
- type: "button",
4204
- "data-bt-ctx-item": "",
4205
- disabled: item.disabled,
4206
- style: {
4207
- ...btnStyle,
4208
- cursor: item.disabled ? "not-allowed" : "pointer",
4209
- opacity: item.disabled ? 0.5 : 1,
4210
- color: item.danger ? "#ef4444" : "inherit"
4211
- },
4212
- onClick: () => {
4213
- if (menuRecord) {
4214
- item.onClick(menuCol.key, menuRecord, menuRowIndex);
4215
- }
4216
- setCellContextMenu(null);
4217
- },
4218
- children: [
4219
- item.icon && /* @__PURE__ */ jsx5(
4220
- "span",
4221
- {
4222
- style: {
4223
- display: "flex",
4224
- width: 14,
4225
- height: 14,
4226
- alignItems: "center",
4227
- justifyContent: "center",
4228
- flexShrink: 0
4229
- },
4230
- children: item.icon
4899
+ menuCol.columnCellContextMenuItems.map((item) => {
4900
+ const resolvedIcon = typeof item.icon === "function" ? menuRecord ? item.icon(
4901
+ menuCol.key,
4902
+ menuRecord,
4903
+ menuRowIndex
4904
+ ) : null : item.icon;
4905
+ const resolvedLabel = typeof item.label === "function" ? menuRecord ? item.label(
4906
+ menuCol.key,
4907
+ menuRecord,
4908
+ menuRowIndex
4909
+ ) : null : item.label;
4910
+ return /* @__PURE__ */ jsxs5(
4911
+ "button",
4912
+ {
4913
+ type: "button",
4914
+ "data-bt-ctx-item": "",
4915
+ disabled: item.disabled,
4916
+ style: {
4917
+ ...btnStyle,
4918
+ cursor: item.disabled ? "not-allowed" : "pointer",
4919
+ opacity: item.disabled ? 0.5 : 1,
4920
+ color: item.danger ? "#ef4444" : "inherit"
4921
+ },
4922
+ onClick: () => {
4923
+ if (menuRecord && item.onClick) {
4924
+ item.onClick(
4925
+ menuCol.key,
4926
+ menuRecord,
4927
+ menuRowIndex
4928
+ );
4231
4929
  }
4232
- ),
4233
- item.label
4234
- ]
4235
- },
4236
- item.key
4237
- ))
4930
+ setCellContextMenu(null);
4931
+ },
4932
+ children: [
4933
+ resolvedIcon && /* @__PURE__ */ jsx5(
4934
+ "span",
4935
+ {
4936
+ style: {
4937
+ display: "flex",
4938
+ width: 14,
4939
+ height: 14,
4940
+ alignItems: "center",
4941
+ justifyContent: "center",
4942
+ flexShrink: 0
4943
+ },
4944
+ children: resolvedIcon
4945
+ }
4946
+ ),
4947
+ resolvedLabel
4948
+ ]
4949
+ },
4950
+ item.key
4951
+ );
4952
+ })
4238
4953
  ] })
4239
4954
  ]
4240
4955
  }
@@ -4244,9 +4959,15 @@ function BoltTable({
4244
4959
  })()
4245
4960
  ] });
4246
4961
  }
4962
+
4963
+ // src/types.ts
4964
+ function defineConfig(config) {
4965
+ return config;
4966
+ }
4247
4967
  export {
4248
4968
  BoltTable,
4249
4969
  DraggableHeader_default as DraggableHeader,
4250
4970
  ResizeOverlay_default as ResizeOverlay,
4251
- TableBody_default as TableBody
4971
+ TableBody_default as TableBody,
4972
+ defineConfig
4252
4973
  };