@grampro/kit 2.6.2 → 2.7.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.
- package/.gbs/skills/gbs-components/SKILL.md +5 -4
- package/.gbs/skills/gbs-components/references/install.md +17 -14
- package/CHANGELOG.md +201 -18
- package/README.md +24 -6
- package/index.cjs +59 -59
- package/package.json +2 -2
- package/schema/passport-overlay-v1.schema.json +67 -0
- package/source/components/accordion/passport.json +45 -8
- package/source/components/accordion/passport.manual.json +46 -0
- package/source/components/alert/passport.json +41 -8
- package/source/components/alert/passport.manual.json +42 -0
- package/source/components/avatar/passport.json +40 -7
- package/source/components/avatar/passport.manual.json +43 -0
- package/source/components/badge/passport.json +43 -7
- package/source/components/badge/passport.manual.json +46 -0
- package/source/components/breadcrumb/passport.json +36 -7
- package/source/components/breadcrumb/passport.manual.json +39 -0
- package/source/components/button/passport.json +1 -1
- package/source/components/button/passport.manual.json +1 -1
- package/source/components/card/passport.json +45 -8
- package/source/components/card/passport.manual.json +47 -0
- package/source/components/checkbox/core/agent.ts +33 -0
- package/source/components/checkbox/core/index.ts +2 -0
- package/source/components/checkbox/passport.json +83 -10
- package/source/components/checkbox/passport.manual.json +82 -0
- package/source/components/combobox/core/agent.ts +38 -0
- package/source/components/combobox/core/index.ts +2 -0
- package/source/components/combobox/passport.json +103 -11
- package/source/components/combobox/passport.manual.json +101 -0
- package/source/components/combobox/react/props.ts +14 -0
- package/source/components/combobox/react/useCombobox.ts +7 -1
- package/source/components/data-grid/export/xlsx.ts +9 -2
- package/source/components/data-grid/passport.json +2 -2
- package/source/components/data-grid/passport.manual.json +1 -1
- package/source/components/date-picker/core/agent.ts +42 -0
- package/source/components/date-picker/core/index.ts +2 -0
- package/source/components/date-picker/passport.json +102 -11
- package/source/components/date-picker/passport.manual.json +100 -0
- package/source/components/date-picker/react/DatePicker.tsx +6 -0
- package/source/components/date-picker/react/DateRangePicker.tsx +4 -0
- package/source/components/date-picker/react/props.ts +17 -0
- package/source/components/dialog/passport.json +54 -9
- package/source/components/dialog/passport.manual.json +54 -0
- package/source/components/file-uploader/passport.json +65 -10
- package/source/components/file-uploader/passport.manual.json +64 -0
- package/source/components/input/core/agent.ts +36 -0
- package/source/components/input/core/index.ts +2 -0
- package/source/components/input/passport.json +89 -12
- package/source/components/input/passport.manual.json +86 -0
- package/source/components/menu/passport.json +64 -9
- package/source/components/menu/passport.manual.json +64 -0
- package/source/components/modal/passport.json +52 -9
- package/source/components/modal/passport.manual.json +52 -0
- package/source/components/number-input/core/agent.ts +38 -0
- package/source/components/number-input/core/index.ts +2 -0
- package/source/components/number-input/passport.json +92 -11
- package/source/components/number-input/passport.manual.json +90 -0
- package/source/components/popover/passport.json +47 -8
- package/source/components/popover/passport.manual.json +48 -0
- package/source/components/progress/passport.json +37 -8
- package/source/components/progress/passport.manual.json +39 -0
- package/source/components/radio-group/core/agent.ts +34 -0
- package/source/components/radio-group/core/index.ts +2 -0
- package/source/components/radio-group/passport.json +77 -10
- package/source/components/radio-group/passport.manual.json +76 -0
- package/source/components/shared/core/agent/field-agent.ts +101 -0
- package/source/components/shared/core/agent/field-executors.ts +137 -0
- package/source/components/shared/core/agent/field.ts +649 -0
- package/source/components/shared/core/agent/index.ts +30 -0
- package/source/components/shared/core/agent/write.ts +58 -0
- package/source/components/shared/react/useFieldAgent.ts +88 -0
- package/source/components/skeleton/passport.json +37 -6
- package/source/components/skeleton/passport.manual.json +39 -0
- package/source/components/spinner/passport.json +34 -7
- package/source/components/spinner/passport.manual.json +37 -0
- package/source/components/switch/core/agent.ts +31 -0
- package/source/components/switch/core/index.ts +2 -0
- package/source/components/switch/passport.json +68 -10
- package/source/components/switch/passport.manual.json +67 -0
- package/source/components/tabs/passport.json +50 -9
- package/source/components/tabs/passport.manual.json +50 -0
- package/source/components/textarea/core/agent.ts +34 -0
- package/source/components/textarea/core/index.ts +2 -0
- package/source/components/textarea/passport.json +83 -10
- package/source/components/textarea/passport.manual.json +82 -0
- package/source/components/toaster/passport.json +41 -8
- package/source/components/toaster/passport.manual.json +42 -0
- package/source/components/tooltip/passport.json +39 -6
- package/source/components/tooltip/passport.manual.json +41 -0
- package/tools/mcp/README.md +9 -1
- package/tools/mcp/design.cjs +6 -1
- package/tools/mcp/figma.cjs +49 -9
- package/tools/mcp/install.cjs +6 -1
- package/tools/mcp/server.cjs +14 -2
package/tools/mcp/figma.cjs
CHANGED
|
@@ -145,7 +145,7 @@ function layoutOf(node) {
|
|
|
145
145
|
* Figma wraps nearly every string in its own frame, and passing that
|
|
146
146
|
* structure through verbatim buries the design in scaffolding.
|
|
147
147
|
*/
|
|
148
|
-
function condense(node, depth) {
|
|
148
|
+
function condense(node, depth, names = { components: {}, componentSets: {} }) {
|
|
149
149
|
const out = { name: node.name, type: node.type };
|
|
150
150
|
|
|
151
151
|
const layout = layoutOf(node);
|
|
@@ -158,8 +158,24 @@ function condense(node, depth) {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
/*
|
|
162
|
+
* An instance is a lookup, when the design was built from a library.
|
|
163
|
+
*
|
|
164
|
+
* `components` and `componentSets` come back in the same response and
|
|
165
|
+
* name every id in it. Reporting the id alone left an agent holding
|
|
166
|
+
* `1:234`, which tells it nothing; the name is the whole point.
|
|
167
|
+
*
|
|
168
|
+
* The set's name is preferred where there is one. A variant is called
|
|
169
|
+
* `Size=md, Variant=primary` and its set is called `Button`, and Button
|
|
170
|
+
* is what the agent is going to write.
|
|
171
|
+
*/
|
|
172
|
+
if (node.type === "INSTANCE" && node.componentId) {
|
|
173
|
+
const master = names.components[node.componentId];
|
|
174
|
+
const set = master && master.componentSetId && names.componentSets[master.componentSetId];
|
|
175
|
+
const resolved = (set && set.name) || (master && master.name);
|
|
176
|
+
if (resolved) out.component = resolved;
|
|
177
|
+
else out.instanceOf = node.componentId;
|
|
178
|
+
}
|
|
163
179
|
|
|
164
180
|
const style = {};
|
|
165
181
|
const fill = solidFill(node);
|
|
@@ -173,7 +189,7 @@ function condense(node, depth) {
|
|
|
173
189
|
|
|
174
190
|
const kids = (node.children || []).filter((c) => c.visible !== false);
|
|
175
191
|
if (kids.length && depth < MAX_DEPTH) {
|
|
176
|
-
const children = kids.map((c) => condense(c, depth + 1));
|
|
192
|
+
const children = kids.map((c) => condense(c, depth + 1, names));
|
|
177
193
|
// A frame that exists only to hold one text node is noise.
|
|
178
194
|
if (children.length === 1 && children[0].text && !children[0].children && !out.text) {
|
|
179
195
|
out.text = children[0].text;
|
|
@@ -395,7 +411,12 @@ function fromPluginExport(design, depth) {
|
|
|
395
411
|
try {
|
|
396
412
|
parsed = typeof design === "string" ? JSON.parse(design) : design;
|
|
397
413
|
} catch (error) {
|
|
398
|
-
|
|
414
|
+
// The parse error carries a slice of the input; do not pass it on.
|
|
415
|
+
return {
|
|
416
|
+
error:
|
|
417
|
+
"That is not JSON. Expected the output of the Figma plugin's " +
|
|
418
|
+
"'Export selection for an agent', or a path to a file it saved.",
|
|
419
|
+
};
|
|
399
420
|
}
|
|
400
421
|
if (!parsed || !Array.isArray(parsed.nodes)) {
|
|
401
422
|
return {
|
|
@@ -589,11 +610,29 @@ async function figmaFrame({ url, design, designFile, frame, depth }) {
|
|
|
589
610
|
repeatedNames(target, names);
|
|
590
611
|
|
|
591
612
|
const limit = Math.min(Number(depth) || MAX_DEPTH, MAX_DEPTH);
|
|
592
|
-
|
|
613
|
+
/*
|
|
614
|
+
* Figma puts the name maps beside the nodes for a /nodes request and at
|
|
615
|
+
* the top level for a whole file, so both shapes are gathered.
|
|
616
|
+
*/
|
|
617
|
+
const library = { components: {}, componentSets: {} };
|
|
618
|
+
for (const source of [file, ...Object.values(file.nodes || {})]) {
|
|
619
|
+
if (!source) continue;
|
|
620
|
+
Object.assign(library.components, source.components || {});
|
|
621
|
+
Object.assign(library.componentSets, source.componentSets || {});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const tree = condense(target, MAX_DEPTH - limit, library);
|
|
593
625
|
|
|
626
|
+
/*
|
|
627
|
+
* Only resolved names are collected. An instance whose master is not in
|
|
628
|
+
* the response keeps its id, and listing the node's own name beside real
|
|
629
|
+
* component names would read as though it were one.
|
|
630
|
+
*/
|
|
594
631
|
const instances = [];
|
|
632
|
+
let unresolved = 0;
|
|
595
633
|
const findInstances = (n) => {
|
|
596
|
-
if (n.
|
|
634
|
+
if (n.component) instances.push(n.component);
|
|
635
|
+
else if (n.instanceOf) unresolved += 1;
|
|
597
636
|
for (const c of n.children || []) findInstances(c);
|
|
598
637
|
};
|
|
599
638
|
findInstances(tree);
|
|
@@ -609,10 +648,11 @@ async function figmaFrame({ url, design, designFile, frame, depth }) {
|
|
|
609
648
|
.sort((a, b) => b[1] - a[1])
|
|
610
649
|
.slice(0, 20)
|
|
611
650
|
.map(([name, n]) => ({ name, times: n })),
|
|
612
|
-
builtFromLibrary: instances.length > 0,
|
|
651
|
+
builtFromLibrary: instances.length > 0 || unresolved > 0,
|
|
613
652
|
tree,
|
|
614
653
|
howToUse: instances.length
|
|
615
|
-
?
|
|
654
|
+
? `This design is built from components: ${[...new Set(instances)].slice(0, 12).join(", ")}`
|
|
655
|
+
+ ". `component` names the master on each instance — use it rather than inferring."
|
|
616
656
|
: "This design was drawn without the component library, so nothing names a component directly. Use the region names and structure to decide which catalogue component each part is, then call get_component before writing it.",
|
|
617
657
|
};
|
|
618
658
|
}
|
package/tools/mcp/install.cjs
CHANGED
|
@@ -86,7 +86,12 @@ function installJson(file, cwd) {
|
|
|
86
86
|
try {
|
|
87
87
|
config = JSON.parse(existing);
|
|
88
88
|
} catch (error) {
|
|
89
|
-
|
|
89
|
+
// Naming the file is the useful part; the parse error would quote it.
|
|
90
|
+
return {
|
|
91
|
+
file,
|
|
92
|
+
status: "failed",
|
|
93
|
+
detail: `${file} is not valid JSON, so it has been left alone.`,
|
|
94
|
+
};
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
|
package/tools/mcp/server.cjs
CHANGED
|
@@ -161,6 +161,18 @@ function getComponent(name) {
|
|
|
161
161
|
states: p.states || [],
|
|
162
162
|
composition: p.composition || {},
|
|
163
163
|
accessibility: p.accessibility || {},
|
|
164
|
+
/*
|
|
165
|
+
* The authored half of the passport. `safeMutations` is the one an agent
|
|
166
|
+
* acts on directly — it is the difference between "you may restyle this"
|
|
167
|
+
* and "you are about to answer a form on the user's behalf" — so leaving
|
|
168
|
+
* it out of the tool meant authoring it and then never telling anyone.
|
|
169
|
+
*
|
|
170
|
+
* `operations` is empty for every component that is not agent-operable
|
|
171
|
+
* yet, and an empty object is the honest answer to "what can you drive".
|
|
172
|
+
*/
|
|
173
|
+
safeMutations: p.safeMutations || {},
|
|
174
|
+
intentDomains: p.intentDomains || [],
|
|
175
|
+
operations: p.operations || {},
|
|
164
176
|
};
|
|
165
177
|
}
|
|
166
178
|
|
|
@@ -371,7 +383,7 @@ const TOOLS = [
|
|
|
371
383
|
{
|
|
372
384
|
name: "figma_frame",
|
|
373
385
|
description:
|
|
374
|
-
"BETA. Read a Figma design
|
|
386
|
+
"BETA. Read a Figma design. Start with a URL (`url`, needs FIGMA_TOKEN): it is one step, and it resolves the names of any component instances in the frame. A file the kit's Figma plugin saved (`designFile`) needs no token and carries every screen at once, which is what to use for a whole product or when a token is unavailable; that export pasted in (`design`) works for a small frame. Called with just a URL it lists the frames in the file; called with `frame` it returns that screen's structure, text, the repeated regions that indicate lists and tables, and the theme variables its colours and radii map onto.",
|
|
375
387
|
inputSchema: {
|
|
376
388
|
type: "object",
|
|
377
389
|
properties: {
|
|
@@ -384,7 +396,7 @@ const TOOLS = [
|
|
|
384
396
|
designFile: {
|
|
385
397
|
type: "string",
|
|
386
398
|
description:
|
|
387
|
-
"Path to a .json saved by the plugin's 'Save .json' button. Preferred over `design`: the file is read here, so the raw export never passes through the conversation.",
|
|
399
|
+
"Path to a .json saved by the plugin's 'Save .json' button, or to one screen of a folder `grampro-kit design` wrote. Preferred over `design`: the file is read here, so the raw export never passes through the conversation.",
|
|
388
400
|
},
|
|
389
401
|
frame: { type: "string", description: "Which frame to read, from the listing." },
|
|
390
402
|
depth: { type: "number", description: "How deep to descend. Lower it if the payload is large." },
|