bolt-table 0.1.36 → 0.1.38

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";
@@ -643,6 +655,298 @@ var DraggableHeader = React.memo(
643
655
  DraggableHeader.displayName = "DraggableHeader";
644
656
  var DraggableHeader_default = DraggableHeader;
645
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
+ var cachedSchema = null;
680
+ function buildSchemaFingerprint(columns, dataLen) {
681
+ return columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__").map((c) => c.key).join(",") + `:${dataLen}`;
682
+ }
683
+ function buildSystemPrompt(columns, data) {
684
+ const fingerprint = buildSchemaFingerprint(columns, data.length);
685
+ if (cachedSchema?.fingerprint === fingerprint) {
686
+ return cachedSchema.prompt;
687
+ }
688
+ const cols = columns.filter((c) => c.key !== "__select__" && c.key !== "__expand__");
689
+ const schema = cols.map((c) => {
690
+ const key = c.dataIndex ?? c.key;
691
+ const title = typeof c.title === "string" ? c.title : c.key;
692
+ const info = detectColumnType(key, data);
693
+ const w = c.width ?? 150;
694
+ const pin = c.pinned ? `, pinned: "${c.pinned}"` : "";
695
+ const hidden = c.hidden ? ", hidden: true" : "";
696
+ const vals = info.sample ? "|vals: " + info.sample : "";
697
+ return ` ${c.key}|${key}|"${title}"|${info.type}|w:${w}${pin}${hidden}${vals}`;
698
+ }).join("\n");
699
+ const sample = data.slice(0, 3).map((row) => {
700
+ const obj = {};
701
+ for (const col of cols) {
702
+ const di = col.dataIndex ?? col.key;
703
+ obj[di] = row[di];
704
+ }
705
+ return obj;
706
+ });
707
+ const prompt = `Data table AI. Respond ONLY with valid JSON, no markdown/explanation.
708
+
709
+ SCHEMA (key|dataIndex|title|type|width|flags|sample):
710
+ ${schema}
711
+
712
+ SAMPLE (${sample.length}/${data.length} rows):
713
+ ${JSON.stringify(sample)}
714
+
715
+ COLUMN ORDER: [${cols.map((c) => `"${c.key}"`).join(",")}]
716
+
717
+ OPS (combine any):
718
+ filter: {type:"filter",conditions:[{column:"<dataIndex>",op:"<op>",value:<v>}],logic:"and"|"or"}
719
+ sort: {type:"sort",column:"<dataIndex>",direction:"asc"|"desc"}
720
+ rowStyle: {type:"rowStyle",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
721
+ cellStyle: {type:"cellStyle",column:"<dataIndex>",conditions:[...],logic:"and"|"or",style:{cssProp:"val"}}
722
+ hideColumns: {type:"hideColumns",columns:["key",...]}
723
+ showColumns: {type:"showColumns",columns:["key",...]}
724
+ resizeColumn: {type:"resizeColumn",column:"<key>",width:<px>}
725
+ reorderColumns: {type:"reorderColumns",order:["key1","key2",...]} (full column order)
726
+ pinColumn: {type:"pinColumn",column:"<key>",pinned:"left"|"right"|false}
727
+ setPage: {type:"setPage",page:<number>}
728
+
729
+ OPS: eq,neq,gt,gte,lt,lte,contains,notContains,startsWith,endsWith,in,notIn
730
+
731
+ FORMAT: {"operations":[...],"message":"brief description"}
732
+
733
+ RULES:
734
+ - Use dataIndex for data ops, key for column ops (hide/show/resize/reorder/pin).
735
+ - Colors: semi-transparent rgba. CSS props: camelCase.
736
+ - reorderColumns: provide FULL ordered array of ALL visible column keys.
737
+ - resizeColumn width: integer pixels (min 40, max 800).
738
+ - Combine multiple ops freely. Message: concise plain English.`;
739
+ cachedSchema = { fingerprint, prompt };
740
+ return prompt;
741
+ }
742
+ async function callAI(config, systemPrompt, userQuery) {
743
+ const { provider, apiKey, model, baseUrl, maxTokens = 1024, temperature = 0.1 } = config;
744
+ if (provider === "openai" || provider === "custom") {
745
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/chat/completions` : "https://api.openai.com/v1/chat/completions";
746
+ const res = await fetch(url, {
747
+ method: "POST",
748
+ headers: {
749
+ "Content-Type": "application/json",
750
+ Authorization: `Bearer ${apiKey}`
751
+ },
752
+ body: JSON.stringify({
753
+ model: model ?? "gpt-4o-mini",
754
+ messages: [
755
+ { role: "system", content: systemPrompt },
756
+ { role: "user", content: userQuery }
757
+ ],
758
+ max_tokens: maxTokens,
759
+ temperature
760
+ })
761
+ });
762
+ if (!res.ok) {
763
+ const body = await res.text().catch(() => "");
764
+ throw new Error(`AI request failed (${res.status}): ${body}`);
765
+ }
766
+ const json = await res.json();
767
+ return json.choices?.[0]?.message?.content ?? "";
768
+ }
769
+ if (provider === "anthropic") {
770
+ const url = baseUrl ? `${baseUrl.replace(/\/$/, "")}/messages` : "https://api.anthropic.com/v1/messages";
771
+ const res = await fetch(url, {
772
+ method: "POST",
773
+ headers: {
774
+ "Content-Type": "application/json",
775
+ "x-api-key": apiKey,
776
+ "anthropic-version": "2023-06-01",
777
+ "anthropic-dangerous-direct-browser-access": "true"
778
+ },
779
+ body: JSON.stringify({
780
+ model: model ?? "claude-sonnet-4-20250514",
781
+ system: systemPrompt,
782
+ messages: [{ role: "user", content: userQuery }],
783
+ max_tokens: maxTokens,
784
+ temperature
785
+ })
786
+ });
787
+ if (!res.ok) {
788
+ const body = await res.text().catch(() => "");
789
+ throw new Error(`AI request failed (${res.status}): ${body}`);
790
+ }
791
+ const json = await res.json();
792
+ const textBlock = json.content?.find(
793
+ (b) => b.type === "text"
794
+ );
795
+ return textBlock?.text ?? "";
796
+ }
797
+ throw new Error(`Unsupported AI provider: ${provider}`);
798
+ }
799
+ function parseAIResponse(raw) {
800
+ let text = raw.trim();
801
+ const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
802
+ if (fenceMatch) text = fenceMatch[1].trim();
803
+ const start = text.indexOf("{");
804
+ const end = text.lastIndexOf("}");
805
+ if (start !== -1 && end > start) {
806
+ text = text.slice(start, end + 1);
807
+ }
808
+ const parsed = JSON.parse(text);
809
+ if (!parsed.operations || !Array.isArray(parsed.operations)) {
810
+ throw new Error("Invalid AI response: missing operations array");
811
+ }
812
+ return {
813
+ operations: parsed.operations,
814
+ message: parsed.message ?? "AI operations applied."
815
+ };
816
+ }
817
+ function evaluateCondition(condition, row) {
818
+ const rawVal = row[condition.column];
819
+ const target = condition.value;
820
+ switch (condition.op) {
821
+ case "eq":
822
+ return rawVal == target;
823
+ case "neq":
824
+ return rawVal != target;
825
+ case "gt":
826
+ return Number(rawVal) > Number(target);
827
+ case "gte":
828
+ return Number(rawVal) >= Number(target);
829
+ case "lt":
830
+ return Number(rawVal) < Number(target);
831
+ case "lte":
832
+ return Number(rawVal) <= Number(target);
833
+ case "contains":
834
+ return String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
835
+ case "notContains":
836
+ return !String(rawVal ?? "").toLowerCase().includes(String(target).toLowerCase());
837
+ case "startsWith":
838
+ return String(rawVal ?? "").toLowerCase().startsWith(String(target).toLowerCase());
839
+ case "endsWith":
840
+ return String(rawVal ?? "").toLowerCase().endsWith(String(target).toLowerCase());
841
+ case "in":
842
+ if (Array.isArray(target)) {
843
+ return target.some((t) => rawVal == t);
844
+ }
845
+ return false;
846
+ case "notIn":
847
+ if (Array.isArray(target)) {
848
+ return !target.some((t) => rawVal == t);
849
+ }
850
+ return true;
851
+ default:
852
+ return true;
853
+ }
854
+ }
855
+ function matchesConditions(conditions, logic, row) {
856
+ if (!conditions || conditions.length === 0) return true;
857
+ if (logic === "or") {
858
+ return conditions.some((c) => evaluateCondition(c, row));
859
+ }
860
+ return conditions.every((c) => evaluateCondition(c, row));
861
+ }
862
+ function applyAIFilter(data, op) {
863
+ return data.filter((row) => matchesConditions(op.conditions, op.logic, row));
864
+ }
865
+ function applyAISort(data, op) {
866
+ const dir = op.direction === "asc" ? 1 : -1;
867
+ const col = op.column;
868
+ return [...data].sort((a, b) => {
869
+ const aVal = a[col];
870
+ const bVal = b[col];
871
+ if (aVal == null && bVal == null) return 0;
872
+ if (aVal == null) return 1;
873
+ if (bVal == null) return -1;
874
+ if (typeof aVal === "number" && typeof bVal === "number")
875
+ return (aVal - bVal) * dir;
876
+ return String(aVal).localeCompare(String(bVal)) * dir;
877
+ });
878
+ }
879
+ function getAIRowStyle(row, ops) {
880
+ let merged;
881
+ for (const op of ops) {
882
+ if (matchesConditions(op.conditions, op.logic, row)) {
883
+ if (!merged) merged = {};
884
+ Object.assign(merged, op.style);
885
+ }
886
+ }
887
+ return merged;
888
+ }
889
+ function getAICellStyle(row, columnKey, ops) {
890
+ let merged;
891
+ for (const op of ops) {
892
+ if (op.column === columnKey && matchesConditions(op.conditions, op.logic, row)) {
893
+ if (!merged) merged = {};
894
+ Object.assign(merged, op.style);
895
+ }
896
+ }
897
+ return merged;
898
+ }
899
+ function applyAIOperations(data, operations) {
900
+ let filteredData = data;
901
+ let sortOp = null;
902
+ const styleOps = [];
903
+ const cellStyleOps = [];
904
+ const hideColumns = [];
905
+ const showColumns = [];
906
+ const resizeOps = [];
907
+ let reorderOp = null;
908
+ const pinOps = [];
909
+ let setPageOp = null;
910
+ for (const op of operations) {
911
+ switch (op.type) {
912
+ case "filter":
913
+ filteredData = applyAIFilter(filteredData, op);
914
+ break;
915
+ case "sort":
916
+ sortOp = op;
917
+ break;
918
+ case "rowStyle":
919
+ styleOps.push(op);
920
+ break;
921
+ case "cellStyle":
922
+ cellStyleOps.push(op);
923
+ break;
924
+ case "hideColumns":
925
+ hideColumns.push(...op.columns);
926
+ break;
927
+ case "showColumns":
928
+ showColumns.push(...op.columns);
929
+ break;
930
+ case "resizeColumn":
931
+ resizeOps.push(op);
932
+ break;
933
+ case "reorderColumns":
934
+ reorderOp = op;
935
+ break;
936
+ case "pinColumn":
937
+ pinOps.push(op);
938
+ break;
939
+ case "setPage":
940
+ setPageOp = op;
941
+ break;
942
+ }
943
+ }
944
+ if (sortOp) {
945
+ filteredData = applyAISort(filteredData, sortOp);
946
+ }
947
+ return { filteredData, sortOp, styleOps, cellStyleOps, hideColumns, showColumns, resizeOps, reorderOp, pinOps, setPageOp };
948
+ }
949
+
646
950
  // src/ResizeOverlay.tsx
647
951
  import { forwardRef, useImperativeHandle, useRef as useRef2 } from "react";
648
952
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -870,9 +1174,11 @@ var Cell = React3.memo(
870
1174
  isLoading,
871
1175
  onEdit,
872
1176
  isEditing,
873
- onEditComplete
1177
+ onEditComplete,
1178
+ cellStyleFn
874
1179
  }) => {
875
1180
  const isPinned = Boolean(column.pinned);
1181
+ const extraCellStyle = cellStyleFn?.(record, column.dataIndex ?? column.key);
876
1182
  if (isLoading && column.key !== "__select__" && column.key !== "__expand__") {
877
1183
  const shimmerContent = column.shimmerRender ? column.shimmerRender() : /* @__PURE__ */ jsx4(
878
1184
  "div",
@@ -1024,7 +1330,8 @@ var Cell = React3.memo(
1024
1330
  minWidth: 0,
1025
1331
  ...column.style,
1026
1332
  ...isPinned ? styles?.pinnedCell : void 0,
1027
- ...styles?.cell
1333
+ ...styles?.cell,
1334
+ ...extraCellStyle
1028
1335
  },
1029
1336
  children: isSystem ? content : showEditor ? content : /* @__PURE__ */ jsx4(
1030
1337
  "div",
@@ -1051,6 +1358,7 @@ var Cell = React3.memo(
1051
1358
  if (prev.onEdit !== next.onEdit) return false;
1052
1359
  if (prev.isEditing !== next.isEditing) return false;
1053
1360
  if (prev.onEditComplete !== next.onEditComplete) return false;
1361
+ if (prev.cellStyleFn !== next.cellStyleFn) return false;
1054
1362
  if (prev.column.key === "__select__") {
1055
1363
  return prev.isSelected === next.isSelected && prev.normalizedSelectedKeys === next.normalizedSelectedKeys;
1056
1364
  }
@@ -1152,7 +1460,8 @@ var TableBody = ({
1152
1460
  onEditComplete,
1153
1461
  enableDynamicRowHeight = false,
1154
1462
  onRowHeightChange,
1155
- columnGridIndexMap
1463
+ columnGridIndexMap,
1464
+ cellStyleFn
1156
1465
  }) => {
1157
1466
  const virtualItems = rowVirtualizer.getVirtualItems();
1158
1467
  const totalSize = rowVirtualizer.getTotalSize();
@@ -1281,7 +1590,8 @@ var TableBody = ({
1281
1590
  recordFingerprint,
1282
1591
  onEdit,
1283
1592
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1284
- onEditComplete
1593
+ onEditComplete,
1594
+ cellStyleFn
1285
1595
  }
1286
1596
  )
1287
1597
  }
@@ -1318,7 +1628,8 @@ var TableBody = ({
1318
1628
  recordFingerprint,
1319
1629
  onEdit,
1320
1630
  isEditing: editingCell?.rowKey === rowKey && editingCell?.columnKey === col.key,
1321
- onEditComplete
1631
+ onEditComplete,
1632
+ cellStyleFn
1322
1633
  }
1323
1634
  )
1324
1635
  }
@@ -1508,7 +1819,8 @@ var TableBody = ({
1508
1819
  recordFingerprint,
1509
1820
  onEdit,
1510
1821
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1511
- onEditComplete
1822
+ onEditComplete,
1823
+ cellStyleFn
1512
1824
  }
1513
1825
  )
1514
1826
  }
@@ -1636,7 +1948,8 @@ var TableBody = ({
1636
1948
  recordFingerprint,
1637
1949
  onEdit,
1638
1950
  isEditing: editingCell?.rowKey === rk && editingCell?.columnKey === col.key,
1639
- onEditComplete
1951
+ onEditComplete,
1952
+ cellStyleFn
1640
1953
  }
1641
1954
  )
1642
1955
  }
@@ -1731,7 +2044,13 @@ function BoltTable({
1731
2044
  globalSearchValue,
1732
2045
  onGlobalSearchChange,
1733
2046
  toolbarContent,
1734
- columnSettingsLabel
2047
+ columnSettingsLabel,
2048
+ aiMode = false,
2049
+ aiConfig,
2050
+ onAIQuery,
2051
+ onAIResponse,
2052
+ aiPlaceholder = "Ask AI anything about your data...",
2053
+ aiButtonLabel
1735
2054
  }) {
1736
2055
  const data = useMemo2(() => {
1737
2056
  if (!Array.isArray(rawData)) return STABLE_EMPTY_DATA;
@@ -1905,6 +2224,213 @@ function BoltTable({
1905
2224
  document.removeEventListener("keydown", onKey);
1906
2225
  };
1907
2226
  }, [showColumnPicker]);
2227
+ const [aiBarOpen, setAiBarOpen] = useState3(false);
2228
+ const [aiQuery, setAiQuery] = useState3("");
2229
+ const [aiLoading, setAiLoading] = useState3(false);
2230
+ const [aiResult, setAiResult] = useState3(null);
2231
+ const [aiError, setAiError] = useState3(null);
2232
+ const aiInputRef = useRef4(null);
2233
+ const [aiStyleOps, setAiStyleOps] = useState3([]);
2234
+ const [aiCellStyleOps, setAiCellStyleOps] = useState3(
2235
+ []
2236
+ );
2237
+ const [aiFilteredDataKeys, setAiFilteredDataKeys] = useState3(null);
2238
+ const [aiSortKey, setAiSortKey] = useState3(null);
2239
+ const [aiSortDir, setAiSortDir] = useState3(null);
2240
+ const aiFiltersStorageKey = columnPersistence && typeof columnPersistence === "object" ? `bt-ai-filters-${columnPersistence.storageKey}` : "bt-ai-filters";
2241
+ const [savedAIFilters, setSavedAIFilters] = useState3(() => {
2242
+ try {
2243
+ const raw = localStorage.getItem(aiFiltersStorageKey);
2244
+ return raw ? JSON.parse(raw) : [];
2245
+ } catch {
2246
+ return [];
2247
+ }
2248
+ });
2249
+ const [showSavedFilters, setShowSavedFilters] = useState3(false);
2250
+ const savedFiltersRef = useRef4(null);
2251
+ React4.useEffect(() => {
2252
+ if (!showSavedFilters) return;
2253
+ const close = (e) => {
2254
+ if (savedFiltersRef.current && !savedFiltersRef.current.contains(e.target)) {
2255
+ setShowSavedFilters(false);
2256
+ }
2257
+ };
2258
+ document.addEventListener("mousedown", close);
2259
+ return () => document.removeEventListener("mousedown", close);
2260
+ }, [showSavedFilters]);
2261
+ const saveCurrentAIFilter = useCallback2(() => {
2262
+ if (!aiResult) return;
2263
+ const label = aiQuery || aiResult.message;
2264
+ const entry = { label, operations: aiResult.operations, query: aiQuery };
2265
+ const next = [...savedAIFilters, entry];
2266
+ setSavedAIFilters(next);
2267
+ try {
2268
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2269
+ } catch {
2270
+ }
2271
+ }, [aiResult, aiQuery, savedAIFilters, aiFiltersStorageKey]);
2272
+ const removeSavedFilter = useCallback2((index) => {
2273
+ const next = savedAIFilters.filter((_, i) => i !== index);
2274
+ setSavedAIFilters(next);
2275
+ try {
2276
+ localStorage.setItem(aiFiltersStorageKey, JSON.stringify(next));
2277
+ } catch {
2278
+ }
2279
+ }, [savedAIFilters, aiFiltersStorageKey]);
2280
+ const applySavedFilter = useCallback2((filter) => {
2281
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, filter.operations);
2282
+ setAiStyleOps(sOps);
2283
+ setAiCellStyleOps(csOps);
2284
+ if (filter.operations.some((op) => op.type === "filter")) {
2285
+ const keySet = /* @__PURE__ */ new Set();
2286
+ filteredData.forEach((row, idx) => {
2287
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2288
+ keySet.add(k);
2289
+ });
2290
+ setAiFilteredDataKeys(keySet);
2291
+ } else {
2292
+ setAiFilteredDataKeys(null);
2293
+ }
2294
+ if (sortOp) {
2295
+ setAiSortKey(sortOp.column);
2296
+ setAiSortDir(sortOp.direction);
2297
+ } else {
2298
+ setAiSortKey(null);
2299
+ setAiSortDir(null);
2300
+ }
2301
+ if (hideCols.length > 0 || showCols.length > 0) {
2302
+ setColumns(
2303
+ (prev) => prev.map((col) => {
2304
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2305
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2306
+ return col;
2307
+ })
2308
+ );
2309
+ }
2310
+ for (const rOp of resizeOps) {
2311
+ const w = Math.max(40, Math.min(800, rOp.width));
2312
+ setColumnWidths((prev) => {
2313
+ const n = new Map(prev);
2314
+ n.set(rOp.column, w);
2315
+ return n;
2316
+ });
2317
+ onColumnResize?.(rOp.column, w);
2318
+ }
2319
+ if (reorderOp) {
2320
+ setColumnOrder(reorderOp.order);
2321
+ onColumnOrderChange?.(reorderOp.order);
2322
+ }
2323
+ for (const pOp of pinOps) {
2324
+ setColumns(
2325
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2326
+ );
2327
+ onColumnPin?.(pOp.column, pOp.pinned);
2328
+ }
2329
+ if (setPageOp) {
2330
+ setInternalPage(setPageOp.page);
2331
+ }
2332
+ setAiResult({ operations: filter.operations, message: `Applied saved filter: ${filter.label}` });
2333
+ setShowSavedFilters(false);
2334
+ }, [data, rowKey, onColumnResize, onColumnOrderChange, onColumnPin]);
2335
+ const onAIResponseRef = useRef4(onAIResponse);
2336
+ onAIResponseRef.current = onAIResponse;
2337
+ const handleAISubmit = useCallback2(async () => {
2338
+ const query = aiQuery.trim();
2339
+ if (!query) return;
2340
+ setAiLoading(true);
2341
+ setAiError(null);
2342
+ try {
2343
+ let response;
2344
+ if (onAIQuery) {
2345
+ response = await onAIQuery(query, {
2346
+ data,
2347
+ columns: initialColumns
2348
+ });
2349
+ } else if (aiConfig) {
2350
+ const sysPrompt = buildSystemPrompt(initialColumns, data);
2351
+ const raw = await callAI(aiConfig, sysPrompt, query);
2352
+ response = parseAIResponse(raw);
2353
+ } else {
2354
+ throw new Error("AI mode requires either aiConfig or onAIQuery prop");
2355
+ }
2356
+ const { filteredData, sortOp, styleOps: sOps, cellStyleOps: csOps, hideColumns: hideCols, showColumns: showCols, resizeOps, reorderOp, pinOps, setPageOp } = applyAIOperations(data, response.operations);
2357
+ setAiStyleOps(sOps);
2358
+ setAiCellStyleOps(csOps);
2359
+ if (response.operations.some((op) => op.type === "filter")) {
2360
+ const keySet = /* @__PURE__ */ new Set();
2361
+ filteredData.forEach((row, idx) => {
2362
+ const k = typeof rowKey === "function" ? rowKey(row) : String(row[typeof rowKey === "string" ? rowKey : "id"] ?? idx);
2363
+ keySet.add(k);
2364
+ });
2365
+ setAiFilteredDataKeys(keySet);
2366
+ } else {
2367
+ setAiFilteredDataKeys(null);
2368
+ }
2369
+ if (sortOp) {
2370
+ setAiSortKey(sortOp.column);
2371
+ setAiSortDir(sortOp.direction);
2372
+ } else {
2373
+ setAiSortKey(null);
2374
+ setAiSortDir(null);
2375
+ }
2376
+ if (hideCols.length > 0 || showCols.length > 0) {
2377
+ setColumns(
2378
+ (prev) => prev.map((col) => {
2379
+ if (hideCols.includes(col.key)) return { ...col, hidden: true };
2380
+ if (showCols.includes(col.key)) return { ...col, hidden: false };
2381
+ return col;
2382
+ })
2383
+ );
2384
+ }
2385
+ for (const rOp of resizeOps) {
2386
+ const w = Math.max(40, Math.min(800, rOp.width));
2387
+ setColumnWidths((prev) => {
2388
+ const n = new Map(prev);
2389
+ n.set(rOp.column, w);
2390
+ return n;
2391
+ });
2392
+ onColumnResize?.(rOp.column, w);
2393
+ }
2394
+ if (reorderOp) {
2395
+ setColumnOrder(reorderOp.order);
2396
+ onColumnOrderChange?.(reorderOp.order);
2397
+ }
2398
+ for (const pOp of pinOps) {
2399
+ setColumns(
2400
+ (prev) => prev.map((col) => col.key === pOp.column ? { ...col, pinned: pOp.pinned } : col)
2401
+ );
2402
+ onColumnPin?.(pOp.column, pOp.pinned);
2403
+ }
2404
+ if (setPageOp) {
2405
+ setInternalPage(setPageOp.page);
2406
+ }
2407
+ setAiResult(response);
2408
+ onAIResponseRef.current?.(response);
2409
+ } catch (err) {
2410
+ setAiError(err instanceof Error ? err.message : "AI query failed");
2411
+ } finally {
2412
+ setAiLoading(false);
2413
+ }
2414
+ }, [aiQuery, aiConfig, onAIQuery, data, initialColumns, rowKey]);
2415
+ const handleAIClear = useCallback2(() => {
2416
+ setAiResult(null);
2417
+ setAiError(null);
2418
+ setAiStyleOps([]);
2419
+ setAiCellStyleOps([]);
2420
+ setAiFilteredDataKeys(null);
2421
+ setAiSortKey(null);
2422
+ setAiSortDir(null);
2423
+ setAiQuery("");
2424
+ }, []);
2425
+ const handleAIBarClose = useCallback2(() => {
2426
+ setAiBarOpen(false);
2427
+ handleAIClear();
2428
+ }, [handleAIClear]);
2429
+ React4.useEffect(() => {
2430
+ if (aiBarOpen && aiInputRef.current) {
2431
+ setTimeout(() => aiInputRef.current?.focus(), 300);
2432
+ }
2433
+ }, [aiBarOpen]);
1908
2434
  const columnsWithPersistedWidths = useMemo2(
1909
2435
  () => columns.map((col) => ({
1910
2436
  ...col,
@@ -2546,6 +3072,47 @@ function BoltTable({
2546
3072
  }
2547
3073
  return result;
2548
3074
  }, [data, sortState, columnFilters, globalSearchValue, internalGlobalSearch]);
3075
+ const aiProcessedData = useMemo2(() => {
3076
+ let result = processedData;
3077
+ if (aiFilteredDataKeys) {
3078
+ result = result.filter((row, idx) => {
3079
+ if (row == null) return false;
3080
+ const k = typeof rowKey === "function" ? rowKey(row) : String(
3081
+ row[typeof rowKey === "string" ? rowKey : "id"] ?? idx
3082
+ );
3083
+ return aiFilteredDataKeys.has(k);
3084
+ });
3085
+ }
3086
+ if (aiSortKey && aiSortDir) {
3087
+ const dir = aiSortDir === "asc" ? 1 : -1;
3088
+ const col = aiSortKey;
3089
+ result = [...result].sort((a, b) => {
3090
+ const aVal = a[col];
3091
+ const bVal = b[col];
3092
+ if (aVal == null && bVal == null) return 0;
3093
+ if (aVal == null) return 1;
3094
+ if (bVal == null) return -1;
3095
+ if (typeof aVal === "number" && typeof bVal === "number")
3096
+ return (aVal - bVal) * dir;
3097
+ return String(aVal).localeCompare(String(bVal)) * dir;
3098
+ });
3099
+ }
3100
+ return result;
3101
+ }, [processedData, aiFilteredDataKeys, aiSortKey, aiSortDir, rowKey]);
3102
+ const getAIRowStyleForRecord = useCallback2(
3103
+ (record) => {
3104
+ if (aiStyleOps.length === 0) return void 0;
3105
+ return getAIRowStyle(record, aiStyleOps);
3106
+ },
3107
+ [aiStyleOps]
3108
+ );
3109
+ const getAICellStyleForRecord = useCallback2(
3110
+ (record, columnKey) => {
3111
+ if (aiCellStyleOps.length === 0) return void 0;
3112
+ return getAICellStyle(record, columnKey, aiCellStyleOps);
3113
+ },
3114
+ [aiCellStyleOps]
3115
+ );
2549
3116
  const pinnedRowCacheRef = useRef4(/* @__PURE__ */ new Map());
2550
3117
  const { pinnedTopRows, pinnedBottomRows, unpinnedProcessedData } = useMemo2(() => {
2551
3118
  if (!resolvedRowPinning || !resolvedRowPinning.top?.length && !resolvedRowPinning.bottom?.length) {
@@ -2553,7 +3120,7 @@ function BoltTable({
2553
3120
  return {
2554
3121
  pinnedTopRows: [],
2555
3122
  pinnedBottomRows: [],
2556
- unpinnedProcessedData: processedData
3123
+ unpinnedProcessedData: aiProcessedData
2557
3124
  };
2558
3125
  }
2559
3126
  const topKeySet = new Set((resolvedRowPinning.top ?? []).map(String));
@@ -2563,7 +3130,7 @@ function BoltTable({
2563
3130
  const topMap = /* @__PURE__ */ new Map();
2564
3131
  const bottomMap = /* @__PURE__ */ new Map();
2565
3132
  const rest = [];
2566
- processedData.forEach((row, idx) => {
3133
+ aiProcessedData.forEach((row, idx) => {
2567
3134
  if (row == null) return;
2568
3135
  const key = getSafeRowKey(row, idx);
2569
3136
  if (topKeySet.has(key)) {
@@ -2603,7 +3170,7 @@ function BoltTable({
2603
3170
  unpinnedProcessedData: rest
2604
3171
  };
2605
3172
  }, [
2606
- processedData,
3173
+ aiProcessedData,
2607
3174
  resolvedRowPinning,
2608
3175
  getSafeRowKey,
2609
3176
  keepPinnedRowsAcrossPages
@@ -2672,7 +3239,7 @@ function BoltTable({
2672
3239
  return unpinnedProcessedData.slice(start, start + pgSize);
2673
3240
  }, [unpinnedProcessedData, needsClientPagination, pgCurrent, pgSize]);
2674
3241
  const shimmerCount = pgEnabled ? pgSize : 15;
2675
- const showShimmer = isLoading && processedData.length === 0;
3242
+ const showShimmer = isLoading && aiProcessedData.length === 0;
2676
3243
  const shimmerRowKeyField = typeof rowKey === "string" ? rowKey : "id";
2677
3244
  const shimmerData = useMemo2(() => {
2678
3245
  if (!showShimmer) return null;
@@ -2956,49 +3523,395 @@ function BoltTable({
2956
3523
  border: 1px dashed ${accentColor} !important;
2957
3524
  }
2958
3525
  ${onRowClick ? "[data-bt-cell] { cursor: pointer; }" : ""}
3526
+ @keyframes bt-spin { to { transform: rotate(360deg); } }
3527
+ @keyframes bt-ai-shimmer {
3528
+ 0% { background-position: -200% 0; }
3529
+ 100% { background-position: 200% 0; }
3530
+ }
2959
3531
  ` }),
2960
- (!hideGlobalSearch || showColumnSettings) && /* @__PURE__ */ jsxs5(
3532
+ (!hideGlobalSearch || showColumnSettings || aiMode) && /* @__PURE__ */ jsxs5(
2961
3533
  "div",
2962
3534
  {
2963
3535
  style: {
3536
+ position: "relative",
2964
3537
  display: "flex",
2965
3538
  alignItems: "center",
2966
3539
  gap: 8,
2967
3540
  padding: "6px 8px",
2968
3541
  borderBottom: "1px solid rgba(128,128,128,0.2)",
2969
3542
  fontSize: 12,
2970
- flexShrink: 0
3543
+ flexShrink: 0,
3544
+ zIndex: 20
2971
3545
  },
2972
3546
  children: [
2973
- !hideGlobalSearch && /* @__PURE__ */ jsxs5(
3547
+ /* @__PURE__ */ jsxs5(
2974
3548
  "div",
2975
3549
  {
2976
3550
  style: {
2977
3551
  display: "flex",
2978
3552
  alignItems: "center",
2979
- gap: 4,
3553
+ gap: 8,
2980
3554
  flex: "1 1 0%",
2981
- position: "relative"
3555
+ opacity: aiBarOpen ? 0 : 1,
3556
+ transform: aiBarOpen ? "scale(0.97)" : "scale(1)",
3557
+ transition: "opacity 0.25s ease, transform 0.25s ease",
3558
+ pointerEvents: aiBarOpen ? "none" : "auto",
3559
+ minWidth: 0
3560
+ },
3561
+ children: [
3562
+ !hideGlobalSearch && /* @__PURE__ */ jsxs5(
3563
+ "div",
3564
+ {
3565
+ style: {
3566
+ display: "flex",
3567
+ alignItems: "center",
3568
+ gap: 4,
3569
+ flex: "1 1 0%",
3570
+ position: "relative"
3571
+ },
3572
+ children: [
3573
+ /* @__PURE__ */ jsx5(
3574
+ "span",
3575
+ {
3576
+ style: {
3577
+ display: "flex",
3578
+ color: "GrayText",
3579
+ flexShrink: 0
3580
+ },
3581
+ children: icons?.search ?? /* @__PURE__ */ jsx5(SearchIcon, { style: { width: 14, height: 14 } })
3582
+ }
3583
+ ),
3584
+ /* @__PURE__ */ jsx5(
3585
+ "input",
3586
+ {
3587
+ type: "text",
3588
+ placeholder: "Search all columns...",
3589
+ value: globalSearchValue ?? internalGlobalSearch,
3590
+ onChange: (e) => {
3591
+ const v = e.target.value;
3592
+ if (onGlobalSearchChange) onGlobalSearchChange(v);
3593
+ else setInternalGlobalSearch(v);
3594
+ },
3595
+ style: {
3596
+ flex: "1 1 0%",
3597
+ border: "none",
3598
+ outline: "none",
3599
+ background: "transparent",
3600
+ font: "inherit",
3601
+ color: "inherit",
3602
+ padding: "4px 6px",
3603
+ minWidth: 0
3604
+ }
3605
+ }
3606
+ ),
3607
+ (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ jsx5(
3608
+ "button",
3609
+ {
3610
+ type: "button",
3611
+ onClick: () => {
3612
+ if (onGlobalSearchChange) onGlobalSearchChange("");
3613
+ else setInternalGlobalSearch("");
3614
+ },
3615
+ style: {
3616
+ display: "flex",
3617
+ alignItems: "center",
3618
+ justifyContent: "center",
3619
+ background: "none",
3620
+ border: "none",
3621
+ cursor: "pointer",
3622
+ padding: 2,
3623
+ color: "GrayText",
3624
+ flexShrink: 0
3625
+ },
3626
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
3627
+ }
3628
+ )
3629
+ ]
3630
+ }
3631
+ ),
3632
+ toolbarContent,
3633
+ showColumnSettings && /* @__PURE__ */ jsxs5("div", { style: { position: "relative", flexShrink: 0 }, children: [
3634
+ /* @__PURE__ */ jsxs5(
3635
+ "button",
3636
+ {
3637
+ type: "button",
3638
+ onClick: () => setShowColumnPicker((p) => !p),
3639
+ style: {
3640
+ display: "flex",
3641
+ alignItems: "center",
3642
+ justifyContent: "center",
3643
+ background: "none",
3644
+ border: "1px solid rgba(128,128,128,0.2)",
3645
+ borderRadius: 4,
3646
+ cursor: "pointer",
3647
+ padding: "4px 6px",
3648
+ color: "inherit",
3649
+ gap: 4,
3650
+ fontSize: 12
3651
+ },
3652
+ title: "Column settings",
3653
+ children: [
3654
+ icons?.columns ?? /* @__PURE__ */ jsx5(ColumnsIcon, { style: { width: 14, height: 14 } }),
3655
+ /* @__PURE__ */ jsx5("span", { children: columnSettingsLabel ?? "Columns" })
3656
+ ]
3657
+ }
3658
+ ),
3659
+ showColumnPicker && /* @__PURE__ */ jsx5(
3660
+ "div",
3661
+ {
3662
+ ref: columnPickerRef,
3663
+ style: {
3664
+ position: "absolute",
3665
+ top: "100%",
3666
+ right: 0,
3667
+ zIndex: 99999,
3668
+ minWidth: 200,
3669
+ maxHeight: 320,
3670
+ overflowY: "auto",
3671
+ borderRadius: 8,
3672
+ border: "1px solid rgba(128,128,128,0.2)",
3673
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3674
+ backdropFilter: "blur(16px)",
3675
+ WebkitBackdropFilter: "blur(16px)",
3676
+ backgroundColor: "rgba(128,128,128,0.08)",
3677
+ padding: "4px 0",
3678
+ marginTop: 4
3679
+ },
3680
+ children: initialColumns.filter(
3681
+ (c) => c.key !== "__select__" && c.key !== "__expand__"
3682
+ ).map((col) => {
3683
+ const current = columns.find(
3684
+ (c) => c.key === col.key
3685
+ );
3686
+ const isHidden = current?.hidden ?? false;
3687
+ const isPinned = !!current?.pinned;
3688
+ return /* @__PURE__ */ jsxs5(
3689
+ "label",
3690
+ {
3691
+ style: {
3692
+ display: "flex",
3693
+ alignItems: "center",
3694
+ gap: 8,
3695
+ padding: "6px 12px",
3696
+ cursor: isPinned ? "not-allowed" : "pointer",
3697
+ opacity: isPinned ? 0.5 : 1,
3698
+ fontSize: 12
3699
+ },
3700
+ children: [
3701
+ /* @__PURE__ */ jsx5(
3702
+ "input",
3703
+ {
3704
+ type: "checkbox",
3705
+ checked: !isHidden,
3706
+ disabled: isPinned,
3707
+ onChange: () => {
3708
+ if (isPinned) return;
3709
+ handleToggleHide(col.key);
3710
+ },
3711
+ style: {
3712
+ cursor: isPinned ? "not-allowed" : "pointer",
3713
+ accentColor
3714
+ }
3715
+ }
3716
+ ),
3717
+ /* @__PURE__ */ jsx5(
3718
+ "span",
3719
+ {
3720
+ style: {
3721
+ overflow: "hidden",
3722
+ textOverflow: "ellipsis",
3723
+ whiteSpace: "nowrap"
3724
+ },
3725
+ children: typeof col.title === "string" ? col.title : col.key
3726
+ }
3727
+ )
3728
+ ]
3729
+ },
3730
+ col.key
3731
+ );
3732
+ })
3733
+ }
3734
+ )
3735
+ ] }),
3736
+ aiMode && savedAIFilters.length > 0 && /* @__PURE__ */ jsxs5("div", { ref: savedFiltersRef, style: { position: "relative", flexShrink: 0 }, children: [
3737
+ /* @__PURE__ */ jsxs5(
3738
+ "button",
3739
+ {
3740
+ type: "button",
3741
+ onClick: () => setShowSavedFilters((p) => !p),
3742
+ style: {
3743
+ display: "flex",
3744
+ alignItems: "center",
3745
+ gap: 4,
3746
+ background: "none",
3747
+ border: "1px solid rgba(128,128,128,0.2)",
3748
+ borderRadius: 4,
3749
+ cursor: "pointer",
3750
+ padding: "4px 6px",
3751
+ color: "inherit",
3752
+ fontSize: 12
3753
+ },
3754
+ title: "Saved AI filters",
3755
+ children: [
3756
+ /* @__PURE__ */ jsx5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) }),
3757
+ /* @__PURE__ */ jsx5("span", { children: savedAIFilters.length })
3758
+ ]
3759
+ }
3760
+ ),
3761
+ showSavedFilters && /* @__PURE__ */ jsxs5(
3762
+ "div",
3763
+ {
3764
+ style: {
3765
+ position: "absolute",
3766
+ top: "100%",
3767
+ right: 0,
3768
+ zIndex: 99999,
3769
+ minWidth: 240,
3770
+ maxWidth: 360,
3771
+ maxHeight: 320,
3772
+ overflowY: "auto",
3773
+ borderRadius: 8,
3774
+ border: "1px solid rgba(128,128,128,0.2)",
3775
+ boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3776
+ backdropFilter: "blur(16px)",
3777
+ WebkitBackdropFilter: "blur(16px)",
3778
+ backgroundColor: "rgba(128,128,128,0.08)",
3779
+ padding: "4px 0",
3780
+ marginTop: 4
3781
+ },
3782
+ children: [
3783
+ /* @__PURE__ */ jsx5("div", { style: { padding: "6px 12px", fontSize: 11, opacity: 0.5, fontWeight: 600 }, children: "Saved Filters" }),
3784
+ savedAIFilters.map((f, i) => /* @__PURE__ */ jsxs5(
3785
+ "div",
3786
+ {
3787
+ style: {
3788
+ display: "flex",
3789
+ alignItems: "center",
3790
+ gap: 6,
3791
+ padding: "6px 12px",
3792
+ cursor: "pointer",
3793
+ fontSize: 12
3794
+ },
3795
+ onMouseEnter: (e) => {
3796
+ e.currentTarget.style.backgroundColor = "rgba(128,128,128,0.15)";
3797
+ },
3798
+ onMouseLeave: (e) => {
3799
+ e.currentTarget.style.backgroundColor = "transparent";
3800
+ },
3801
+ children: [
3802
+ /* @__PURE__ */ jsx5(
3803
+ "span",
3804
+ {
3805
+ style: { flex: "1 1 0%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
3806
+ onClick: () => applySavedFilter(f),
3807
+ children: f.label
3808
+ }
3809
+ ),
3810
+ /* @__PURE__ */ jsx5(
3811
+ "button",
3812
+ {
3813
+ type: "button",
3814
+ onClick: (e) => {
3815
+ e.stopPropagation();
3816
+ removeSavedFilter(i);
3817
+ },
3818
+ style: {
3819
+ display: "flex",
3820
+ alignItems: "center",
3821
+ background: "none",
3822
+ border: "none",
3823
+ cursor: "pointer",
3824
+ padding: 2,
3825
+ color: "GrayText",
3826
+ flexShrink: 0
3827
+ },
3828
+ title: "Remove",
3829
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 10, height: 10 } })
3830
+ }
3831
+ )
3832
+ ]
3833
+ },
3834
+ i
3835
+ ))
3836
+ ]
3837
+ }
3838
+ )
3839
+ ] }),
3840
+ aiMode && /* @__PURE__ */ jsxs5(
3841
+ "button",
3842
+ {
3843
+ type: "button",
3844
+ onClick: () => setAiBarOpen(true),
3845
+ style: {
3846
+ display: "flex",
3847
+ alignItems: "center",
3848
+ justifyContent: "center",
3849
+ gap: 4,
3850
+ background: `linear-gradient(135deg, ${accentColor}18, ${accentColor}08)`,
3851
+ border: `1px solid ${accentColor}40`,
3852
+ borderRadius: 4,
3853
+ cursor: "pointer",
3854
+ padding: "4px 8px",
3855
+ color: accentColor,
3856
+ fontSize: 12,
3857
+ fontWeight: 500,
3858
+ flexShrink: 0,
3859
+ transition: "all 0.2s ease"
3860
+ },
3861
+ title: "Ask AI",
3862
+ children: [
3863
+ icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 14, height: 14 } }),
3864
+ /* @__PURE__ */ jsx5("span", { children: aiButtonLabel ?? "Ask AI" })
3865
+ ]
3866
+ }
3867
+ )
3868
+ ]
3869
+ }
3870
+ ),
3871
+ aiMode && /* @__PURE__ */ jsxs5(
3872
+ "div",
3873
+ {
3874
+ style: {
3875
+ position: "absolute",
3876
+ inset: 0,
3877
+ display: "flex",
3878
+ alignItems: "center",
3879
+ gap: 8,
3880
+ padding: "4px 8px",
3881
+ opacity: aiBarOpen ? 1 : 0,
3882
+ transform: aiBarOpen ? "translateX(0)" : "translateX(40px)",
3883
+ transition: "opacity 0.3s cubic-bezier(0.4,0,0.2,1), transform 0.3s cubic-bezier(0.4,0,0.2,1)",
3884
+ pointerEvents: aiBarOpen ? "auto" : "none",
3885
+ zIndex: 2,
3886
+ background: "inherit",
3887
+ backdropFilter: "blur(12px)",
3888
+ WebkitBackdropFilter: "blur(12px)"
2982
3889
  },
2983
3890
  children: [
2984
3891
  /* @__PURE__ */ jsx5(
2985
3892
  "span",
2986
3893
  {
2987
- style: { display: "flex", color: "GrayText", flexShrink: 0 },
2988
- children: icons?.search ?? /* @__PURE__ */ jsx5(SearchIcon, { style: { width: 14, height: 14 } })
3894
+ style: {
3895
+ display: "flex",
3896
+ color: accentColor,
3897
+ flexShrink: 0
3898
+ },
3899
+ children: icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 16, height: 16 } })
2989
3900
  }
2990
3901
  ),
2991
3902
  /* @__PURE__ */ jsx5(
2992
3903
  "input",
2993
3904
  {
3905
+ ref: aiInputRef,
2994
3906
  type: "text",
2995
- placeholder: "Search all columns...",
2996
- value: globalSearchValue ?? internalGlobalSearch,
2997
- onChange: (e) => {
2998
- const v = e.target.value;
2999
- if (onGlobalSearchChange) onGlobalSearchChange(v);
3000
- else setInternalGlobalSearch(v);
3907
+ placeholder: aiPlaceholder,
3908
+ value: aiQuery,
3909
+ onChange: (e) => setAiQuery(e.target.value),
3910
+ onKeyDown: (e) => {
3911
+ if (e.key === "Enter" && !aiLoading) handleAISubmit();
3912
+ if (e.key === "Escape") handleAIBarClose();
3001
3913
  },
3914
+ disabled: aiLoading,
3002
3915
  style: {
3003
3916
  flex: "1 1 0%",
3004
3917
  border: "none",
@@ -3007,18 +3920,51 @@ function BoltTable({
3007
3920
  font: "inherit",
3008
3921
  color: "inherit",
3009
3922
  padding: "4px 6px",
3010
- minWidth: 0
3923
+ minWidth: 0,
3924
+ fontSize: 12
3011
3925
  }
3012
3926
  }
3013
3927
  ),
3014
- (globalSearchValue ?? internalGlobalSearch) && /* @__PURE__ */ jsx5(
3928
+ /* @__PURE__ */ jsx5(
3015
3929
  "button",
3016
3930
  {
3017
3931
  type: "button",
3018
- onClick: () => {
3019
- if (onGlobalSearchChange) onGlobalSearchChange("");
3020
- else setInternalGlobalSearch("");
3932
+ onClick: handleAISubmit,
3933
+ disabled: aiLoading || !aiQuery.trim(),
3934
+ style: {
3935
+ display: "flex",
3936
+ alignItems: "center",
3937
+ justifyContent: "center",
3938
+ background: aiQuery.trim() ? accentColor : "rgba(128,128,128,0.15)",
3939
+ border: "none",
3940
+ borderRadius: 4,
3941
+ cursor: aiLoading || !aiQuery.trim() ? "not-allowed" : "pointer",
3942
+ padding: "4px 8px",
3943
+ color: aiQuery.trim() ? "#fff" : "GrayText",
3944
+ transition: "all 0.2s ease",
3945
+ flexShrink: 0,
3946
+ gap: 4,
3947
+ fontSize: 12,
3948
+ opacity: aiLoading ? 0.7 : 1
3021
3949
  },
3950
+ title: "Send",
3951
+ children: aiLoading ? /* @__PURE__ */ jsx5(Fragment4, { children: icons?.loader ?? /* @__PURE__ */ jsx5(
3952
+ LoaderIcon,
3953
+ {
3954
+ style: {
3955
+ width: 14,
3956
+ height: 14,
3957
+ animation: "bt-spin 1s linear infinite"
3958
+ }
3959
+ }
3960
+ ) }) : /* @__PURE__ */ jsx5(Fragment4, { children: icons?.send ?? /* @__PURE__ */ jsx5(SendIcon, { style: { width: 14, height: 14 } }) })
3961
+ }
3962
+ ),
3963
+ /* @__PURE__ */ jsx5(
3964
+ "button",
3965
+ {
3966
+ type: "button",
3967
+ onClick: handleAIBarClose,
3022
3968
  style: {
3023
3969
  display: "flex",
3024
3970
  alignItems: "center",
@@ -3030,114 +3976,127 @@ function BoltTable({
3030
3976
  color: "GrayText",
3031
3977
  flexShrink: 0
3032
3978
  },
3033
- children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
3979
+ title: "Close AI",
3980
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 14, height: 14 } })
3034
3981
  }
3035
3982
  )
3036
3983
  ]
3037
3984
  }
3985
+ )
3986
+ ]
3987
+ }
3988
+ ),
3989
+ aiResult && /* @__PURE__ */ jsxs5(
3990
+ "div",
3991
+ {
3992
+ style: {
3993
+ display: "flex",
3994
+ alignItems: "center",
3995
+ gap: 8,
3996
+ padding: "6px 12px",
3997
+ fontSize: 12,
3998
+ borderBottom: "1px solid rgba(128,128,128,0.2)",
3999
+ background: `linear-gradient(90deg, ${accentColor}08, transparent)`,
4000
+ flexShrink: 0
4001
+ },
4002
+ children: [
4003
+ /* @__PURE__ */ jsx5("span", { style: { color: accentColor, display: "flex", flexShrink: 0 }, children: icons?.sparkles ?? /* @__PURE__ */ jsx5(SparklesIcon, { style: { width: 14, height: 14 } }) }),
4004
+ /* @__PURE__ */ jsx5("span", { style: { flex: "1 1 0%", opacity: 0.85 }, children: aiResult.message }),
4005
+ /* @__PURE__ */ jsxs5(
4006
+ "button",
4007
+ {
4008
+ type: "button",
4009
+ onClick: saveCurrentAIFilter,
4010
+ style: {
4011
+ display: "flex",
4012
+ alignItems: "center",
4013
+ gap: 4,
4014
+ background: `${accentColor}12`,
4015
+ border: `1px solid ${accentColor}30`,
4016
+ borderRadius: 4,
4017
+ cursor: "pointer",
4018
+ padding: "2px 8px",
4019
+ color: accentColor,
4020
+ fontSize: 11,
4021
+ flexShrink: 0,
4022
+ fontWeight: 500,
4023
+ transition: "all 0.2s ease"
4024
+ },
4025
+ title: "Save this filter for quick access later",
4026
+ children: [
4027
+ /* @__PURE__ */ jsxs5("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
4028
+ /* @__PURE__ */ jsx5("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
4029
+ /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
4030
+ /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
4031
+ ] }),
4032
+ /* @__PURE__ */ jsx5("span", { children: "Save Filter" })
4033
+ ]
4034
+ }
3038
4035
  ),
3039
- toolbarContent,
3040
- showColumnSettings && /* @__PURE__ */ jsxs5("div", { style: { position: "relative", flexShrink: 0 }, children: [
3041
- /* @__PURE__ */ jsxs5(
3042
- "button",
3043
- {
3044
- type: "button",
3045
- onClick: () => setShowColumnPicker((p) => !p),
3046
- style: {
3047
- display: "flex",
3048
- alignItems: "center",
3049
- justifyContent: "center",
3050
- background: "none",
3051
- border: "1px solid rgba(128,128,128,0.2)",
3052
- borderRadius: 4,
3053
- cursor: "pointer",
3054
- padding: "4px 6px",
3055
- color: "inherit",
3056
- gap: 4,
3057
- fontSize: 12
3058
- },
3059
- title: "Column settings",
3060
- children: [
3061
- icons?.columns ?? /* @__PURE__ */ jsx5(ColumnsIcon, { style: { width: 14, height: 14 } }),
3062
- /* @__PURE__ */ jsx5("span", { children: columnSettingsLabel ?? "Columns" })
3063
- ]
3064
- }
3065
- ),
3066
- showColumnPicker && /* @__PURE__ */ jsx5(
3067
- "div",
3068
- {
3069
- ref: columnPickerRef,
3070
- style: {
3071
- position: "absolute",
3072
- top: "100%",
3073
- right: 0,
3074
- zIndex: 99999,
3075
- minWidth: 200,
3076
- maxHeight: 320,
3077
- overflowY: "auto",
3078
- borderRadius: 8,
3079
- border: "1px solid rgba(128,128,128,0.2)",
3080
- boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
3081
- backdropFilter: "blur(16px)",
3082
- WebkitBackdropFilter: "blur(16px)",
3083
- backgroundColor: "rgba(128,128,128,0.08)",
3084
- padding: "4px 0",
3085
- marginTop: 4
3086
- },
3087
- children: initialColumns.filter(
3088
- (c) => c.key !== "__select__" && c.key !== "__expand__"
3089
- ).map((col) => {
3090
- const current = columns.find((c) => c.key === col.key);
3091
- const isHidden = current?.hidden ?? false;
3092
- const isPinned = !!current?.pinned;
3093
- return /* @__PURE__ */ jsxs5(
3094
- "label",
3095
- {
3096
- style: {
3097
- display: "flex",
3098
- alignItems: "center",
3099
- gap: 8,
3100
- padding: "6px 12px",
3101
- cursor: isPinned ? "not-allowed" : "pointer",
3102
- opacity: isPinned ? 0.5 : 1,
3103
- fontSize: 12
3104
- },
3105
- children: [
3106
- /* @__PURE__ */ jsx5(
3107
- "input",
3108
- {
3109
- type: "checkbox",
3110
- checked: !isHidden,
3111
- disabled: isPinned,
3112
- onChange: () => {
3113
- if (isPinned) return;
3114
- handleToggleHide(col.key);
3115
- },
3116
- style: {
3117
- cursor: isPinned ? "not-allowed" : "pointer",
3118
- accentColor
3119
- }
3120
- }
3121
- ),
3122
- /* @__PURE__ */ jsx5(
3123
- "span",
3124
- {
3125
- style: {
3126
- overflow: "hidden",
3127
- textOverflow: "ellipsis",
3128
- whiteSpace: "nowrap"
3129
- },
3130
- children: typeof col.title === "string" ? col.title : col.key
3131
- }
3132
- )
3133
- ]
3134
- },
3135
- col.key
3136
- );
3137
- })
3138
- }
3139
- )
3140
- ] })
4036
+ /* @__PURE__ */ jsxs5(
4037
+ "button",
4038
+ {
4039
+ type: "button",
4040
+ onClick: handleAIClear,
4041
+ style: {
4042
+ display: "flex",
4043
+ alignItems: "center",
4044
+ gap: 4,
4045
+ background: "none",
4046
+ border: "1px solid rgba(128,128,128,0.2)",
4047
+ borderRadius: 4,
4048
+ cursor: "pointer",
4049
+ padding: "2px 8px",
4050
+ color: "inherit",
4051
+ fontSize: 11,
4052
+ flexShrink: 0,
4053
+ opacity: 0.7
4054
+ },
4055
+ title: "Clear AI results",
4056
+ children: [
4057
+ icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 10, height: 10 } }),
4058
+ /* @__PURE__ */ jsx5("span", { children: "Clear" })
4059
+ ]
4060
+ }
4061
+ )
4062
+ ]
4063
+ }
4064
+ ),
4065
+ aiError && !aiResult && /* @__PURE__ */ jsxs5(
4066
+ "div",
4067
+ {
4068
+ style: {
4069
+ display: "flex",
4070
+ alignItems: "center",
4071
+ gap: 8,
4072
+ padding: "6px 12px",
4073
+ fontSize: 12,
4074
+ borderBottom: "1px solid rgba(239,68,68,0.2)",
4075
+ background: "rgba(239,68,68,0.06)",
4076
+ color: "#ef4444",
4077
+ flexShrink: 0
4078
+ },
4079
+ children: [
4080
+ /* @__PURE__ */ jsx5("span", { style: { flex: "1 1 0%" }, children: aiError }),
4081
+ /* @__PURE__ */ jsx5(
4082
+ "button",
4083
+ {
4084
+ type: "button",
4085
+ onClick: () => setAiError(null),
4086
+ style: {
4087
+ display: "flex",
4088
+ alignItems: "center",
4089
+ justifyContent: "center",
4090
+ background: "none",
4091
+ border: "none",
4092
+ cursor: "pointer",
4093
+ padding: 2,
4094
+ color: "#ef4444",
4095
+ flexShrink: 0
4096
+ },
4097
+ children: icons?.close ?? /* @__PURE__ */ jsx5(XIcon, { style: { width: 12, height: 12 } })
4098
+ }
4099
+ )
3141
4100
  ]
3142
4101
  }
3143
4102
  ),
@@ -3639,14 +4598,20 @@ function BoltTable({
3639
4598
  gridTemplateColumns,
3640
4599
  headerHeight: HEADER_HEIGHT,
3641
4600
  rowClassName,
3642
- rowStyle,
4601
+ rowStyle: aiStyleOps.length > 0 ? (record, index) => {
4602
+ const base = rowStyle ? rowStyle(record, index) : void 0;
4603
+ const ai = getAIRowStyleForRecord(record);
4604
+ if (!base && !ai) return {};
4605
+ return { ...base, ...ai };
4606
+ } : rowStyle,
3643
4607
  bodyGridRow: hasColumnGroups ? 3 : 2,
3644
4608
  onEdit,
3645
4609
  editingCell,
3646
4610
  onEditComplete: handleEditComplete,
3647
4611
  enableDynamicRowHeight,
3648
4612
  onRowHeightChange: handleRowHeightChange,
3649
- columnGridIndexMap
4613
+ columnGridIndexMap,
4614
+ cellStyleFn: aiCellStyleOps.length > 0 ? (record, columnKey) => getAICellStyleForRecord(record, columnKey) : void 0
3650
4615
  }
3651
4616
  )
3652
4617
  ]
@@ -4264,9 +5229,15 @@ function BoltTable({
4264
5229
  })()
4265
5230
  ] });
4266
5231
  }
5232
+
5233
+ // src/types.ts
5234
+ function defineConfig(config) {
5235
+ return config;
5236
+ }
4267
5237
  export {
4268
5238
  BoltTable,
4269
5239
  DraggableHeader_default as DraggableHeader,
4270
5240
  ResizeOverlay_default as ResizeOverlay,
4271
- TableBody_default as TableBody
5241
+ TableBody_default as TableBody,
5242
+ defineConfig
4272
5243
  };