@alaarab/ogrid-mcp 2.14.0 → 2.14.2

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.
@@ -18,7 +18,7 @@ A collapsible sidebar panel that provides quick access to column visibility cont
18
18
  ```
19
19
 
20
20
  :::tip
21
- The `SideBar` component is framework-agnostic and uses inline styles. All React UI packages (Radix, Fluent, Material) use the same `SideBar` from `@alaarab/ogrid-react`.
21
+ The `SideBar` component is framework-agnostic and uses inline styles. All React UI packages (Radix, Fluent) use the same `SideBar` from `@alaarab/ogrid-react`.
22
22
  :::
23
23
 
24
24
  </TabItem>
@@ -10,7 +10,7 @@ OGrid 2.9 ships seven framework-native hooks that expose grid logic without
10
10
  imposing any chrome. Pair `useHeadlessGrid` with the spreadsheet hooks to
11
11
  add inline edit, range selection, fill handle, clipboard, undo/redo, and
12
12
  keyboard navigation to your own table markup (shadcn `<Table>`, plain HTML,
13
- Material `<DataGrid>`, anything).
13
+ Fluent `<DataGrid>`, anything).
14
14
 
15
15
  All seven hooks ship as React hooks:
16
16
 
@@ -18,7 +18,7 @@ OGrid's premium inputs are optional cell editor components distributed in a sepa
18
18
  npm install @alaarab/ogrid-react-inputs
19
19
  ```
20
20
 
21
- Works with any OGrid React UI package (Radix, Fluent, Material).
21
+ Works with any OGrid React UI package (Radix, Fluent).
22
22
 
23
23
  </TabItem>
24
24
  </Tabs>
@@ -213,7 +213,7 @@ OGrid includes built-in cell editors out of the box. The premium input packages
213
213
 
214
214
  | | Default (built-in) | Premium (add-on packages) |
215
215
  |---|---|---|
216
- | **Package** | Included in all OGrid packages | `@alaarab/ogrid-{react,angular,vue,js}-inputs` |
216
+ | **Package** | Included in all OGrid packages | `@alaarab/ogrid-react-inputs` |
217
217
  | **Editors** | Text, Select, Rich Select, Checkbox, Date (inline) | DatePicker, Rating, ColorPicker, Slider, Tags |
218
218
  | **Bundle cost** | Zero (built-in) | Only added when you `npm install` the package |
219
219
 
@@ -15,7 +15,7 @@ fits the page you're building — you can mix them in the same app.
15
15
  | **What you write** | One JSX element | A handful of hook calls + your own `<table>` markup |
16
16
  | **Time to first working grid** | ~5 lines | ~50-100 lines |
17
17
  | **Customization ceiling** | High (theme tokens, slots) | Unlimited (you draw the chrome) |
18
- | **Design-system fit** | Inherits via theme presets (shadcn, Fluent, Material) | Native — uses your chrome primitives directly |
18
+ | **Design-system fit** | Inherits via theme presets (shadcn, Fluent) | Native — uses your chrome primitives directly |
19
19
  | **Spreadsheet features** | Built-in | Composable from hooks |
20
20
  | **Best for** | Standard list pages, admin tables, dashboards | Pages where chrome integration matters more than save-time |
21
21
 
@@ -23,7 +23,7 @@ fits the page you're building — you can mix them in the same app.
23
23
 
24
24
  - You want a complete grid in one element. Sort, filter, paginate, edit,
25
25
  range select, fill handle, copy/paste, undo, keyboard nav — all wired.
26
- - The default chrome (via your chosen UI variant — Radix / Fluent / Material)
26
+ - The default chrome (via your chosen UI variant — Radix / Fluent)
27
27
  is close enough to your design system that a theme preset (`preset-shadcn.css`)
28
28
  can bridge the rest.
29
29
  - You're prototyping or building admin pages where shipping fast matters
@@ -42,8 +42,7 @@ const columns = [
42
42
  ## Use `useHeadlessGrid` + the spreadsheet hooks when…
43
43
 
44
44
  - You want OGrid's logic but rendered with your own table chrome
45
- (shadcn `<Table>`, Fluent `<DataGrid>`, Material `<TableRow>`, plain HTML —
46
- anything).
45
+ (shadcn `<Table>`, Fluent `<DataGrid>`, plain HTML — anything).
47
46
  - The chrome integration is non-negotiable: a specific design system,
48
47
  custom row layouts, embedded widgets in cells, or shared `<TableRow>`
49
48
  components from the rest of the app.
@@ -90,7 +90,7 @@ search_docs query="server-side data" framework="react"
90
90
 
91
91
  Parameters:
92
92
  - `query` (required) — what you're looking for
93
- - `framework` (optional) — `react`, `angular`, `vue`, or `js`
93
+ - `framework` (optional) — `react`
94
94
  - `category` (optional) — `features`, `getting-started`, `guides`, or `api`
95
95
 
96
96
  ### `list_docs`
@@ -114,7 +114,7 @@ get_docs path="features/filtering"
114
114
  Pull code snippets from a doc page, optionally filtered by framework.
115
115
 
116
116
  ```
117
- get_code_example path="features/editing" framework="angular"
117
+ get_code_example path="features/editing" framework="react"
118
118
  ```
119
119
 
120
120
  ### `detect_version`
package/dist/esm/index.js CHANGED
@@ -93,6 +93,21 @@ function parseDocFile(filePath, docsDir2) {
93
93
  codeBlocks
94
94
  };
95
95
  }
96
+ function countOccurrences(haystack, needle, cap) {
97
+ if (!needle) return 0;
98
+ let idx = 0;
99
+ let count = 0;
100
+ while (count < cap) {
101
+ idx = haystack.indexOf(needle, idx);
102
+ if (idx === -1) break;
103
+ count++;
104
+ idx += needle.length;
105
+ }
106
+ return count;
107
+ }
108
+ function tokenize(queryLower) {
109
+ return queryLower.split(/\s+/).filter((t) => t.length > 1);
110
+ }
96
111
  function scoreEntry(entry, queryLower) {
97
112
  let score = 0;
98
113
  const titleLower = entry.title.toLowerCase();
@@ -108,15 +123,18 @@ function scoreEntry(entry, queryLower) {
108
123
  }
109
124
  if (contentLower.includes(queryLower)) {
110
125
  score += 10;
111
- let idx = 0;
112
- let count = 0;
113
- while (count < 10) {
114
- idx = contentLower.indexOf(queryLower, idx);
115
- if (idx === -1) break;
116
- count++;
117
- idx += queryLower.length;
126
+ score += countOccurrences(contentLower, queryLower, 10) * 2;
127
+ }
128
+ const tokens = tokenize(queryLower);
129
+ if (tokens.length > 1) {
130
+ for (const token of tokens) {
131
+ if (titleLower.includes(token)) score += 25;
132
+ if (descLower.includes(token)) score += 12;
133
+ if (contentLower.includes(token)) {
134
+ score += 3;
135
+ score += countOccurrences(contentLower, token, 5);
136
+ }
118
137
  }
119
- score += count * 2;
120
138
  }
121
139
  if (entry.category === "features") score += 3;
122
140
  if (entry.category === "getting-started") score += 2;
@@ -125,9 +143,20 @@ function scoreEntry(entry, queryLower) {
125
143
  function scoreCodeBlock(entry, block, queryLower, framework) {
126
144
  let score = 0;
127
145
  if (framework && block.framework !== framework) return -1;
128
- if (entry.title.toLowerCase().includes(queryLower)) score += 50;
129
- if (entry.description.toLowerCase().includes(queryLower)) score += 25;
130
- if (block.code.toLowerCase().includes(queryLower)) score += 30;
146
+ const titleLower = entry.title.toLowerCase();
147
+ const descLower = entry.description.toLowerCase();
148
+ const codeLower = block.code.toLowerCase();
149
+ if (titleLower.includes(queryLower)) score += 50;
150
+ if (descLower.includes(queryLower)) score += 25;
151
+ if (codeLower.includes(queryLower)) score += 30;
152
+ const tokens = tokenize(queryLower);
153
+ if (tokens.length > 1) {
154
+ for (const token of tokens) {
155
+ if (titleLower.includes(token)) score += 12;
156
+ if (descLower.includes(token)) score += 6;
157
+ if (codeLower.includes(token)) score += 8;
158
+ }
159
+ }
131
160
  return score;
132
161
  }
133
162
  function loadDocsIndex(docsDir2) {
@@ -215,15 +244,19 @@ function fromResourcePath(resourcePath, index2) {
215
244
  return index2.getByPath(resourcePath + ".mdx") ?? index2.getByPath(resourcePath + ".md") ?? index2.getByPath(resourcePath);
216
245
  }
217
246
  function createOGridMcpServer(index2, bridge) {
218
- const server2 = new McpServer({
219
- name: "ogrid-docs",
220
- version: "2.3.0",
221
- instructions: `OGrid documentation server. OGrid is a lightweight, headless data grid for React (Radix and Fluent UI implementations).
247
+ const server2 = new McpServer(
248
+ {
249
+ name: "ogrid-docs",
250
+ version: "2.3.0"
251
+ },
252
+ {
253
+ instructions: `OGrid documentation server. OGrid is a lightweight, headless data grid for React (Radix and Fluent UI implementations).
222
254
 
223
255
  Tools: search_docs (keyword search), list_docs (browse by category), get_docs (full page), get_code_example (code snippets), detect_version (detect OGrid version in your project).
224
256
  Resources: ogrid://quick-reference (key API overview), ogrid://docs/{path} (any doc page by path).
225
257
  Categories: features, getting-started, guides, api.`
226
- });
258
+ }
259
+ );
227
260
  server2.tool(
228
261
  "search_docs",
229
262
  "Search OGrid documentation by keyword. Returns matching docs with title, description, and content excerpt.",
@@ -440,12 +473,14 @@ Tip: use \`get_code_example\` with framework="${result.framework}" or \`search_d
440
473
  {
441
474
  uri: uri.href,
442
475
  mimeType: "text/markdown",
476
+ // Source of truth: docs/api/ogrid-props.mdx + docs/api/column-def.mdx.
477
+ // Keep this in sync when those API references change.
443
478
  text: [
444
479
  "# OGrid Quick Reference",
445
480
  "",
446
481
  "## Install",
447
482
  "```bash",
448
- "# React (choose one)",
483
+ "# React (choose one UI variant)",
449
484
  "npm install @alaarab/ogrid-react-radix",
450
485
  "npm install @alaarab/ogrid-react-fluent",
451
486
  "```",
@@ -453,34 +488,38 @@ Tip: use \`get_code_example\` with framework="${result.framework}" or \`search_d
453
488
  "## Core Props (IOGridProps)",
454
489
  "| Prop | Type | Description |",
455
490
  "|------|------|-------------|",
456
- "| `data` | `T[]` | Client-side row data |",
457
- "| `columns` | `IColumnDef<T>[]` | Column definitions |",
458
- "| `dataSource` | `IDataSource<T>` | Server-side data source |",
459
- "| `pagination` | `boolean \\| number` | Enable pagination (number = page size) |",
460
- '| `rowSelection` | `"single" \\| "multiple"` | Row selection mode |',
491
+ "| `data` | `T[]` | Client-side row data (mutually exclusive with `dataSource`) |",
492
+ "| `dataSource` | `IDataSource<T>` | Server-side data source (mutually exclusive with `data`) |",
493
+ "| `columns` | `(IColumnDef<T> \\| IColumnGroupDef<T>)[]` | Column definitions (and optional groups) |",
494
+ "| `getRowId` | `(item: T) => RowId` | **Required.** Stable unique id for each row |",
495
+ "| `defaultPageSize` | `number` | Initial page size in uncontrolled mode (default 25) |",
496
+ "| `pageSizeOptions` | `number[]` | Options shown in the page-size dropdown |",
497
+ '| `rowSelection` | `"none" \\| "single" \\| "multiple"` | Row selection mode |',
498
+ "| `editable` | `boolean` | Enable inline cell editing globally |",
461
499
  "| `formulas` | `boolean` | Enable formula engine (=SUM, =IF, etc.) |",
462
500
  "| `cellReferences` | `boolean` | Excel-style A1 column headers + name box |",
463
501
  '| `workerSort` | `boolean \\| "auto"` | Web Worker sort/filter |',
464
502
  '| `columnChooser` | `boolean \\| "toolbar" \\| "sidebar"` | Column visibility control |',
465
503
  "| `sideBar` | `boolean \\| ISideBarDef` | Sidebar panel |",
466
504
  "| `toolbar` | `ReactNode` | Custom toolbar content |",
467
- "| `onRowSelectionChanged` | `(rows: T[]) => void` | Row selection callback |",
468
- "| `onCellValueChanged` | `(e: ICellValueChangedEvent) => void` | Cell edit callback |",
505
+ "| `onSelectionChange` | `(e: IRowSelectionChangeEvent<T>) => void` | Row selection callback |",
506
+ "| `onCellValueChanged` | `(e: ICellValueChangedEvent<T>) => void` | Cell edit callback |",
469
507
  "",
470
508
  "## IColumnDef Key Fields",
471
509
  "| Field | Type | Description |",
472
510
  "|-------|------|-------------|",
473
- "| `columnId` | `string` | Unique column ID (maps to data key) |",
474
- "| `headerName` | `string` | Column header label |",
511
+ "| `columnId` | `string` | Unique column id (maps to the data key) |",
512
+ "| `name` | `string` | Column header label |",
475
513
  '| `type` | `"text" \\| "numeric" \\| "date" \\| "boolean"` | Data type |',
476
- '| `filter` | `"none" \\| "text" \\| "multiSelect" \\| "date"` | Filter type |',
477
- "| `editable` | `boolean \\| (row) => boolean` | Enable inline editing |",
478
- "| `width` | `number` | Column width in px |",
479
- '| `pinned` | `"left" \\| "right"` | Pin column |',
480
- "| `sortable` | `boolean` | Enable sorting |",
481
- "| `hidden` | `boolean` | Hide column by default |",
482
- "| `valueGetter` | `(row: T) => unknown` | Custom value extractor |",
483
- "| `renderCell` | `(value, row) => ReactNode` | Custom cell renderer (React) |",
514
+ "| `filterable` | `IColumnFilterDef` | Filter config; omit to make the column not filterable |",
515
+ "| `editable` | `boolean \\| ((item: T) => boolean)` | Enable inline editing |",
516
+ "| `sortable` | `boolean` | Enable sorting (default true) |",
517
+ "| `defaultWidth` | `number` | Default column width in pixels |",
518
+ '| `pinned` | `"left" \\| "right"` | Pin column to an edge |',
519
+ "| `defaultVisible` | `boolean` | Show column by default (false = hidden, toggle via chooser) |",
520
+ '| `cellEditor` | `"text" \\| "select" \\| "checkbox" \\| "richSelect" \\| "date" \\| ComponentType` | Cell editor |',
521
+ "| `valueGetter` | `(item: T) => unknown` | Custom value extractor |",
522
+ "| `renderCell` | `(item: T) => ReactNode` | Custom cell renderer |",
484
523
  "",
485
524
  "## Common Patterns",
486
525
  "",
@@ -488,27 +527,27 @@ Tip: use \`get_code_example\` with framework="${result.framework}" or \`search_d
488
527
  "```tsx",
489
528
  'import { OGrid } from "@alaarab/ogrid-react-radix";',
490
529
  "const columns = [",
491
- ' { columnId: "name", headerName: "Name", type: "text", filter: "text" },',
492
- ' { columnId: "age", headerName: "Age", type: "numeric", sortable: true },',
530
+ ' { columnId: "name", name: "Name", type: "text", filterable: { type: "text" } },',
531
+ ' { columnId: "age", name: "Age", type: "numeric", sortable: true },',
493
532
  "];",
494
- "<OGrid data={rows} columns={columns} pagination={50} />",
533
+ "<OGrid data={rows} columns={columns} getRowId={(r) => r.id} defaultPageSize={50} />",
495
534
  "```",
496
535
  "",
497
536
  "### Server-side data",
498
537
  "```tsx",
499
538
  "const dataSource = {",
500
- " fetchPage: async ({ page, pageSize, sortModel, filterModel }) => {",
501
- " const res = await fetch(`/api/data?page=${page}&size=${pageSize}`);",
539
+ " fetchPage: async ({ page, pageSize, sort, filters, signal }) => {",
540
+ " const res = await fetch(`/api/data?page=${page}&size=${pageSize}`, { signal });",
502
541
  " const json = await res.json();",
503
- " return { rows: json.data, totalCount: json.total };",
504
- " }",
542
+ " return { items: json.data, totalCount: json.total };",
543
+ " },",
505
544
  "};",
506
- "<OGrid dataSource={dataSource} columns={columns} pagination={50} />",
545
+ "<OGrid dataSource={dataSource} columns={columns} getRowId={(r) => r.id} defaultPageSize={50} />",
507
546
  "```",
508
547
  "",
509
548
  "### Formula support",
510
549
  "```tsx",
511
- "<OGrid data={rows} columns={columns} formulas cellReferences />",
550
+ "<OGrid data={rows} columns={columns} getRowId={(r) => r.id} formulas cellReferences />",
512
551
  '// Users can type =SUM(A1:C3), =IF(A1>0,"yes","no"), etc.',
513
552
  "```"
514
553
  ].join("\n")
@@ -742,7 +781,7 @@ _${state.rowCount - limit} more rows not shown. Increase maxRows to see more._`)
742
781
  {
743
782
  gridId: z.string().describe("Grid ID as registered by connectGridToBridge()"),
744
783
  type: z.enum(["update_cell", "set_filter", "clear_filters", "set_sort", "go_to_page"]).describe("Command type"),
745
- payload: z.record(z.unknown()).describe("Command-specific payload (see tool description for fields per type)"),
784
+ payload: z.record(z.string(), z.unknown()).describe("Command-specific payload (see tool description for fields per type)"),
746
785
  timeoutMs: z.number().int().min(100).max(3e4).optional().describe("How long to wait for the app to execute the command (default: 5000ms)")
747
786
  },
748
787
  async ({ gridId, type, payload, timeoutMs }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-mcp",
3
- "version": "2.14.0",
3
+ "version": "2.14.2",
4
4
  "description": "MCP server for OGrid documentation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  "prebuild": "node scripts/bundle-docs.mjs",
26
26
  "build": "tsup",
27
27
  "dev": "tsup --watch",
28
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
29
  "test": "bun test --preload ../../bun-test.setup.ts"
29
30
  },
30
31
  "dependencies": {