@grampro/kit 2.4.3 → 2.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 (73) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +12 -0
  3. package/index.cjs +44 -2
  4. package/package.json +1 -1
  5. package/source/components/accordion/passport.json +11 -7
  6. package/source/components/accordion/react/Accordion.tsx +5 -0
  7. package/source/components/alert/passport.json +3 -3
  8. package/source/components/avatar/passport.json +8 -6
  9. package/source/components/avatar/react/Avatar.tsx +4 -0
  10. package/source/components/badge/passport.json +8 -6
  11. package/source/components/badge/react/Badge.tsx +1 -0
  12. package/source/components/badge/react/Tag.tsx +1 -0
  13. package/source/components/breadcrumb/passport.json +8 -6
  14. package/source/components/breadcrumb/react/Breadcrumb.tsx +1 -0
  15. package/source/components/button/passport.json +8 -6
  16. package/source/components/button/react/Button.tsx +1 -0
  17. package/source/components/card/passport.json +17 -9
  18. package/source/components/card/react/Card.tsx +5 -0
  19. package/source/components/card/react/Stat.tsx +5 -0
  20. package/source/components/checkbox/passport.json +32 -14
  21. package/source/components/checkbox/react/Checkbox.tsx +17 -0
  22. package/source/components/checkbox/react/CheckboxGroup.tsx +6 -0
  23. package/source/components/combobox/passport.json +36 -16
  24. package/source/components/combobox/react/Select.tsx +6 -1
  25. package/source/components/combobox/react/props.ts +23 -0
  26. package/source/components/data-grid/core/types.ts +53 -0
  27. package/source/components/data-grid/passport.json +82 -30
  28. package/source/components/data-grid/react/AskGrid.tsx +10 -0
  29. package/source/components/data-grid/react/DataGrid.tsx +16 -0
  30. package/source/components/date-picker/passport.json +30 -14
  31. package/source/components/date-picker/react/DatePicker.tsx +5 -0
  32. package/source/components/date-picker/react/props.ts +13 -1
  33. package/source/components/dialog/core/types.ts +14 -0
  34. package/source/components/dialog/passport.json +26 -12
  35. package/source/components/dialog/react/Dialog.tsx +4 -0
  36. package/source/components/file-uploader/passport.json +44 -18
  37. package/source/components/file-uploader/react/FileUploader.tsx +39 -0
  38. package/source/components/input/passport.json +14 -8
  39. package/source/components/input/react/Input.tsx +3 -0
  40. package/source/components/menu/passport.json +29 -13
  41. package/source/components/menu/react/Menu.tsx +7 -0
  42. package/source/components/menu/react/items.tsx +4 -0
  43. package/source/components/modal/passport.json +14 -8
  44. package/source/components/modal/react/Modal.tsx +6 -0
  45. package/source/components/number-input/passport.json +23 -11
  46. package/source/components/number-input/react/NumberInput.tsx +13 -0
  47. package/source/components/popover/passport.json +17 -9
  48. package/source/components/popover/react/Popover.tsx +7 -0
  49. package/source/components/progress/passport.json +3 -3
  50. package/source/components/radio-group/passport.json +32 -14
  51. package/source/components/radio-group/react/Radio.tsx +17 -0
  52. package/source/components/radio-group/react/RadioGroup.tsx +13 -0
  53. package/source/components/skeleton/passport.json +8 -6
  54. package/source/components/skeleton/react/Skeleton.tsx +5 -0
  55. package/source/components/spinner/passport.json +3 -3
  56. package/source/components/switch/passport.json +23 -11
  57. package/source/components/switch/react/Switch.tsx +14 -0
  58. package/source/components/tabs/passport.json +11 -7
  59. package/source/components/tabs/react/Tabs.tsx +2 -0
  60. package/source/components/textarea/passport.json +14 -8
  61. package/source/components/textarea/react/Textarea.tsx +3 -0
  62. package/source/components/toaster/passport.json +8 -6
  63. package/source/components/toaster/react/Toaster.tsx +4 -0
  64. package/source/components/tooltip/passport.json +3 -3
  65. package/tools/mcp/README.md +224 -0
  66. package/tools/mcp/figma.cjs +445 -0
  67. package/tools/mcp/install.cjs +270 -0
  68. package/tools/mcp/secret-cli.cjs +174 -0
  69. package/tools/mcp/secret.cjs +289 -0
  70. package/tools/mcp/server.cjs +497 -0
  71. package/tools/passport/cli.cjs +8 -1
  72. package/tools/passport/extract.cjs +7 -3
  73. package/tools/passport/merge.cjs +24 -4
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  "source": {
15
- "libraryVersion": "2.4.3",
15
+ "libraryVersion": "2.5.0",
16
16
  "files": [
17
17
  "core/index.ts",
18
18
  "core/navigation.ts",
@@ -21,7 +21,7 @@
21
21
  "react/Tabs.tsx",
22
22
  "react/context.ts"
23
23
  ],
24
- "sourceHash": "sha256:af648a096e1a1c4e3f61c57b4e61a6c115c3d2351d5f2c8c6b4519348ae80dfc"
24
+ "sourceHash": "sha256:3d8e29857c59101e547f234c45735db68cc87ee143cc87d841372ec612e2c663"
25
25
  },
26
26
  "props": [
27
27
  {
@@ -124,7 +124,9 @@
124
124
  "type": "other",
125
125
  "typeText": "ReactNode",
126
126
  "required": false,
127
- "origin": "derived"
127
+ "origin": "derived",
128
+ "description": "An icon before the tab's text.",
129
+ "descriptionOrigin": "jsdoc"
128
130
  },
129
131
  {
130
132
  "name": "id",
@@ -155,7 +157,9 @@
155
157
  "type": "function",
156
158
  "signature": "((value: string) => void) | undefined",
157
159
  "required": false,
158
- "origin": "derived"
160
+ "origin": "derived",
161
+ "description": "The newly selected tab. Not fired when the same tab is clicked again.",
162
+ "descriptionOrigin": "jsdoc"
159
163
  },
160
164
  {
161
165
  "name": "orientation",
@@ -305,9 +309,9 @@
305
309
  "safeMutations": {},
306
310
  "examples": [],
307
311
  "coverage": {
308
- "propsTotal": 25,
309
- "propsDescribed": 7,
310
- "describedPct": 28,
312
+ "propsTotal": 11,
313
+ "propsDescribed": 9,
314
+ "describedPct": 82,
311
315
  "authoredSections": [
312
316
  "accessibility"
313
317
  ]
@@ -33,6 +33,7 @@ export interface TabsProps extends Omit<
33
33
  value?: string;
34
34
  /** Selected tab at first. Without either, the first enabled tab is selected. */
35
35
  defaultValue?: string;
36
+ /** The newly selected tab. Not fired when the same tab is clicked again. */
36
37
  onValueChange?(value: string): void;
37
38
  /** Default `horizontal`. */
38
39
  orientation?: TabsOrientation;
@@ -221,6 +222,7 @@ export interface TabProps extends Omit<
221
222
  > {
222
223
  /** Identifies the tab and its panel. */
223
224
  value: string;
225
+ /** An icon before the tab's text. */
224
226
  icon?: ReactNode;
225
227
  /** A small count or label after the text, e.g. unread items. */
226
228
  badge?: ReactNode;
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.4.3",
11
+ "libraryVersion": "2.5.0",
12
12
  "files": [
13
13
  "core/count.ts",
14
14
  "core/index.ts",
@@ -19,7 +19,7 @@
19
19
  "react/locale.ts",
20
20
  "react/props.ts"
21
21
  ],
22
- "sourceHash": "sha256:35a538fdd2522c310be181bbb598fdc6eebb99fc5dd1a0ffab3cccf8d84ac43a"
22
+ "sourceHash": "sha256:a586004558fc7c4059756daa252c36028f3f9b1f4cf55fcccb9b5af2ce1e21e9"
23
23
  },
24
24
  "props": [
25
25
  {
@@ -93,7 +93,9 @@
93
93
  "type": "other",
94
94
  "typeText": "ReactNode",
95
95
  "required": false,
96
- "origin": "derived"
96
+ "origin": "derived",
97
+ "description": "A hint under the label, linked as the field's description.",
98
+ "descriptionOrigin": "jsdoc"
97
99
  },
98
100
  {
99
101
  "name": "disabled",
@@ -137,7 +139,9 @@
137
139
  "type": "other",
138
140
  "typeText": "ReactNode",
139
141
  "required": false,
140
- "origin": "derived"
142
+ "origin": "derived",
143
+ "description": "The field's name, shown above it and linked to it.",
144
+ "descriptionOrigin": "jsdoc"
141
145
  },
142
146
  {
143
147
  "name": "localeText",
@@ -267,7 +271,9 @@
267
271
  "typeText": "FieldSize | undefined",
268
272
  "required": false,
269
273
  "default": "md",
270
- "origin": "derived"
274
+ "origin": "derived",
275
+ "description": "Default `md`.",
276
+ "descriptionOrigin": "jsdoc"
271
277
  },
272
278
  {
273
279
  "name": "style",
@@ -372,9 +378,9 @@
372
378
  "safeMutations": {},
373
379
  "examples": [],
374
380
  "coverage": {
375
- "propsTotal": 35,
376
- "propsDescribed": 8,
377
- "describedPct": 23,
381
+ "propsTotal": 12,
382
+ "propsDescribed": 10,
383
+ "describedPct": 83,
378
384
  "authoredSections": [
379
385
  "accessibility"
380
386
  ]
@@ -28,10 +28,13 @@ export interface TextareaProps
28
28
  defaultValue?: string;
29
29
  /** The new text on every change. The native `onChange` fires as well. */
30
30
  onValueChange?(value: string): void;
31
+ /** The field's name, shown above it and linked to it. */
31
32
  label?: ReactNode;
33
+ /** A hint under the label, linked as the field's description. */
32
34
  description?: ReactNode;
33
35
  /** Message below the field; also marks it invalid. */
34
36
  error?: ReactNode;
37
+ /** Default `md`. */
35
38
  size?: FieldSize;
36
39
  /** Grow and shrink with the text, between `minRows` and `maxRows`. Default false. */
37
40
  autoResize?: boolean;
@@ -11,7 +11,7 @@
11
11
  ]
12
12
  },
13
13
  "source": {
14
- "libraryVersion": "2.4.3",
14
+ "libraryVersion": "2.5.0",
15
15
  "files": [
16
16
  "core/api.ts",
17
17
  "core/index.ts",
@@ -26,7 +26,7 @@
26
26
  "react/props.ts",
27
27
  "react/useToasts.ts"
28
28
  ],
29
- "sourceHash": "sha256:ac828195f4d7ced89c99eb2bd78bb2581ba7a81d62dcfb892b9acef75bddda77"
29
+ "sourceHash": "sha256:976553d5d606c12b441caba76badad73812eea6dde400b51eb3ee17d05db7871"
30
30
  },
31
31
  "props": [
32
32
  {
@@ -63,7 +63,9 @@
63
63
  ],
64
64
  "typeText": "'ltr' | 'rtl' | 'auto' | undefined",
65
65
  "required": false,
66
- "origin": "derived"
66
+ "origin": "derived",
67
+ "description": "Writing direction, which decides which corner `position` means.\nDefault `auto`, taken from the document.",
68
+ "descriptionOrigin": "jsdoc"
67
69
  },
68
70
  {
69
71
  "name": "duration",
@@ -211,9 +213,9 @@
211
213
  "safeMutations": {},
212
214
  "examples": [],
213
215
  "coverage": {
214
- "propsTotal": 12,
215
- "propsDescribed": 7,
216
- "describedPct": 58,
216
+ "propsTotal": 8,
217
+ "propsDescribed": 8,
218
+ "describedPct": 100,
217
219
  "authoredSections": [
218
220
  "accessibility"
219
221
  ]
@@ -36,6 +36,10 @@ export interface ToasterProps {
36
36
  icons?: Partial<Record<ToastType, ReactNode>>;
37
37
  /** A store from `createToastStore()`. Defaults to the one behind `toast()`. */
38
38
  store?: ToastStore;
39
+ /**
40
+ * Writing direction, which decides which corner `position` means.
41
+ * Default `auto`, taken from the document.
42
+ */
39
43
  dir?: "ltr" | "rtl" | "auto";
40
44
  className?: string;
41
45
  classNames?: Partial<Record<ToasterSlot, string>>;
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.4.3",
11
+ "libraryVersion": "2.5.0",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/types.ts",
@@ -160,9 +160,9 @@
160
160
  "safeMutations": {},
161
161
  "examples": [],
162
162
  "coverage": {
163
- "propsTotal": 12,
163
+ "propsTotal": 9,
164
164
  "propsDescribed": 7,
165
- "describedPct": 58,
165
+ "describedPct": 78,
166
166
  "authoredSections": [
167
167
  "accessibility"
168
168
  ]
@@ -0,0 +1,224 @@
1
+ # GramproKit MCP server — beta
2
+
3
+ Serves the component catalogue to a coding agent, so it writes against the
4
+ real contract instead of recalling one.
5
+
6
+ > **Beta.** The tools work and are tested, but their names and shapes may
7
+ > change before this settles.
8
+
9
+ ## Setup
10
+
11
+ ```bash
12
+ npx @grampro/kit mcp install
13
+ ```
14
+
15
+ That writes the configuration for every agent it knows about — Claude Code,
16
+ Codex and Cursor — and tells you which files it touched. Restrict it with
17
+ `--for codex` (or `claude`, `cursor`, or a comma-separated list).
18
+
19
+ It is worth using rather than editing the files by hand: it writes UTF-8
20
+ without a byte-order mark and with LF endings, which is the one thing that
21
+ reliably goes wrong. PowerShell's `Out-File -Encoding utf8` adds a BOM, and
22
+ every JSON parser then rejects the file with an error that names the first
23
+ character and explains nothing.
24
+
25
+ Running it again is safe. It merges into whatever is already there, leaves
26
+ other servers alone, and says `unchanged` when there is nothing to do.
27
+
28
+ It writes to the project root — the nearest folder up from you with a `.git`
29
+ or a `package.json` — and prints which one it chose, because an agent reads
30
+ these files from the folder it has open and writing to a subdirectory is the
31
+ one way this can quietly do nothing.
32
+
33
+ ### If the server does not appear
34
+
35
+ The file being written is not the same as the server being loaded, and in
36
+ Claude Code two things stand between them.
37
+
38
+ **It needs approving.** A `.mcp.json` server is project-scoped, and Claude
39
+ Code asks before trusting one — until you say yes it sits at `Pending
40
+ approval`, and nothing asks twice. `claude mcp reset-project-choices` brings
41
+ the prompt back if it was dismissed.
42
+
43
+ **It needs a fresh session.** The configuration is read when a session
44
+ starts, so an editor window that was already open sees nothing. In the VS
45
+ Code extension, run *Developer: Reload Window*.
46
+
47
+ `/mcp` lists the servers and their state, which is the quickest way to tell
48
+ "not configured" from "configured, not approved".
49
+
50
+ To skip both, install it for yourself rather than for the project:
51
+
52
+ ```bash
53
+ claude mcp add -s user grampro -- npx -y @grampro/kit mcp
54
+ ```
55
+
56
+ User scope lives in `~/.claude.json`, loads in every project, and needs no
57
+ approval because it is yours rather than the repository's. Project scope is
58
+ the better choice when you want the rest of the team to get it from a commit.
59
+
60
+ ### What it writes
61
+
62
+ | agent | file | format |
63
+ | --- | --- | --- |
64
+ | Claude Code | `.mcp.json` | `mcpServers.grampro` |
65
+ | Codex | `.codex/config.toml` | `[mcp_servers.grampro]` |
66
+ | Cursor | `.cursor/mcp.json` | `mcpServers.grampro` |
67
+
68
+ The Codex config is project-scoped. For every project instead, put the same
69
+ `[mcp_servers.grampro]` block in `~/.codex/config.toml`.
70
+
71
+ A project that already has the package installed gets a direct `node`
72
+ invocation; anywhere else the config uses `npx -y @grampro/kit mcp`, which
73
+ works without an install but re-resolves the package on every start.
74
+
75
+ ### By hand, if you prefer
76
+
77
+ ```bash
78
+ claude mcp add grampro -- npx -y @grampro/kit mcp
79
+ ```
80
+
81
+ ```toml
82
+ # ~/.codex/config.toml
83
+ [mcp_servers.grampro]
84
+ command = "npx"
85
+ args = ["-y", "@grampro/kit", "mcp"]
86
+ ```
87
+
88
+ MCP servers are not invoked by name. There is no `/grampro`; the model calls
89
+ the tools itself when they are relevant. In Claude Code, `/mcp` shows whether
90
+ the server connected.
91
+
92
+ ## Why
93
+
94
+ An agent asked to build a page has two ways to learn a component's API: read
95
+ the source, which costs a great deal of context and still invites invention,
96
+ or be told. This tells it — one component at a time, from the passport that
97
+ was generated from the source and so cannot drift from it.
98
+
99
+ The catalogue is 280 KB of passports. A page needs two or three components,
100
+ which is a few KB. That difference is the whole argument for a server over a
101
+ document pasted into the prompt.
102
+
103
+ ## Tools
104
+
105
+ | tool | what it answers |
106
+ | --- | --- |
107
+ | `list_components` | What exists, and what each one is for |
108
+ | `get_component` | Props with types and allowed values, which are required, handler signatures, slots, states, composition rules |
109
+ | `get_example` | Working usage from the component's README |
110
+ | `get_guidance` | Authored notes on forms, overlays, the grid, pickers, styling, accessibility |
111
+ | `figma_frame` | A design's structure, text, repeated regions and theme variables |
112
+ | `install_component` | Copies components into the project being worked in |
113
+ | `validate_usage` | Whether the JSX an agent wrote matches the passports |
114
+
115
+ `validate_usage` is the one that earns its place. In the experiment that
116
+ justified building this, a page written from passports alone compiled on the
117
+ first attempt apart from a single prop the model had remembered from
118
+ elsewhere rather than read here. Every type, union value and handler
119
+ signature the passport carried came out right. So the remaining failure is
120
+ invention, and this catches it:
121
+
122
+ ```
123
+ <Alert variant="info" title="Hi" dismissible onDismiss={…} />
124
+
125
+ dismissible — not a prop of this component. Did you mean: onDismiss
126
+ ```
127
+
128
+ ## From a Figma design
129
+
130
+ Two routes, and the second needs no credentials.
131
+
132
+ **By URL.** Pass a `figma.com/design/...` link. Called with just the URL it
133
+ lists the frames in the file; called again with `frame` it returns that
134
+ screen. A link that already carries a `node-id` — which is what Figma copies
135
+ when you right-click a frame — skips straight to that frame in one call.
136
+ Needs a token.
137
+
138
+ **By plugin export.** In Figma, run **Grampro Kit → Export selection for an
139
+ agent**, then pass the JSON as `design`. The plugin reads the scene graph
140
+ directly, so this works on any file you can open, asks for no token, and
141
+ never touches the REST API — which also means it cannot be rate limited.
142
+
143
+ Either way the answer is the same shape: the structure, the text, the
144
+ regions whose names repeat — eight `Status cell` nodes are a column — and
145
+ the `--gbs-*` variables the design's own colours and radii map onto.
146
+
147
+ It does not decide which component each region is. A design drawn without
148
+ the component library has no component instances to look up, so that call is
149
+ inference, and an agent holding the catalogue and the designer's own names
150
+ infers better than string matching in a tool. What is not a kit component is
151
+ reported as such rather than approximated.
152
+
153
+ Colours are given roles rather than ranked. The most-used colour in a design
154
+ is the body text, and a near-black navy scores high on saturation, so both
155
+ "most common" and "most saturated" pick the wrong thing. Lightness and
156
+ saturation together find the accent.
157
+
158
+ ### Rate limits
159
+
160
+ Figma's limits on file reads are cost-based, and the cost is the size of the
161
+ response. Asking for a whole file to answer a question about one frame is
162
+ what earns a 429, and the lockout is long — a `Retry-After` of 395274 has
163
+ been observed on a single token.
164
+
165
+ So nothing here fetches a whole file. Listing the frames uses `depth=1`,
166
+ which returns the document's outline rather than its contents, and the frame
167
+ itself is then fetched by id. A URL carrying a `node-id` skips the listing
168
+ altogether. Responses are held for five minutes, so the natural two-call
169
+ flow — list, then read — costs one listing and one frame.
170
+
171
+ If a token is already locked out, the plugin export route still works.
172
+
173
+ ## Credentials
174
+
175
+ The URL route needs a Figma personal access token. Store it where the
176
+ operating system keeps credentials:
177
+
178
+ ```bash
179
+ npx @grampro/kit secret set figma
180
+ ```
181
+
182
+ It prompts, with the echo off, and never takes the value as an argument —
183
+ arguments land in shell history and in every process listing on the machine.
184
+ Piping works too, for a script: `echo "$TOKEN" | npx @grampro/kit secret set figma`.
185
+
186
+ | platform | where it goes |
187
+ | --- | --- |
188
+ | Windows | Data Protection API, tied to your Windows account |
189
+ | macOS | the keychain |
190
+ | Linux | Secret Service (GNOME Keyring, KWallet) |
191
+ | anything else | a 0600 file in your config directory, and it says so |
192
+
193
+ `secret list` shows what is stored, masked, and names the backend in use.
194
+ `secret remove figma` deletes it. Create the token at Figma → Settings →
195
+ Security → Personal access tokens, with `File content: read` and nothing
196
+ else.
197
+
198
+ The token is read from `FIGMA_TOKEN` in the environment first, then the
199
+ store, then a `.env` — the last only so that an upgrade does not break a
200
+ project set up the old way. Reading from a `.env` prints a note saying to
201
+ move it. A credential in a file inside a working tree is one `git add -A`
202
+ away from being published, which is why this moved.
203
+
204
+ ## Suggested use
205
+
206
+ Tell the agent to call `get_component` before writing JSX for a component,
207
+ and `validate_usage` on what it wrote before reporting back. Both are cheap,
208
+ and together they close the gap between code that looks right and code that
209
+ compiles.
210
+
211
+ ## Notes
212
+
213
+ No dependencies. MCP is newline-delimited JSON-RPC over stdio, which is a
214
+ few hundred lines, and a server shipped inside a library that every consumer
215
+ installs should not drag an SDK along with it.
216
+
217
+ Start-up is about the cost of an empty Node process. An agent spawns the
218
+ server once per session, and `index.cjs` routes `mcp` before loading the
219
+ CLI's own dependencies, which are no use to a JSON-RPC loop and cost around
220
+ 70ms. The passports are indexed on the first question and not re-read after.
221
+
222
+ The catalogue it serves is the one in this package, not the components
223
+ copied into your project — so an agent can see a component before you
224
+ install it, and `install_component` puts it in place.