figma-relai 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5YB7XMT6.js → chunk-EBP7POOP.js} +82 -28
- package/dist/chunk-EBP7POOP.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{manifest-OUVKP73V.js → manifest-CFCCUJEV.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-5YB7XMT6.js.map +0 -1
- /package/dist/{manifest-OUVKP73V.js.map → manifest-CFCCUJEV.js.map} +0 -0
|
@@ -9,7 +9,7 @@ function createServer() {
|
|
|
9
9
|
return new McpServer(
|
|
10
10
|
{
|
|
11
11
|
name: "Relai",
|
|
12
|
-
version: "0.2.
|
|
12
|
+
version: "0.2.1"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
instructions: `
|
|
@@ -116,6 +116,7 @@ var FIGMA_COMMANDS = [
|
|
|
116
116
|
"get_relaunch_data",
|
|
117
117
|
// Styling
|
|
118
118
|
"set_fill_color",
|
|
119
|
+
"set_fills",
|
|
119
120
|
"set_stroke_color",
|
|
120
121
|
"set_corner_radius",
|
|
121
122
|
"set_blend_mode",
|
|
@@ -154,6 +155,9 @@ var FIGMA_COMMANDS = [
|
|
|
154
155
|
"get_component_properties",
|
|
155
156
|
"set_component_properties",
|
|
156
157
|
"detach_instance",
|
|
158
|
+
"reset_instance",
|
|
159
|
+
"audit_colors",
|
|
160
|
+
"find_orphan_instances",
|
|
157
161
|
// Variables
|
|
158
162
|
"get_variable_collections",
|
|
159
163
|
"get_variables",
|
|
@@ -705,6 +709,8 @@ var propertiesSchema = z3.object({
|
|
|
705
709
|
blendMode: blendModeSchema.optional(),
|
|
706
710
|
// Paint
|
|
707
711
|
fillColor: colorSchema.optional(),
|
|
712
|
+
fills: z3.array(z3.record(z3.unknown())).optional().describe("Raw Paint[] \u2014 replaces the whole fill list; [] clears all fills"),
|
|
713
|
+
strokes: z3.array(z3.record(z3.unknown())).optional().describe("Raw Paint[] \u2014 replaces the whole stroke list; [] clears all strokes"),
|
|
708
714
|
strokeColor: colorSchema.optional(),
|
|
709
715
|
strokeWeight: z3.number().min(0).optional(),
|
|
710
716
|
cornerRadius: z3.number().min(0).optional(),
|
|
@@ -780,6 +786,11 @@ function mapPropertiesToCommands(nodeId, p) {
|
|
|
780
786
|
layoutSizingHorizontal: p.layoutSizingHorizontal,
|
|
781
787
|
layoutSizingVertical: p.layoutSizingVertical
|
|
782
788
|
});
|
|
789
|
+
if (p.fills !== void 0 || p.strokes !== void 0)
|
|
790
|
+
add("set_fills", {
|
|
791
|
+
...p.fills !== void 0 ? { fills: p.fills } : {},
|
|
792
|
+
...p.strokes !== void 0 ? { strokes: p.strokes } : {}
|
|
793
|
+
});
|
|
783
794
|
if (p.fillColor !== void 0) add("set_fill_color", { color: p.fillColor });
|
|
784
795
|
if (p.strokeColor !== void 0 || p.strokeWeight !== void 0)
|
|
785
796
|
add("set_stroke_color", {
|
|
@@ -1016,7 +1027,7 @@ import { z as z6 } from "zod";
|
|
|
1016
1027
|
function register5(server, sendCommand) {
|
|
1017
1028
|
server.tool(
|
|
1018
1029
|
"manage_components",
|
|
1019
|
-
"Component workflow: list local components, create a component from a node, create_set (combine components as variants), instantiate (place an instance by componentKey \u2014 imports from the team library if needed), get_props / set_props (component properties on an instance), get_overrides / set_overrides (copy overrides from a source instance to targets), detach.",
|
|
1030
|
+
"Component workflow: list local components, create a component from a node, create_set (combine components as variants), instantiate (place an instance by componentKey \u2014 imports from the team library if needed), get_props / set_props (component properties on an instance), get_overrides / set_overrides (copy overrides from a source instance to targets), reset_instance (clear ALL overrides so the instance re-inherits its main component \u2014 returns property snapshots before/after so you can re-apply what mattered), detach.",
|
|
1020
1031
|
{
|
|
1021
1032
|
action: z6.enum([
|
|
1022
1033
|
"list",
|
|
@@ -1027,9 +1038,10 @@ function register5(server, sendCommand) {
|
|
|
1027
1038
|
"set_props",
|
|
1028
1039
|
"get_overrides",
|
|
1029
1040
|
"set_overrides",
|
|
1041
|
+
"reset_instance",
|
|
1030
1042
|
"detach"
|
|
1031
1043
|
]),
|
|
1032
|
-
nodeId: z6.string().optional().describe("Target node (create/get_props/set_props/detach)"),
|
|
1044
|
+
nodeId: z6.string().optional().describe("Target node (create/get_props/set_props/reset_instance/detach)"),
|
|
1033
1045
|
componentIds: z6.array(z6.string()).optional().describe("create_set: components to combine"),
|
|
1034
1046
|
componentKey: z6.string().optional().describe("instantiate: component key (or node id)"),
|
|
1035
1047
|
x: z6.number().optional().describe("instantiate: position"),
|
|
@@ -1079,6 +1091,9 @@ function register5(server, sendCommand) {
|
|
|
1079
1091
|
targetNodeIds: args.targetNodeIds
|
|
1080
1092
|
});
|
|
1081
1093
|
break;
|
|
1094
|
+
case "reset_instance":
|
|
1095
|
+
result = await sendCommand("reset_instance", { nodeId: args.nodeId });
|
|
1096
|
+
break;
|
|
1082
1097
|
case "detach":
|
|
1083
1098
|
result = await sendCommand("detach_instance", { nodeId: args.nodeId });
|
|
1084
1099
|
break;
|
|
@@ -1842,7 +1857,8 @@ function register17(server, sendCommand) {
|
|
|
1842
1857
|
// src/tools/v2/context.ts
|
|
1843
1858
|
var context_exports = {};
|
|
1844
1859
|
__export(context_exports, {
|
|
1845
|
-
register: () => register18
|
|
1860
|
+
register: () => register18,
|
|
1861
|
+
styleCountsFrom: () => styleCountsFrom
|
|
1846
1862
|
});
|
|
1847
1863
|
import { z as z19 } from "zod";
|
|
1848
1864
|
|
|
@@ -2020,6 +2036,22 @@ function hasBinding(boundVars, prefix) {
|
|
|
2020
2036
|
}
|
|
2021
2037
|
|
|
2022
2038
|
// src/tools/v2/context.ts
|
|
2039
|
+
function styleCountsFrom(styles) {
|
|
2040
|
+
const counts = { paint: 0, text: 0, effect: 0, grid: 0 };
|
|
2041
|
+
if (Array.isArray(styles)) {
|
|
2042
|
+
for (const s of styles) {
|
|
2043
|
+
const t = (s.type || "").toLowerCase();
|
|
2044
|
+
if (t in counts) counts[t]++;
|
|
2045
|
+
}
|
|
2046
|
+
} else if (styles && typeof styles === "object") {
|
|
2047
|
+
const o = styles;
|
|
2048
|
+
counts.paint = Array.isArray(o.paintStyles) ? o.paintStyles.length : 0;
|
|
2049
|
+
counts.text = Array.isArray(o.textStyles) ? o.textStyles.length : 0;
|
|
2050
|
+
counts.effect = Array.isArray(o.effectStyles) ? o.effectStyles.length : 0;
|
|
2051
|
+
counts.grid = Array.isArray(o.gridStyles) ? o.gridStyles.length : 0;
|
|
2052
|
+
}
|
|
2053
|
+
return { ...counts, total: counts.paint + counts.text + counts.effect + counts.grid };
|
|
2054
|
+
}
|
|
2023
2055
|
function register18(server, sendCommand) {
|
|
2024
2056
|
server.tool(
|
|
2025
2057
|
"get_document_overview",
|
|
@@ -2035,30 +2067,21 @@ function register18(server, sendCommand) {
|
|
|
2035
2067
|
// Older plugin builds don't have the handler — degrade quietly
|
|
2036
2068
|
sendCommand("get_conventions", {}).catch(() => null)
|
|
2037
2069
|
]);
|
|
2038
|
-
const componentCount = docInfo.componentCount ??
|
|
2039
|
-
const stylesByType =
|
|
2040
|
-
if (Array.isArray(styles)) {
|
|
2041
|
-
for (const s of styles) {
|
|
2042
|
-
const t = (s.type || "").toLowerCase();
|
|
2043
|
-
if (t === "paint") stylesByType.paint++;
|
|
2044
|
-
else if (t === "text") stylesByType.text++;
|
|
2045
|
-
else if (t === "effect") stylesByType.effect++;
|
|
2046
|
-
else if (t === "grid") stylesByType.grid++;
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2070
|
+
const componentCount = docInfo.componentCount ?? null;
|
|
2071
|
+
const stylesByType = styleCountsFrom(styles);
|
|
2049
2072
|
const data = {
|
|
2050
2073
|
name: docInfo.name,
|
|
2051
2074
|
currentPage: docInfo.currentPage,
|
|
2052
2075
|
pages: docInfo.pages || [],
|
|
2053
2076
|
counts: {
|
|
2054
2077
|
components: componentCount,
|
|
2055
|
-
styles:
|
|
2078
|
+
styles: stylesByType.total,
|
|
2056
2079
|
variableCollections: Array.isArray(collections) ? collections.length : 0
|
|
2057
2080
|
}
|
|
2058
2081
|
};
|
|
2059
2082
|
const conventionsText = conventions?.content ?? null;
|
|
2060
2083
|
return standardResult({
|
|
2061
|
-
summary: `"${data.name}" \u2014 ${data.pages.length} pages,
|
|
2084
|
+
summary: `"${data.name}" \u2014 ${data.pages.length} pages, ` + (data.counts.components != null ? `${data.counts.components} components, ` : "") + `${data.counts.styles} styles, ${data.counts.variableCollections} variable collections` + (data.counts.components == null ? " (component inventory via get_design_system)" : "") + (conventionsText ? ". This file has conventions \u2014 follow them (see data.conventions)." : ""),
|
|
2062
2085
|
data: { ...data, ...conventionsText ? { conventions: conventionsText } : {} },
|
|
2063
2086
|
recommended_next: [
|
|
2064
2087
|
{ tool: "get_selection_context", reason: "Inspect currently selected nodes" },
|
|
@@ -2256,13 +2279,7 @@ function register18(server, sendCommand) {
|
|
|
2256
2279
|
modes: Array.isArray(c.modes) ? c.modes : [],
|
|
2257
2280
|
variableCount: Array.isArray(c.variableIds) ? c.variableIds.length : 0
|
|
2258
2281
|
}));
|
|
2259
|
-
const
|
|
2260
|
-
if (Array.isArray(styles)) {
|
|
2261
|
-
for (const s of styles) {
|
|
2262
|
-
const t = (s.type || "").toLowerCase();
|
|
2263
|
-
if (t in stylesByType) stylesByType[t]++;
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2282
|
+
const { total: _ignored, ...stylesByType } = styleCountsFrom(styles);
|
|
2266
2283
|
const totalVars = collectionSummaries.reduce((sum, c) => sum + c.variableCount, 0);
|
|
2267
2284
|
const data = {
|
|
2268
2285
|
collections: collectionSummaries,
|
|
@@ -2535,7 +2552,25 @@ function registerAnalysisTools(server, sendCommand) {
|
|
|
2535
2552
|
}
|
|
2536
2553
|
const unboundColors = [];
|
|
2537
2554
|
const counters = { totalProps: 0, boundCount: 0 };
|
|
2555
|
+
let hiddenCount = 0;
|
|
2556
|
+
let scanned = 0;
|
|
2557
|
+
let capped = false;
|
|
2538
2558
|
for (const id of targetIds) {
|
|
2559
|
+
let audit = null;
|
|
2560
|
+
try {
|
|
2561
|
+
audit = await sendCommand("audit_colors", { nodeId: id }, 6e4);
|
|
2562
|
+
} catch {
|
|
2563
|
+
audit = null;
|
|
2564
|
+
}
|
|
2565
|
+
if (audit && typeof audit.totalProperties === "number") {
|
|
2566
|
+
counters.totalProps += audit.totalProperties;
|
|
2567
|
+
counters.boundCount += audit.boundCount ?? 0;
|
|
2568
|
+
hiddenCount += audit.hiddenCount ?? 0;
|
|
2569
|
+
scanned += audit.scanned ?? 0;
|
|
2570
|
+
capped = capped || !!audit.capped;
|
|
2571
|
+
for (const issue of audit.issues ?? []) unboundColors.push(issue);
|
|
2572
|
+
continue;
|
|
2573
|
+
}
|
|
2539
2574
|
const [nodeInfo, boundVars] = await Promise.all([
|
|
2540
2575
|
sendCommand("get_node_info", { nodeId: id, depth: 2, maxNodes: 200 }),
|
|
2541
2576
|
sendCommand("get_bound_variables", { nodeId: id }).catch(() => null)
|
|
@@ -2557,11 +2592,15 @@ function registerAnalysisTools(server, sendCommand) {
|
|
|
2557
2592
|
boundCount: counters.boundCount,
|
|
2558
2593
|
unboundCount: unboundColors.length,
|
|
2559
2594
|
tokenCoverage,
|
|
2560
|
-
unboundColors
|
|
2595
|
+
unboundColors,
|
|
2596
|
+
...hiddenCount ? { hiddenCount } : {},
|
|
2597
|
+
...scanned ? { scanned } : {},
|
|
2598
|
+
...capped ? { capped } : {}
|
|
2561
2599
|
};
|
|
2562
|
-
const
|
|
2600
|
+
const visibleIssues = unboundColors.length - hiddenCount;
|
|
2601
|
+
const warnings = unboundColors.length > 0 ? [{ category: "tokens", message: `${unboundColors.length} color(s) not bound to design tokens${hiddenCount ? ` (${hiddenCount} on hidden paints)` : ""}` }] : [];
|
|
2563
2602
|
return standardResult({
|
|
2564
|
-
summary: `Color audit: ${
|
|
2603
|
+
summary: `Color audit: ${visibleIssues} unbound color(s)` + (hiddenCount ? ` + ${hiddenCount} on hidden paints` : "") + `. Token coverage: ${Math.round(data.tokenCoverage * 100)}%` + (capped ? " (scan capped \u2014 large subtree)" : ""),
|
|
2565
2604
|
data,
|
|
2566
2605
|
warnings,
|
|
2567
2606
|
recommended_next: unboundColors.length > 0 ? [
|
|
@@ -3159,6 +3198,21 @@ function register20(server, sendCommand) {
|
|
|
3159
3198
|
fix: !adequate ? { tool: "update_node", reason: "Increase size to meet touch target minimum", args: { nodeId: targetId } } : void 0
|
|
3160
3199
|
});
|
|
3161
3200
|
}
|
|
3201
|
+
try {
|
|
3202
|
+
const orph = await sendCommand("find_orphan_instances", { nodeId: targetId }, 6e4);
|
|
3203
|
+
if (orph && typeof orph.scanned === "number") {
|
|
3204
|
+
const found = orph.orphans ?? [];
|
|
3205
|
+
results.push({
|
|
3206
|
+
rule: "orphaned_instances",
|
|
3207
|
+
passed: found.length === 0,
|
|
3208
|
+
severity: "warning",
|
|
3209
|
+
message: found.length ? `${found.length} instance(s) reference deleted components (of ${orph.scanned} scanned): ${found.slice(0, 3).map((o) => o.name).join(", ")}${found.length > 3 ? "\u2026" : ""}` : `No orphaned instances (${orph.scanned} scanned)`,
|
|
3210
|
+
nodeId: targetId,
|
|
3211
|
+
fix: found.length ? { tool: "manage_components", reason: "Re-instantiate from a living component, or detach deliberately", args: { action: "list" } } : void 0
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
} catch {
|
|
3215
|
+
}
|
|
3162
3216
|
const passed = results.filter((r) => r.passed).length;
|
|
3163
3217
|
const failed = results.filter((r) => !r.passed).length;
|
|
3164
3218
|
const data = {
|
|
@@ -3459,4 +3513,4 @@ export {
|
|
|
3459
3513
|
listToolCatalog,
|
|
3460
3514
|
registerPrompts
|
|
3461
3515
|
};
|
|
3462
|
-
//# sourceMappingURL=chunk-
|
|
3516
|
+
//# sourceMappingURL=chunk-EBP7POOP.js.map
|