@opencorestack/opengridx 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +6 -0
  3. package/dist/ai.cjs +2 -1
  4. package/dist/ai.cjs.map +1 -1
  5. package/dist/ai.d.ts +203 -3
  6. package/dist/ai.es.js +2 -1
  7. package/dist/ai.es.js.map +1 -1
  8. package/dist/index.d.ts +248 -4
  9. package/dist/opengridx.css +1 -1
  10. package/dist/opengridx.es.js +7104 -5726
  11. package/dist/opengridx.es.js.map +1 -1
  12. package/dist/opengridx.umd.js +16 -16
  13. package/dist/opengridx.umd.js.map +1 -1
  14. package/docs/API_REFERENCE.md +88 -7
  15. package/docs/README.md +3 -1
  16. package/docs/architecture/datagrid-orchestration.md +9 -4
  17. package/docs/components/filter-panel.md +1 -0
  18. package/docs/components/header.md +4 -1
  19. package/docs/components/toolbar.md +7 -0
  20. package/docs/customization/theming.md +3 -1
  21. package/docs/features/ai-toolkit.md +99 -5
  22. package/docs/features/cell-selection.md +8 -2
  23. package/docs/features/fill-handle.md +125 -0
  24. package/docs/features/filtering.md +3 -0
  25. package/docs/features/header-filters.md +138 -0
  26. package/docs/features/keyboard-navigation.md +20 -2
  27. package/docs/roadmap.md +2 -3
  28. package/lib/ai/agentTools.ts +160 -0
  29. package/lib/ai/index.ts +19 -2
  30. package/lib/ai/promptHandler.ts +109 -0
  31. package/lib/ai/types.ts +89 -25
  32. package/lib/ai/validate.ts +10 -0
  33. package/lib/components/AiAssistant/GridAiAssistantArea.tsx +26 -0
  34. package/lib/components/AiAssistant/GridAiAssistantPanel.css +171 -0
  35. package/lib/components/AiAssistant/GridAiAssistantPanel.tsx +145 -0
  36. package/lib/components/AiAssistant/useSpeechInput.ts +74 -0
  37. package/lib/components/Cell/Cell.css +28 -0
  38. package/lib/components/Cell/Cell.tsx +6 -2
  39. package/lib/components/DataGrid/DataGrid.tsx +76 -3
  40. package/lib/components/DataGrid/GridLiveRegion.tsx +5 -2
  41. package/lib/components/DataGrid/GridPinnedRows.tsx +5 -1
  42. package/lib/components/DataGrid/GridVirtualRows.tsx +5 -1
  43. package/lib/components/FilterPanel/FilterPanel.tsx +2 -9
  44. package/lib/components/Header/Header.tsx +44 -23
  45. package/lib/components/Header/headerColumnStyle.ts +28 -0
  46. package/lib/components/HeaderFilter/HeaderFilter.css +160 -0
  47. package/lib/components/HeaderFilter/HeaderFilterCell.tsx +370 -0
  48. package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +124 -0
  49. package/lib/components/HeaderFilter/HeaderFilterRow.tsx +157 -0
  50. package/lib/components/Toolbar/GridToolbar.tsx +51 -0
  51. package/lib/hooks/core/gridApiPlaceholder.ts +8 -0
  52. package/lib/hooks/core/gridToolbarHostContext.ts +5 -0
  53. package/lib/hooks/core/useGridApiMethods.ts +39 -0
  54. package/lib/hooks/core/useGridAriaRows.ts +5 -3
  55. package/lib/hooks/core/useGridCellSelection.ts +77 -11
  56. package/lib/hooks/core/useGridColumnsPanel.ts +24 -1
  57. package/lib/hooks/core/useGridControlledState.ts +27 -0
  58. package/lib/hooks/core/useGridFillHandle.ts +323 -0
  59. package/lib/hooks/core/useGridKeyboardNavigation.ts +29 -10
  60. package/lib/hooks/core/useGridToolbarProps.ts +19 -0
  61. package/lib/hooks/features/useGridAiAssistant.ts +284 -0
  62. package/lib/hooks/features/useGridBatchEdit.ts +2 -2
  63. package/lib/index.ts +3 -0
  64. package/lib/styles/opengridx.css +2 -0
  65. package/lib/theme/DataGridThemeProvider.tsx +2 -0
  66. package/lib/theme/types.ts +2 -0
  67. package/lib/types/index.ts +219 -1
  68. package/lib/utils/aiAssistant.ts +205 -0
  69. package/lib/utils/aiBrand.ts +17 -0
  70. package/lib/utils/cellSelection.ts +25 -5
  71. package/lib/utils/editing/fill.ts +238 -0
  72. package/lib/utils/filtering/index.ts +14 -0
  73. package/lib/utils/focus/index.ts +9 -3
  74. package/lib/utils/headerFilters.ts +189 -0
  75. package/llms.txt +44 -4
  76. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@
5
5
 
6
6
  ---
7
7
 
8
+ ## [3.5.0] — 2026-10-09
9
+
10
+ ### Added
11
+
12
+ - **AI toolkit, part 2: the Ask AI panel** — new `aiAssistant` prop (`{ onPrompt, placeholder?, suggestions?, voice?, parts? }`). `GridToolbar` shows an **Ask AI** button (`localeText.aiAssistantButton`) that opens a prompt panel under the toolbar; `apiRef.openAiAssistant()` / `closeAiAssistant()` open and close it without a toolbar, and `slots.aiAssistantPanel` replaces it (`GridAiAssistantPanelProps`). The panel has a prompt input (Enter sends), suggestion chips, a microphone button only where the browser has `SpeechRecognition` (the microphone is requested on click, never on load; `voice: false` hides it), Stop (aborts `context.signal`), the reply text, the session's earlier prompts, and the list of ignored parts. A reply is applied at once as one undoable step: every change is a removable chip (removing one re-applies the reply without it) and **Undo** restores the models from before the reply. `role="dialog"` labelled "Ask AI", focus to the input on open and back on close, Escape closes, status in the grid's live region. New `onAiAssistantApply(result)` and `onAiAssistantError(error)`. Grids without `aiAssistant` are unchanged, and the core bundle still holds no AI code.
13
+ - **Only validated replies are applied.** `validateGridAiState` now brands its result and the result's `state` with the non-enumerable `Symbol.for('opengridx.ai.validated')` (shape, equality and JSON unchanged); the panel applies a state only when it carries it, and otherwise shows "The reply was not validated" with a development warning.
14
+ - **`createGridAiPromptHandler({ columns, callModel, parts?, schemaOptions? })`** in `@opencorestack/opengridx/ai` — builds `onPrompt` for the app's own model: the schema once, then `callModel({ prompt, schema, currentState, history }, { signal })`; the reply may be a state object, a JSON string or text with a fenced JSON block (the text or a `message` key becomes the reply message). A `callModel` failure becomes an error result; an abort rejects with an `AbortError`.
15
+ - **`createGridAgentTools(apiRef, columns, options?)`** in `/ai` — plain tool objects (`name`, `description`, `inputSchema`, `execute`) for the Vercel AI SDK, CopilotKit, OpenAI or Anthropic tool calling and WebMCP: `get_grid_state`, `set_filter`, `set_sort`, `set_grouping`, `set_aggregation`, `set_column_visibility`, `clear_filters`, and `get_row_summary` only with `allowRowAccess: true` (the one tool that sends row data). Inputs are validated and applied all or nothing; `execute` never throws.
16
+ - **`GridApi`**: `setSortModel`, `setRowGroupingModel`, `setAggregationModel`, `setColumnVisibilityModel`, `setPivotModel` (each fires its change callback) and `getGridAiState()`, plus `openAiAssistant()` / `closeAiAssistant()`.
17
+ - **`onRowGroupingModelChange`** — `rowGroupingModel` is now a controlled/uncontrolled pair: without the prop the grid keeps its own grouping model, which `apiRef.setRowGroupingModel` and the assistant change. A grid that passes `rowGroupingModel` stays controlled. (v2.0 removed this callback because nothing called it; the assistant and `apiRef` now do.)
18
+ - New types: `GridAiAssistantOptions`, `GridAiAssistantPanelProps`, `GridAiAssistantStatus`, `GridAiChip`, `GridAiPromptContext`, `GridAiPromptResult`, `GridAiHistoryEntry`, `GridAiModelRequest`, `GridAiPromptHandlerOptions`, `GridAgentTool`, `GridAgentToolResult`, `GridAgentToolsOptions`, `GridAgentApi` (exported from `/ai`); `GridToolbarProps.onAiAssistantToggle` / `aiAssistantOpen` / `aiAssistantLabel`; `GridLocaleText.aiAssistantButton`.
19
+ - Demo page *AI Assistant* (`/ai-assistant`): a simulated, rule-based model (no API key, no network), suggestion chips, the real `fetch` integration sample, and a scripted agent driving the grid through `createGridAgentTools`. Package smoke suite: `ai-assistant` scenario in both fixtures.
20
+ - **Header filter row** — `headerFilters` (default off) adds a filter row under the column headers, inside the sticky header: a text box for `string`, a number box for `number` and a date box for `date` (the shared `Input`), an Any / Yes / No select for `boolean` and a `valueOptions` select for `singleSelect` (none for `image`), each with an operator button that opens a menu of the type's operators and a clear button. Typing is debounced by 300 ms; typing still pending is committed if the column scrolls out of the render window. New `GridColDef.headerFilter` (`false`: empty cell) and `GridColDef.headerFilterOperator` (starting operator; defaults `contains`, `=`, `is`), and `headerFilterHeight` (default 40, CSS variable `--ogx-header-filter-height`).
21
+ - **One shared `filterModel`.** Each cell owns only the root-level item `id: 'header:<field>'`: it is added at the end, updated in place and removed when cleared, and every other item, group, `logicOperator` and `quickFilterValues` passes through. The filter panel edits the same item and keeps its id. When the root `logicOperator` is `'or'`, the field sits inside a group, or the field has an untagged or second root item, the cell shows a read-only "Custom filter" label instead (a button that opens the `GridToolbar` filter panel); the row never rewrites a model it cannot show. With `filterMode="server"` changes are reported through `onFilterModelChange` as usual. Not rendered while `pivotMode` is active.
22
+ - **Layout.** Filter cells take each column's width, flex and sticky offset from the same calculation as the column headers, so they line up with pinned columns, column virtualization and horizontal scrolling. Scroll-into-view (keyboard, `scrollToIndexes`) and range auto-scroll measure the sticky header, so rows never end up under the taller header.
23
+ - **Keyboard and ARIA.** The row is a header row (`role="row"`, counted in `aria-rowcount` and the rows' `aria-rowindex`) of `role="columnheader"` cells labelled "Filter &lt;header&gt;". ArrowDown on a column header moves to its filter cell and on into the first row; Enter, Space or typing focuses the control, Escape returns to the cell, Alt+ArrowDown or Ctrl/Cmd+Enter opens the operator menu, Delete clears the filter. Controls have `tabIndex={-1}`: the grid stays a single Tab stop and Tab from a control leaves the grid.
24
+ - Public classes `ogx__header-filter-row`, `ogx__header-filter-cell` (`--focused`, `--active`, `--custom`, `--empty`, `--system`, `--pinned`, `--pinned-left`, `--pinned-right`, `--pinned-left-last`, `--pinned-right-first`), `ogx__header-filter-operator` (`--open`), `ogx__header-filter-menu`, `ogx__header-filter-input-wrapper`, `ogx__header-filter-input`, `ogx__header-filter-select`, `ogx__header-filter-operator-label`, `ogx__header-filter-clear`, `ogx__header-filter-custom`. The row uses the header's theme variables, so every built-in theme and dark mode style it.
25
+ - `GridToolbarProps.forceFiltersOpen` / `onFiltersPanelClose` (the grid passes them so "Custom filter" can open the panel); `HeaderProps.headerFilters`, `filterModel`, `onFilterModelChange`, `onOpenFilterPanel`, and `focusedCell.headerFilter`.
26
+ - Guide `docs/features/header-filters.md`, demo page *Header Filters* (`/header-filters`), and a `header-filters` scenario in both package smoke fixtures.
27
+ - **Fill handle** — with `cellSelection` on, the range's bottom-right cell (or the only selected cell) carries a fill handle. Drag it down, up, right or left (one direction: the side the pointer's cell lies beyond, the larger pointer movement on a diagonal; auto-scrolls past the grid edge like a range drag; touch drags work, `touch-action: none`) to fill the new cells from the range: numbers and dates with an exactly equal step continue as a series (decimals rounded to the source's places; dates by month on the same day of the month, clamped to the month end, else by calendar day), anything else repeats the source pattern, one cell copies. Alt at release copies instead of continuing; Escape cancels. **Ctrl/Cmd+D** fills down from the range's top row and **Ctrl/Cmd+R** right from its left column (focus in the grid, no editor open; the browser's bookmark and reload keys are prevented only then). Fills write through `valueSetter` and `processRowUpdate` once per row as one batch edit and one undo step; non-editable, synthetic and span-covered cells are skipped (`'notEditable'`); the range becomes the source plus the filled cells. New props `disableFillHandle` and `onFill(result)` (`GridBatchEditResult` plus `direction`), new `GridColDef.fillValue(params)` to decide a column's filled values (a throw skips the cell, `'invalidValue'`), new types `GridFillDirection`, `GridFillResult`, `GridFillValueParams`. Public class `ogx__cell-fill-handle` (24 px hit area), variable `--ogx-fill-handle-color` (defaults to the range border; theme key `grid.fillHandleColor`, set by every built-in theme). Guide: `docs/features/fill-handle.md`. Demo page *Fill Handle* (`/fill-handle`).
28
+ - Package smoke suite: `fill` scenario (handle drag series, Ctrl/Cmd+D, undo) in both fixtures.
29
+
30
+ ### Changed
31
+
32
+ - `GridAiState`, `GridAiPart` and `GridAiValidationError` are declared in the grid's types so the `aiAssistant` prop can use them; `@opencorestack/opengridx/ai` re-exports them, so existing imports keep working.
33
+ - The `/ai` size budget in `check-bundle` is 8 kB gzipped (was 5 kB); the entry point is about 6.7 kB with the handler and tools.
34
+
35
+ ---
36
+
8
37
  ## [3.4.0] — 2026-10-08
9
38
 
10
39
  ### Added
@@ -36,6 +65,10 @@
36
65
  - `GridApi` has six new members (`pasteText`, `undo`, `redo`, `canUndo`, `canRedo`, `clearHistory`); an object written to satisfy `GridApi` by hand must add them. `useGridApiRef()`'s placeholder has no-op versions.
37
66
  - A committed single-cell edit is stored through the same path as batch edits; behaviour is unchanged.
38
67
 
68
+ ### Fixed
69
+
70
+ - **Cell range selection: a drag that auto-scrolled could end short of the pointer** (since 3.3.0). Dragging past the bottom of the grid scrolls it; on a slow machine the rows scrolled into view were not rendered yet when the pointer was hit-tested, so the range's head stayed on an earlier row (and the release could leave it there). When no cell is under the pointer during a drag, the head row is now worked out from the scroll position and the row heights.
71
+
39
72
  ## [3.3.0] — 2026-10-07
40
73
 
41
74
  ### Added
package/README.md CHANGED
@@ -76,6 +76,7 @@ Without a `height` prop the grid fills its container (`height: 100%`, or the fre
76
76
  - **Fully Customizable**: Slots system for replacing any component (pagination, overlays, toolbar).
77
77
  - **Export Functionality**: Built-in CSV, Excel (HTML `.xls`, or real `.xlsx` with the optional ExcelJS peer), JSON, PDF (optional jsPDF peer) and Print export.
78
78
  - **Clipboard**: `Ctrl+C` / `Cmd+C` copies the selected rows' visible columns as TSV for Excel/Sheets.
79
+ - **Header Filters** (v3.5): `headerFilters` adds a filter row under the column headers (text, number, date and select controls with an operator menu) that edits the same `filterModel` as the filter panel and the quick filter.
79
80
  - **Cell Range Selection** (v3.3): drag, Shift+click or Shift+arrows select a rectangle of cells; copy it as TSV and see its count, sum and average in a status bar (`cellSelection`, `showCellSelectionStats`).
80
81
  - **AI Toolkit** (v3.4): `@opencorestack/opengridx/ai` turns your columns into a JSON Schema for your own model and validates its reply before the grid applies it (`getGridAiSchema`, `validateGridAiState`). No AI SDK, no row data sent by default.
81
82
  - **Accessibility**: WCAG 2.1 AA — full ARIA roles and keyboard navigation.
@@ -157,6 +158,8 @@ This means that **Cursor**, **GitHub Copilot**, **Windsurf**, and other AI agent
157
158
  | `sortable` | `boolean` | `true` | Enable/disable column sorting. |
158
159
  | `sortComparator` | `(v1, v2, p1, p2) => number` | — | Custom ascending comparator (sees nulls; negated for desc). v3.1.0+ |
159
160
  | `filterable` | `boolean` | `true` | Enable/disable column filtering. |
161
+ | `headerFilter` | `boolean` | `true` | `false` leaves the column's cell in the header filter row empty (v3.5). |
162
+ | `headerFilterOperator` | `GridFilterOperator` | by type | Operator a new header filter starts with (v3.5). |
160
163
  | `resizable` | `boolean` | `true` | Allow drag-resize; double-click the handle to auto-size to content (v3.1.0+). |
161
164
  | `hideable` | `boolean` | `true` | Allow hiding via the column panel. |
162
165
  | `pinnable` | `boolean` | `true` | Allow pinning via the UI. |
@@ -188,6 +191,8 @@ This means that **Cursor**, **GitHub Copilot**, **Windsurf**, and other AI agent
188
191
  | `onSortModelChange` | `(model: GridSortItem[]) => void` | Fires on sort change. |
189
192
  | `filterModel` | `GridFilterModel` | Controlled filter model. |
190
193
  | `onFilterModelChange` | `(model: GridFilterModel) => void` | Fires on filter change. |
194
+ | `headerFilters` | `boolean` | Filter row under the column headers, sharing `filterModel` (v3.5). |
195
+ | `headerFilterHeight` | `number` | Height of the header filter row. Default `40`. |
191
196
  | `multiSort` | `boolean` | Every header click adds to the sort instead of replacing it (Shift+click always does). |
192
197
 
193
198
  ### Columns
@@ -523,6 +528,7 @@ Full documentation at 👉 **[opencorestack.github.io/OpenGridX](https://opencor
523
528
  - **[Clipboard](docs/features/clipboard.md)** — Copy rows as TSV, paste from Excel / Sheets, clear a range with Delete
524
529
  - **[Cell Range Selection](docs/features/cell-selection.md)** — Select, total and copy cell ranges
525
530
  - **[Undo & Redo](docs/features/undo-redo.md)** — Undo and redo edits, pastes and range clears
531
+ - **[Fill Handle](docs/features/fill-handle.md)** — Drag the range corner to fill a series or copy; Ctrl+D / Ctrl+R
526
532
  - **[AI Toolkit](docs/features/ai-toolkit.md)** — Schema and validator for driving the grid from your own model
527
533
  - **[Keyboard & Accessibility](docs/features/keyboard-navigation.md)** — Keys, focus and ARIA
528
534
  - **[Data Source](docs/features/data-source.md)** — Server-side integration
package/dist/ai.cjs CHANGED
@@ -1 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const z=/^(\d{4})-(\d{2})-(\d{2})$/;function T(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const n=e.trim();if(n==="")return null;const i=z.exec(n);if(i){const r=Number(i[1]),o=Number(i[2])-1,l=Number(i[3]);if(t=new Date(r,o,l),t.getMonth()!==o||t.getDate()!==l)return null}else t=new Date(n)}else return null;return Number.isNaN(t.getTime())?null:t}function Q(e){const t=T(e);if(!t)return"";const n=String(t.getFullYear()).padStart(4,"0"),i=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${i}-${r}`}function Z(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const x=["sum","avg","count","min","max","unique"],I=["sum","avg","count","min","max"],G=["isEmpty","isNotEmpty"],E=3,j=["filter","sort","grouping","aggregation","pivot","columnVisibility"],w={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function v(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function R(e){const t=Array.isArray(e?.include)?e.include:j,n=Array.isArray(e?.exclude)?e.exclude:[];return j.filter(i=>t.includes(i)&&!n.includes(i))}function q(e){if(!Array.isArray(e))return[];const t=new Set,n=[];for(const i of e){if(!$(i))continue;const r=i.field;typeof r!="string"||r===""||r.startsWith("__")||i.isSpacer===!0||t.has(r)||(t.add(r),n.push(i))}return n}function W(e){return Z(e.type)}const C=e=>e.filterable!==!1,U=e=>e.sortable!==!1,A=e=>e.groupable!==!1,B=e=>e.hideable!==!1;function b(e,t=x){if(e.aggregable===!1)return[];const n=e.type==="number"||e.aggregable===!0?t:t.filter(r=>r==="count"),i=e.availableAggregationFunctions;return Array.isArray(i)?n.filter(r=>i.includes(r)):[...n]}function ee(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const n of e.valueOptions){const i=n!==null&&typeof n=="object"?n.value:n;(typeof i=="string"||typeof i=="number"&&Number.isFinite(i))&&!t.includes(i)&&t.push(i)}return t}const te="OpenGridX grid state",re=10;function O(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,n=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${n}`}function m(e,t,n){return n.descriptions?{...e,description:t}:e}function ne(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(n=>typeof n=="string"||typeof n=="boolean"||typeof n=="number"&&Number.isFinite(n)).slice(0,re)}function ie(e,t){let n;switch(e.type){case"number":n={type:"number"};break;case"date":n={type:"string",format:"date"};break;case"boolean":n={type:"boolean"};break;case"singleSelect":{const r=ee(e),o=r.length>0?{enum:r}:{type:"string"};n={anyOf:[o,{type:"array",items:o}]};break}default:n={type:"string"}}const i=ne(e,t);return i.length>0?{...n,examples:i}:n}function oe(e,t){const n=W(e),i=n.filter(l=>G.includes(l)),r=e.type==="singleSelect"?"; isAnyOf takes an array":"",o=i.length>0?`${i.join(" and ")} take no value${r}`:r.slice(2);return m({type:"object",properties:{field:{const:e.field},operator:{enum:n},value:ie(e,t)},required:["field","operator"],additionalProperties:!1},o?`${O(e)}: ${o}`:O(e),t)}function se(e,t,n){const i=e.filter(C),r={},o={enum:["and","or"]};if(i.length>0){n.filterItem={oneOf:i.map(l=>oe(l,t))};for(let l=E;l>=1;l--){const s=l===E?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${l+1}`}]};n[`filterGroup${l}`]={type:"object",properties:{logicOperator:o,items:{type:"array",items:s}},required:["logicOperator","items"],additionalProperties:!1}}r.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},r.logicOperator=o}return r.quickFilterValues=m({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:r,additionalProperties:!1}}function _(e){return{enum:e.map(t=>t.field)}}function le(e,t,n,i){switch(e){case"filter":return se(t,n,i);case"sort":{const r=t.filter(U);return r.length===0?null:m({type:"array",items:{type:"object",properties:{field:_(r),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",n)}case"grouping":{const r=t.filter(A);return r.length===0?null:m({type:"array",items:_(r),uniqueItems:!0},"Outermost first",n)}case"aggregation":{const r={};for(const o of t){const l=b(o);l.length>0&&(r[o.field]=m({enum:l},O(o),n))}return Object.keys(r).length===0?null:{type:"object",properties:r,additionalProperties:!1}}case"pivot":{const r=t.filter(A),o=t.map(s=>({col:s,fns:b(s,I)})).filter(s=>s.fns.length>0).map(({col:s,fns:u})=>m({type:"object",properties:{field:{const:s.field},aggFn:{enum:u}},required:["field","aggFn"],additionalProperties:!1},O(s),n));if(r.length===0||o.length===0)return null;const l={type:"array",items:_(r),uniqueItems:!0};return{type:"object",properties:{rowFields:l,columnFields:l,valueFields:{type:"array",items:{oneOf:o}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const r={};for(const o of t.filter(B))r[o.field]=m({type:"boolean"},O(o),n);return Object.keys(r).length===0?null:m({type:"object",properties:r,additionalProperties:!1},"false hides a column",n)}}}function ue(e,t={}){const n={descriptions:t.descriptions!==!1,examples:t.examples!==!1},i=q(e),r={},o={};for(const s of R(t)){const u=le(s,i,n,r);u&&(o[w[s]]=u)}const l={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:te,type:"object",properties:o,additionalProperties:!1};return n.descriptions&&(l.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(r).length>0&&(l.$defs=r),l}const y={ok:!1},d=e=>({ok:!0,value:e}),fe=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,ae=/^\.\d+(?:e[+-]?\d+)?$/i,ce=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,de=/[\p{Sc}\s]/gu;function pe(e){let t=e.replace(de,"");if(t==="")return e.trim()===""?d(null):y;let n=!1;t.startsWith("(")&&t.endsWith(")")&&(n=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(n=!n),t=t.slice(1));let i=!1;if(t.endsWith("%")&&(i=!0,t=t.slice(0,-1)),ce.test(t))t=t.split(",").join("");else if(!fe.test(t)&&!ae.test(t))return y;let r=Number(t);return Number.isFinite(r)?(i&&(r/=100),n&&(r=-r),d(r)):y}function ge(e,t){const n=e.trim();if(n==="")return d(null);const i=T(n);return i?d(i):y}const me=new Set(["true","yes","1"]),ye=new Set(["false","no","0"]);function be(e,t){const n=e.trim().toLowerCase();return n===""?d(null):me.has(n)?d(!0):ye.has(n)?d(!1):y}function $e(e,t){const n=e.trim();if(n==="")return d(null);if(!t||t.length===0)return y;const i=n.toLowerCase();for(const r of t){const o=typeof r=="object"?r.value:r;if(String(o).toLowerCase()===i)return d(o)}for(const r of t)if(typeof r=="object"&&r.label.toLowerCase()===i)return d(r.value);return y}const a=e=>({ok:!0,value:e}),p={ok:!1,reason:"invalidValue"},N=e=>e.ok?a(e.value):p;function k(e){return e==null||typeof e=="string"&&e.trim()===""}function he(e){return e==null?a(""):typeof e=="string"?a(e):typeof e=="number"||typeof e=="boolean"?a(String(e)):p}function Se(e){return k(e)?a(null):typeof e=="number"?Number.isFinite(e)?a(e):p:typeof e!="string"?p:N(pe(e))}function Oe(e){return k(e)?a(null):e instanceof Date?Number.isNaN(e.getTime())?p:a(e):typeof e=="number"?Number.isFinite(e)?a(new Date(e)):p:typeof e!="string"?p:N(ge(e))}function X(e){return k(e)?a(null):typeof e=="boolean"?a(e):typeof e!="string"&&typeof e!="number"?p:N(be(String(e)))}function ve(e,t){if(k(e))return a(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return p;const n=t.valueOptions;return!n||n.length===0?a(e):N($e(String(e),n))}function Ae(e,t){switch(t.type){case"number":return Se(e);case"date":return Oe(e);case"boolean":return X(e);case"singleSelect":return ve(e,t);default:return he(e)}}const P=200,Fe=100;function f(e,t,n){return e.errors.length<Fe&&e.errors.push({path:t,message:n}),null}function c(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function H(e){return typeof e=="string"?e.trim().toLowerCase():null}function D(e){return Object.keys(e).filter(t=>t!=="__proto__")}function h(e,t,n,i,r){const o=typeof t=="string"?e.byField.get(t):void 0;return o?i(o)?o:f(e,n,`Field ${c(t)} cannot be ${r}`):f(e,n,`Unknown field ${c(t)}`)}function S(e,t,n){if(t==null)return[];const i=Array.isArray(t)?t:[t];return i.length>P&&f(e,n,`Only the first ${P} entries are read`),i.slice(0,P)}function F(e,t,n,i){const r=H(t);return i.find(o=>o.toLowerCase()===r)??f(e,n,`${c(t)} is not one of ${i.join(", ")}`)}function L(e,t,n,i){const r=Ae(t,n);if(!r.ok)return f(e,i,`${c(t)} is not a valid ${n.type??"string"} value`);const o=r.value instanceof Date?Q(r.value):r.value;return o==null||o===""?f(e,i,"Missing value"):{v:o}}function Ee(e,t,n){const i=h(e,t.field,`${n}.field`,C,"filtered");if(!i)return null;const r=F(e,t.operator,`${n}.operator`,W(i));if(r===null)return null;const o={field:i.field,operator:r};if(G.includes(o.operator))return o;const l=v(t,"value")?t.value:void 0;if(o.operator==="isAnyOf"){const u=[];return S(e,l,`${n}.value`).forEach((g,K)=>{const M=L(e,g,i,`${n}.value[${K}]`);M&&!u.includes(M.v)&&u.push(M.v)}),u.length===0?f(e,`${n}.value`,"No valid values"):(o.value=u,o)}if(Array.isArray(l))return f(e,`${n}.value`,"Expected one value, not a list");const s=L(e,l,i,`${n}.value`);return s?(o.value=s.v,o):null}function J(e,t,n){return t===void 0?null:F(e,t,n,["and","or"])}function Y(e,t,n,i){const r=[];return S(e,t,n).forEach((o,l)=>{const s=`${n}[${l}]`;if(!$(o))f(e,s,`Expected a filter, got ${c(o)}`);else if(v(o,"items")||v(o,"logicOperator")){if(i>=E){f(e,s,`Groups nest at most ${E} levels`);return}const u=Y(e,o.items,`${s}.items`,i+1);u.length===0?f(e,s,"Empty group removed"):r.push({logicOperator:J(e,o.logicOperator,`${s}.logicOperator`)??"and",items:u})}else{const u=Ee(e,o,s);u&&r.push(u)}}),r}function Ne(e,t,n){const i={};v(t,"items")&&(i.items=Y(e,t.items,`${n}.items`,0));const r=J(e,t.logicOperator,`${n}.logicOperator`);return r&&(i.logicOperator=r),v(t,"quickFilterValues")&&(i.quickFilterValues=[],S(e,t.quickFilterValues,`${n}.quickFilterValues`).forEach((o,l)=>{const s=typeof o=="string"||typeof o=="number"?String(o).trim():null;s===null?f(e,`${n}.quickFilterValues[${l}]`,`Expected text, got ${c(o)}`):s!==""&&i.quickFilterValues?.push(s)})),i}function ke(e,t,n){const i=[];return S(e,t,n).forEach((r,o)=>{const l=`${n}[${o}]`;if(!$(r))return f(e,l,`Expected { field, sort }, got ${c(r)}`);const s=h(e,r.field,`${l}.field`,U,"sorted"),u=s&&F(e,r.sort,`${l}.sort`,["asc","desc"]);!s||!u||(i.some(g=>g.field===s.field)?f(e,l,`${c(s.field)} is sorted twice`):i.push({field:s.field,sort:u}))}),i}function V(e,t,n,i,r){const o=[];return S(e,t,n).forEach((l,s)=>{const u=h(e,l,`${n}[${s}]`,i,r);u&&!o.includes(u.field)&&o.push(u.field)}),o}const Me=e=>b(e).length>0,_e=e=>b(e,I).length>0;function Pe(e,t,n){const i={};for(const r of D(t)){if(H(t[r])==="none")continue;const o=h(e,r,`${n}.${r}`,Me,"aggregated"),l=o&&F(e,t[r],`${n}.${r}`,b(o));o&&l&&(i[o.field]=l)}return i}function je(e,t,n){const i=[];return S(e,t.valueFields,`${n}.valueFields`).forEach((r,o)=>{const l=`${n}.valueFields[${o}]`;if(!$(r))return f(e,l,`Expected { field, aggFn }, got ${c(r)}`);const s=h(e,r.field,`${l}.field`,_e,"a pivot value"),u=s&&F(e,r.aggFn,`${l}.aggFn`,b(s,I));s&&u&&!i.some(g=>g.field===s.field&&g.aggFn===u)&&i.push({field:s.field,aggFn:u})}),{rowFields:V(e,t.rowFields,`${n}.rowFields`,A,"a pivot label"),columnFields:V(e,t.columnFields,`${n}.columnFields`,A,"a pivot label"),valueFields:i}}function Ve(e,t,n){const i={};for(const r of D(t)){const o=h(e,r,`${n}.${r}`,B,"hidden"),l=X(t[r]);o&&(l.ok&&l.value!==null?i[o.field]=l.value:f(e,`${n}.${r}`,`Expected true or false, got ${c(t[r])}`))}return i}function Ie(e,t,n,i,r){t==="sort"?r.sortModel=ke(e,n,i):t==="grouping"?r.rowGroupingModel=V(e,n,i,A,"grouped"):$(n)?t==="filter"?r.filterModel=Ne(e,n,i):t==="aggregation"?r.aggregationModel=Pe(e,n,i):t==="pivot"?r.pivotModel=je(e,n,i):r.columnVisibilityModel=Ve(e,n,i):f(e,i,`Expected an object, got ${c(n)}`)}function De(e,t,n={}){const i={errors:[],byField:new Map},r={};try{for(const s of q(t))i.byField.set(s.field,s);let o=e;if(typeof e=="string")try{o=JSON.parse(e)}catch{return f(i,"","Not valid JSON"),{state:r,errors:i.errors}}if(!$(o))return f(i,"",`Expected an object, got ${c(o)}`),{state:r,errors:i.errors};const l=R(n);for(const s of D(o)){const u=j.find(g=>w[g]===s);s==="schemaVersion"?o[s]!==1&&f(i,s,`Unsupported schemaVersion ${c(o[s])}`):u?l.includes(u)?Ie(i,u,o[s],s,r):f(i,s,"Part not allowed"):f(i,s,"Unknown part")}}catch{return f(i,"","Could not read the input"),{state:{},errors:i.errors}}return{state:r,errors:i.errors}}exports.getGridAiSchema=ue,exports.validateGridAiState=De;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||Q.has(e)||(Q.add(e),console.warn(`[OpenGridX] ${t}.`,r))}function pe(e,t,r){const o=e[t];if(!r?.valueGetter)return{value:o};try{return{value:r.valueGetter({row:e,field:t,value:o})}}catch(n){return{value:void 0,error:n??new Error("valueGetter failed")}}}function ge(e,t,r){const{value:o,error:n}=pe(e,t,r);return n!==void 0&&de(`valueGetter:${t}`,`valueGetter for column "${t}" threw; the value is read as undefined`,n),o}const me=/^(\d{4})-(\d{2})-(\d{2})$/;function ee(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const r=e.trim();if(r==="")return null;const o=me.exec(r);if(o){const n=Number(o[1]),i=Number(o[2])-1,s=Number(o[3]);if(t=new Date(n,i,s),t.getMonth()!==i||t.getDate()!==s)return null}else t=new Date(r)}else return null;return Number.isNaN(t.getTime())?null:t}function ye(e){const t=ee(e);if(!t)return"";const r=String(t.getFullYear()).padStart(4,"0"),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${r}-${o}-${n}`}function be(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const he=["sum","avg","count","min","max","unique"],B=["sum","avg","count","min","max"],te=["isEmpty","isNotEmpty"],D=3,U=["filter","sort","grouping","aggregation","pivot","columnVisibility"],H={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function re(e){const t=Array.isArray(e?.include)?e.include:U,r=Array.isArray(e?.exclude)?e.exclude:[];return U.filter(o=>t.includes(o)&&!r.includes(o))}function J(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const o of e){if(!$(o))continue;const n=o.field;typeof n!="string"||n===""||n.startsWith("__")||o.isSpacer===!0||t.has(n)||(t.add(n),r.push(o))}return r}function ne(e){return be(e.type)}const ie=e=>e.filterable!==!1,oe=e=>e.sortable!==!1,T=e=>e.groupable!==!1,se=e=>e.hideable!==!1;function F(e,t=he){if(e.aggregable===!1)return[];const r=e.type==="number"||e.aggregable===!0?t:t.filter(n=>n==="count"),o=e.availableAggregationFunctions;return Array.isArray(o)?r.filter(n=>o.includes(n)):[...r]}function $e(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const r of e.valueOptions){const o=r!==null&&typeof r=="object"?r.value:r;(typeof o=="string"||typeof o=="number"&&Number.isFinite(o))&&!t.includes(o)&&t.push(o)}return t}const Ae="OpenGridX grid state",Se=10;function j(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,r=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${r}`}function O(e,t,r){return r.descriptions?{...e,description:t}:e}function Oe(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(r=>typeof r=="string"||typeof r=="boolean"||typeof r=="number"&&Number.isFinite(r)).slice(0,Se)}function ve(e,t){let r;switch(e.type){case"number":r={type:"number"};break;case"date":r={type:"string",format:"date"};break;case"boolean":r={type:"boolean"};break;case"singleSelect":{const n=$e(e),i=n.length>0?{enum:n}:{type:"string"};r={anyOf:[i,{type:"array",items:i}]};break}default:r={type:"string"}}const o=Oe(e,t);return o.length>0?{...r,examples:o}:r}function Me(e,t){const r=ne(e),o=r.filter(s=>te.includes(s)),n=e.type==="singleSelect"?"; isAnyOf takes an array":"",i=o.length>0?`${o.join(" and ")} take no value${n}`:n.slice(2);return O({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:ve(e,t)},required:["field","operator"],additionalProperties:!1},i?`${j(e)}: ${i}`:j(e),t)}function Ee(e,t,r){const o=e.filter(ie),n={},i={enum:["and","or"]};if(o.length>0){r.filterItem={oneOf:o.map(s=>Me(s,t))};for(let s=D;s>=1;s--){const l=s===D?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${s+1}`}]};r[`filterGroup${s}`]={type:"object",properties:{logicOperator:i,items:{type:"array",items:l}},required:["logicOperator","items"],additionalProperties:!1}}n.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},n.logicOperator=i}return n.quickFilterValues=O({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:n,additionalProperties:!1}}function q(e){return{enum:e.map(t=>t.field)}}function we(e,t,r,o){switch(e){case"filter":return Ee(t,r,o);case"sort":{const n=t.filter(oe);return n.length===0?null:O({type:"array",items:{type:"object",properties:{field:q(n),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const n=t.filter(T);return n.length===0?null:O({type:"array",items:q(n),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const n={};for(const i of t){const s=F(i);s.length>0&&(n[i.field]=O({enum:s},j(i),r))}return Object.keys(n).length===0?null:{type:"object",properties:n,additionalProperties:!1}}case"pivot":{const n=t.filter(T),i=t.map(l=>({col:l,fns:F(l,B)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>O({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},j(l),r));if(n.length===0||i.length===0)return null;const s={type:"array",items:q(n),uniqueItems:!0};return{type:"object",properties:{rowFields:s,columnFields:s,valueFields:{type:"array",items:{oneOf:i}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const n={};for(const i of t.filter(se))n[i.field]=O({type:"boolean"},j(i),r);return Object.keys(n).length===0?null:O({type:"object",properties:n,additionalProperties:!1},"false hides a column",r)}}}function Y(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},o=J(e),n={},i={};for(const l of re(t)){const a=we(l,o,r,n);a&&(i[H[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:Ae,type:"object",properties:i,additionalProperties:!1};return r.descriptions&&(s.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(n).length>0&&(s.$defs=n),s}const Fe=Symbol.for("opengridx.ai.validated");function I(e){return Object.defineProperty(e,Fe,{value:!0,enumerable:!1}),e}const M={ok:!1},h=e=>({ok:!0,value:e}),Ne=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,_e=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,je=/[\p{Sc}\s]/gu;function Te(e){let t=e.replace(je,"");if(t==="")return e.trim()===""?h(null):M;let r=!1;t.startsWith("(")&&t.endsWith(")")&&(r=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(r=!r),t=t.slice(1));let o=!1;if(t.endsWith("%")&&(o=!0,t=t.slice(0,-1)),ke.test(t))t=t.split(",").join("");else if(!Ne.test(t)&&!_e.test(t))return M;let n=Number(t);return Number.isFinite(n)?(o&&(n/=100),r&&(n=-n),h(n)):M}function Ge(e,t){const r=e.trim();if(r==="")return h(null);const o=ee(r);return o?h(o):M}const Ve=new Set(["true","yes","1"]),Pe=new Set(["false","no","0"]);function Re(e,t){const r=e.trim().toLowerCase();return r===""?h(null):Ve.has(r)?h(!0):Pe.has(r)?h(!1):M}function De(e,t){const r=e.trim();if(r==="")return h(null);if(!t||t.length===0)return M;const o=r.toLowerCase();for(const n of t){const i=typeof n=="object"?n.value:n;if(String(i).toLowerCase()===o)return h(i)}for(const n of t)if(typeof n=="object"&&n.label.toLowerCase()===o)return h(n.value);return M}const d=e=>({ok:!0,value:e}),A={ok:!1,reason:"invalidValue"},L=e=>e.ok?d(e.value):A;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function Ie(e){return e==null?d(""):typeof e=="string"?d(e):typeof e=="number"||typeof e=="boolean"?d(String(e)):A}function Le(e){return C(e)?d(null):typeof e=="number"?Number.isFinite(e)?d(e):A:typeof e!="string"?A:L(Te(e))}function Ce(e){return C(e)?d(null):e instanceof Date?Number.isNaN(e.getTime())?A:d(e):typeof e=="number"?Number.isFinite(e)?d(new Date(e)):A:typeof e!="string"?A:L(Ge(e))}function le(e){return C(e)?d(null):typeof e=="boolean"?d(e):typeof e!="string"&&typeof e!="number"?A:L(Re(String(e)))}function qe(e,t){if(C(e))return d(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return A;const r=t.valueOptions;return!r||r.length===0?d(e):L(De(String(e),r))}function We(e,t){switch(t.type){case"number":return Le(e);case"date":return Ce(e);case"boolean":return le(e);case"singleSelect":return qe(e,t);default:return Ie(e)}}const W=200,Ue=100;function u(e,t,r){return e.errors.length<Ue&&e.errors.push({path:t,message:r}),null}function p(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function ae(e){return typeof e=="string"?e.trim().toLowerCase():null}function K(e){return Object.keys(e).filter(t=>t!=="__proto__")}function N(e,t,r,o,n){const i=typeof t=="string"?e.byField.get(t):void 0;return i?o(i)?i:u(e,r,`Field ${p(t)} cannot be ${n}`):u(e,r,`Unknown field ${p(t)}`)}function _(e,t,r){if(t==null)return[];const o=Array.isArray(t)?t:[t];return o.length>W&&u(e,r,`Only the first ${W} entries are read`),o.slice(0,W)}function G(e,t,r,o){const n=ae(t);return o.find(i=>i.toLowerCase()===n)??u(e,r,`${p(t)} is not one of ${o.join(", ")}`)}function Z(e,t,r,o){const n=We(t,r);if(!n.ok)return u(e,o,`${p(t)} is not a valid ${r.type??"string"} value`);const i=n.value instanceof Date?ye(n.value):n.value;return i==null||i===""?u(e,o,"Missing value"):{v:i}}function Xe(e,t,r){const o=N(e,t.field,`${r}.field`,ie,"filtered");if(!o)return null;const n=G(e,t.operator,`${r}.operator`,ne(o));if(n===null)return null;const i={field:o.field,operator:n};if(te.includes(i.operator))return i;const s=S(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return _(e,s,`${r}.value`).forEach((c,v)=>{const g=Z(e,c,o,`${r}.value[${v}]`);g&&!a.includes(g.v)&&a.push(g.v)}),a.length===0?u(e,`${r}.value`,"No valid values"):(i.value=a,i)}if(Array.isArray(s))return u(e,`${r}.value`,"Expected one value, not a list");const l=Z(e,s,o,`${r}.value`);return l?(i.value=l.v,i):null}function ue(e,t,r){return t===void 0?null:G(e,t,r,["and","or"])}function fe(e,t,r,o){const n=[];return _(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!$(i))u(e,l,`Expected a filter, got ${p(i)}`);else if(S(i,"items")||S(i,"logicOperator")){if(o>=D){u(e,l,`Groups nest at most ${D} levels`);return}const a=fe(e,i.items,`${l}.items`,o+1);a.length===0?u(e,l,"Empty group removed"):n.push({logicOperator:ue(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Xe(e,i,l);a&&n.push(a)}}),n}function Be(e,t,r){const o={};S(t,"items")&&(o.items=fe(e,t.items,`${r}.items`,0));const n=ue(e,t.logicOperator,`${r}.logicOperator`);return n&&(o.logicOperator=n),S(t,"quickFilterValues")&&(o.quickFilterValues=[],_(e,t.quickFilterValues,`${r}.quickFilterValues`).forEach((i,s)=>{const l=typeof i=="string"||typeof i=="number"?String(i).trim():null;l===null?u(e,`${r}.quickFilterValues[${s}]`,`Expected text, got ${p(i)}`):l!==""&&o.quickFilterValues?.push(l)})),o}function He(e,t,r){const o=[];return _(e,t,r).forEach((n,i)=>{const s=`${r}[${i}]`;if(!$(n))return u(e,s,`Expected { field, sort }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,oe,"sorted"),a=l&&G(e,n.sort,`${s}.sort`,["asc","desc"]);!l||!a||(o.some(c=>c.field===l.field)?u(e,s,`${p(l.field)} is sorted twice`):o.push({field:l.field,sort:a}))}),o}function X(e,t,r,o,n){const i=[];return _(e,t,r).forEach((s,l)=>{const a=N(e,s,`${r}[${l}]`,o,n);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Je=e=>F(e).length>0,Ye=e=>F(e,B).length>0;function Ke(e,t,r){const o={};for(const n of K(t)){if(ae(t[n])==="none")continue;const i=N(e,n,`${r}.${n}`,Je,"aggregated"),s=i&&G(e,t[n],`${r}.${n}`,F(i));i&&s&&(o[i.field]=s)}return o}function ze(e,t,r){const o=[];return _(e,t.valueFields,`${r}.valueFields`).forEach((n,i)=>{const s=`${r}.valueFields[${i}]`;if(!$(n))return u(e,s,`Expected { field, aggFn }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,Ye,"a pivot value"),a=l&&G(e,n.aggFn,`${s}.aggFn`,F(l,B));l&&a&&!o.some(c=>c.field===l.field&&c.aggFn===a)&&o.push({field:l.field,aggFn:a})}),{rowFields:X(e,t.rowFields,`${r}.rowFields`,T,"a pivot label"),columnFields:X(e,t.columnFields,`${r}.columnFields`,T,"a pivot label"),valueFields:o}}function Qe(e,t,r){const o={};for(const n of K(t)){const i=N(e,n,`${r}.${n}`,se,"hidden"),s=le(t[n]);i&&(s.ok&&s.value!==null?o[i.field]=s.value:u(e,`${r}.${n}`,`Expected true or false, got ${p(t[n])}`))}return o}function Ze(e,t,r,o,n){t==="sort"?n.sortModel=He(e,r,o):t==="grouping"?n.rowGroupingModel=X(e,r,o,T,"grouped"):$(r)?t==="filter"?n.filterModel=Be(e,r,o):t==="aggregation"?n.aggregationModel=Ke(e,r,o):t==="pivot"?n.pivotModel=ze(e,r,o):n.columnVisibilityModel=Qe(e,r,o):u(e,o,`Expected an object, got ${p(r)}`)}function z(e,t,r={}){const o=xe(e,t,r);return I(o.state),I(o)}function xe(e,t,r){const o={errors:[],byField:new Map},n={};try{for(const l of J(t))o.byField.set(l.field,l);let i=e;if(typeof e=="string")try{i=JSON.parse(e)}catch{return u(o,"","Not valid JSON"),{state:n,errors:o.errors}}if(!$(i))return u(o,"",`Expected an object, got ${p(i)}`),{state:n,errors:o.errors};const s=re(r);for(const l of K(i)){const a=U.find(c=>H[c]===l);l==="schemaVersion"?i[l]!==1&&u(o,l,`Unsupported schemaVersion ${p(i[l])}`):a?s.includes(a)?Ze(o,a,i[l],l,n):u(o,l,"Part not allowed"):u(o,l,"Unknown part")}}catch{return u(o,"","Could not read the input"),{state:{},errors:o.errors}}return{state:n,errors:o.errors}}const k="```";function V(){return typeof DOMException=="function"?new DOMException("The operation was aborted.","AbortError"):Object.assign(new Error("The operation was aborted."),{name:"AbortError"})}function et(e){const t=e.trim();if(t.startsWith("{"))return{json:t,message:""};const r=e.indexOf(k),o=r<0?-1:e.indexOf(`
2
+ `,r+k.length),n=o<0?-1:e.indexOf(k,o+1),i=n<0?"":e.slice(r+k.length,o).trim().toLowerCase();return n<0||i!==""&&i!=="json"?{json:null,message:t}:{json:e.slice(o+1,n),message:`${e.slice(0,r)}${e.slice(n+k.length)}`.trim()}}function tt(e){return e instanceof Error&&e.message?e.message:typeof e=="string"&&e?e:"Unknown error"}function ce(e,t){const r={state:I({}),errors:e};return t&&(r.message=t),I(r)}function rt(e,t,r){let o=e,n="";if(typeof e=="string"){const s=et(e);if(s.json===null)return ce([],s.message||void 0);o=s.json,n=s.message}else if($(e)&&S(e,"message")&&typeof e.message=="string"){const{message:s,...l}=e;o=l,n=s}const i=z(o,t,r);return n&&(i.message=n),i}function nt(e){const{columns:t,callModel:r,parts:o,schemaOptions:n={}}=e,i={include:o??n.include,exclude:n.exclude},s=Y(t,{...n,...i});return async(l,a)=>{const c=a?.signal??new AbortController().signal;if(c.aborted)throw V();const v=new Promise((m,y)=>{c.addEventListener("abort",()=>y(V()),{once:!0})});v.catch(()=>{});let g;try{const m={prompt:String(l),schema:s,currentState:a?.currentState??{},history:Array.isArray(a?.history)?a.history:[]};g=await Promise.race([r(m,{signal:c}),v])}catch(m){if(c.aborted)throw V();return ce([{path:"",message:`The model call failed: ${tt(m)}`}])}if(c.aborted)throw V();return rt(g,t,i)}}const it=20,ot=1e3,st=[{name:"set_filter",part:"filter",description:"Replace the filter model of the grid (filters and quick-filter words)."},{name:"set_sort",part:"sort",description:"Replace the sort model of the grid, most significant field first. An empty list clears sorting."},{name:"set_grouping",part:"grouping",description:"Group the rows by these fields, outermost first. An empty list removes grouping."},{name:"set_aggregation",part:"aggregation",description:"Replace the summaries: field to aggregation function. An empty object removes them."},{name:"set_column_visibility",part:"columnVisibility",description:"Show (true) or hide (false) columns. Columns not listed keep their visibility."}];function w(e,t=""){return{ok:!1,errors:[{path:t,message:e}]}}async function P(e){try{return e()}catch(t){return w(t instanceof Error&&t.message?t.message:"The tool failed")}}function R(e){const t=e?.current;return t&&typeof t.getGridAiState=="function"?t:null}function lt(e){return e instanceof Date?Number.isNaN(e.getTime())?null:e.toISOString():typeof e=="number"?Number.isFinite(e)?e:null:typeof e=="string"||typeof e=="boolean"?e:null}function at(e,t,r,o,n){const i=H[o],s=Y(t,{include:[o]});if(!S(s.properties,i))return null;const{$schema:l,schemaVersion:a,title:c,description:v,...g}=s;return{name:r,description:n,inputSchema:{...g,required:[i]},execute:m=>P(()=>{const y=R(e);if(!y)return w("The grid is not ready");if(!$(m)||!S(m,i))return w(`Expected an object with "${i}"`,i);const{state:f,errors:E}=z({[i]:m[i]},t,{include:[o]});if(E.length>0)return{ok:!1,errors:E};const b={};return f.filterModel&&y.setFilterModel(b.filterModel=f.filterModel),f.sortModel&&y.setSortModel(b.sortModel=f.sortModel),f.rowGroupingModel&&y.setRowGroupingModel(b.rowGroupingModel=f.rowGroupingModel),f.aggregationModel&&y.setAggregationModel(b.aggregationModel=f.aggregationModel),f.columnVisibilityModel&&(b.columnVisibilityModel={...y.getGridAiState().columnVisibilityModel,...f.columnVisibilityModel},y.setColumnVisibilityModel(b.columnVisibilityModel)),{ok:!0,applied:b}})}}const x={type:"object",properties:{},additionalProperties:!1};function ut(e,t,r={}){const o=J(t),n=[{name:"get_grid_state",description:"Read the current filter, sort, grouping, aggregation, pivot and column visibility of the grid, and its columns.",inputSchema:x,execute:()=>P(()=>{const i=R(e);return i?{ok:!0,state:i.getGridAiState(),columns:o.map(s=>({field:s.field,headerName:typeof s.headerName=="string"?s.headerName:s.field,type:typeof s.type=="string"?s.type:"string"}))}:w("The grid is not ready")})}];for(const{name:i,part:s,description:l}of st){const a=at(e,o,i,s,l);a&&n.push(a)}if(n.push({name:"clear_filters",description:"Remove every filter and the quick-filter words.",inputSchema:x,execute:()=>P(()=>{const i=R(e);if(!i)return w("The grid is not ready");const s={items:[]};return i.setFilterModel(s),{ok:!0,applied:{filterModel:s}}})}),r.allowRowAccess===!0){const i=typeof r.maxRows=="number"&&r.maxRows>=1?Math.min(Math.floor(r.maxRows),ot):it,s=Array.isArray(r.fields)?o.filter(l=>r.fields?.includes(l.field)):o;n.push({name:"get_row_summary",description:`Count the rows that pass the current filter and read the first ones (at most ${i}, fields: ${s.map(l=>l.field).join(", ")}).`,inputSchema:{type:"object",properties:{limit:{type:"integer",minimum:1,maximum:i}},additionalProperties:!1},execute:l=>P(()=>{const a=R(e);if(!a)return w("The grid is not ready");const c=$(l)&&S(l,"limit")?l.limit:i,v=typeof c=="number"&&c>=1?Math.min(Math.floor(c),i):i,g=a.getAllFilteredRows(),m=Array.isArray(g)?g:[],y=m.slice(0,v).map(f=>{const E={};if(!$(f))return E;for(const b of s)E[b.field]=lt(ge(f,b.field,b));return E});return{ok:!0,rowCount:m.length,rows:y}})})}return n}exports.createGridAgentTools=ut,exports.createGridAiPromptHandler=nt,exports.getGridAiSchema=Y,exports.validateGridAiState=z;