faux-studio 0.3.5 → 0.3.7
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/index.js +139 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11007,14 +11007,14 @@ async function isCdpAlive(port) {
|
|
|
11007
11007
|
try {
|
|
11008
11008
|
const controller = new AbortController();
|
|
11009
11009
|
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
11010
|
-
const res = await fetch(`http://127.0.0.1:${port}/json/
|
|
11010
|
+
const res = await fetch(`http://127.0.0.1:${port}/json/list`, {
|
|
11011
11011
|
signal: controller.signal
|
|
11012
11012
|
});
|
|
11013
11013
|
clearTimeout(timeout);
|
|
11014
11014
|
if (!res.ok) return { alive: false, isFigma: false };
|
|
11015
|
-
const
|
|
11016
|
-
const
|
|
11017
|
-
return { alive: true, isFigma
|
|
11015
|
+
const targets = await res.json();
|
|
11016
|
+
const isFigma = targets.some((t) => t.url?.startsWith("views://"));
|
|
11017
|
+
return { alive: true, isFigma };
|
|
11018
11018
|
} catch {
|
|
11019
11019
|
return { alive: false, isFigma: false };
|
|
11020
11020
|
}
|
|
@@ -11041,7 +11041,7 @@ async function listTargets(port) {
|
|
|
11041
11041
|
}
|
|
11042
11042
|
var FIGMA_DESIGN_URL_RE = /figma\.com\/(file|design)\//;
|
|
11043
11043
|
function findFigmaDesignTarget(targets) {
|
|
11044
|
-
return targets.find((t) => t.type === "page" && FIGMA_DESIGN_URL_RE.test(t.url)) || null;
|
|
11044
|
+
return targets.find((t) => t.type === "page" && FIGMA_DESIGN_URL_RE.test(t.url)) || targets.find((t) => t.type === "page" && t.url.startsWith("views://")) || null;
|
|
11045
11045
|
}
|
|
11046
11046
|
async function probeCdpPorts() {
|
|
11047
11047
|
for (const port of KNOWN_CDP_PORTS) {
|
|
@@ -11096,7 +11096,8 @@ async function launchFigmaWithCdp() {
|
|
|
11096
11096
|
}
|
|
11097
11097
|
function launchFigmaProcess(figmaPath, port) {
|
|
11098
11098
|
if (process.platform === "darwin") {
|
|
11099
|
-
|
|
11099
|
+
const binary = `${figmaPath}/Contents/MacOS/Figma`;
|
|
11100
|
+
spawn2(binary, [`--remote-debugging-port=${port}`], {
|
|
11100
11101
|
detached: true,
|
|
11101
11102
|
stdio: "ignore"
|
|
11102
11103
|
}).unref();
|
|
@@ -25422,6 +25423,107 @@ function annotate(tool) {
|
|
|
25422
25423
|
if (!annotations) return tool;
|
|
25423
25424
|
return { ...tool, annotations };
|
|
25424
25425
|
}
|
|
25426
|
+
var RESOURCES = [
|
|
25427
|
+
{
|
|
25428
|
+
uri: "figma://file",
|
|
25429
|
+
name: "Current File",
|
|
25430
|
+
description: "File name, pages, and current page info",
|
|
25431
|
+
mimeType: "application/json",
|
|
25432
|
+
script: `
|
|
25433
|
+
const root = figma.root;
|
|
25434
|
+
const page = figma.currentPage;
|
|
25435
|
+
return {
|
|
25436
|
+
name: root.name,
|
|
25437
|
+
currentPage: { id: page.id, name: page.name },
|
|
25438
|
+
pages: root.children.map(p => ({
|
|
25439
|
+
id: p.id,
|
|
25440
|
+
name: p.name,
|
|
25441
|
+
childCount: p.children.length,
|
|
25442
|
+
})),
|
|
25443
|
+
};
|
|
25444
|
+
`
|
|
25445
|
+
},
|
|
25446
|
+
{
|
|
25447
|
+
uri: "figma://selection",
|
|
25448
|
+
name: "Current Selection",
|
|
25449
|
+
description: "Nodes the user has selected in Figma",
|
|
25450
|
+
mimeType: "application/json",
|
|
25451
|
+
script: `
|
|
25452
|
+
const sel = figma.currentPage.selection;
|
|
25453
|
+
return {
|
|
25454
|
+
count: sel.length,
|
|
25455
|
+
selection: sel.map(n => ({
|
|
25456
|
+
id: n.id,
|
|
25457
|
+
name: n.name,
|
|
25458
|
+
type: n.type,
|
|
25459
|
+
x: 'x' in n ? n.x : undefined,
|
|
25460
|
+
y: 'y' in n ? n.y : undefined,
|
|
25461
|
+
width: 'width' in n ? n.width : undefined,
|
|
25462
|
+
height: 'height' in n ? n.height : undefined,
|
|
25463
|
+
})),
|
|
25464
|
+
};
|
|
25465
|
+
`
|
|
25466
|
+
},
|
|
25467
|
+
{
|
|
25468
|
+
uri: "figma://variables",
|
|
25469
|
+
name: "Design Tokens",
|
|
25470
|
+
description: "All variable collections and their variables (colors, spacing, typography tokens)",
|
|
25471
|
+
mimeType: "application/json",
|
|
25472
|
+
script: `
|
|
25473
|
+
const collections = figma.variables.getLocalVariableCollections();
|
|
25474
|
+
return collections.map(c => ({
|
|
25475
|
+
id: c.id,
|
|
25476
|
+
name: c.name,
|
|
25477
|
+
modes: c.modes,
|
|
25478
|
+
variableCount: c.variableIds.length,
|
|
25479
|
+
variables: c.variableIds.map(vid => {
|
|
25480
|
+
const v = figma.variables.getVariableById(vid);
|
|
25481
|
+
if (!v) return null;
|
|
25482
|
+
return {
|
|
25483
|
+
id: v.id,
|
|
25484
|
+
name: v.name,
|
|
25485
|
+
resolvedType: v.resolvedType,
|
|
25486
|
+
valuesByMode: v.valuesByMode,
|
|
25487
|
+
};
|
|
25488
|
+
}).filter(Boolean),
|
|
25489
|
+
}));
|
|
25490
|
+
`
|
|
25491
|
+
},
|
|
25492
|
+
{
|
|
25493
|
+
uri: "figma://components",
|
|
25494
|
+
name: "Components",
|
|
25495
|
+
description: "Local components and component sets in the file",
|
|
25496
|
+
mimeType: "application/json",
|
|
25497
|
+
script: `
|
|
25498
|
+
const nodes = figma.root.findAllWithCriteria({ types: ['COMPONENT', 'COMPONENT_SET'] });
|
|
25499
|
+
return nodes.map(c => ({
|
|
25500
|
+
id: c.id,
|
|
25501
|
+
name: c.name,
|
|
25502
|
+
type: c.type,
|
|
25503
|
+
description: c.description || undefined,
|
|
25504
|
+
parent: c.parent ? { id: c.parent.id, name: c.parent.name } : undefined,
|
|
25505
|
+
}));
|
|
25506
|
+
`
|
|
25507
|
+
},
|
|
25508
|
+
{
|
|
25509
|
+
uri: "figma://styles",
|
|
25510
|
+
name: "Styles",
|
|
25511
|
+
description: "Text styles, paint styles, and effect styles defined in the file",
|
|
25512
|
+
mimeType: "application/json",
|
|
25513
|
+
script: `
|
|
25514
|
+
const textStyles = figma.getLocalTextStyles();
|
|
25515
|
+
const paintStyles = figma.getLocalPaintStyles();
|
|
25516
|
+
const effectStyles = figma.getLocalEffectStyles();
|
|
25517
|
+
return {
|
|
25518
|
+
textStyles: textStyles.map(s => ({
|
|
25519
|
+
id: s.id, name: s.name, fontSize: s.fontSize, fontName: s.fontName,
|
|
25520
|
+
})),
|
|
25521
|
+
paintStyles: paintStyles.map(s => ({ id: s.id, name: s.name })),
|
|
25522
|
+
effectStyles: effectStyles.map(s => ({ id: s.id, name: s.name })),
|
|
25523
|
+
};
|
|
25524
|
+
`
|
|
25525
|
+
}
|
|
25526
|
+
];
|
|
25425
25527
|
var INSTRUCTIONS = `You are connected to Figma Desktop via faux-studio. You can create, modify, and inspect designs using the tools below.
|
|
25426
25528
|
|
|
25427
25529
|
## Workflow
|
|
@@ -25449,6 +25551,14 @@ var INSTRUCTIONS = `You are connected to Figma Desktop via faux-studio. You can
|
|
|
25449
25551
|
- \`get_components\` \u2014 list available components
|
|
25450
25552
|
- \`get_screenshot\` \u2014 visual capture of the canvas
|
|
25451
25553
|
|
|
25554
|
+
## Resources (via @mentions)
|
|
25555
|
+
Resources provide quick read-only access to Figma state without tool calls:
|
|
25556
|
+
- \`figma://file\` \u2014 file name, pages, current page
|
|
25557
|
+
- \`figma://selection\` \u2014 currently selected nodes
|
|
25558
|
+
- \`figma://variables\` \u2014 all design tokens and variable collections
|
|
25559
|
+
- \`figma://components\` \u2014 local component library
|
|
25560
|
+
- \`figma://styles\` \u2014 text, paint, and effect styles
|
|
25561
|
+
|
|
25452
25562
|
## Best Practices
|
|
25453
25563
|
- Always read the existing design system before creating new elements.
|
|
25454
25564
|
- Use auto-layout (horizontal/vertical) for responsive layouts \u2014 avoid absolute positioning.
|
|
@@ -25457,9 +25567,9 @@ var INSTRUCTIONS = `You are connected to Figma Desktop via faux-studio. You can
|
|
|
25457
25567
|
- Create components for reusable UI patterns.`;
|
|
25458
25568
|
function createMcpServer(deps) {
|
|
25459
25569
|
const server2 = new Server(
|
|
25460
|
-
{ name: "faux-studio", version: "0.3.
|
|
25570
|
+
{ name: "faux-studio", version: "0.3.7" },
|
|
25461
25571
|
{
|
|
25462
|
-
capabilities: { tools: { listChanged: true }, logging: {} },
|
|
25572
|
+
capabilities: { tools: { listChanged: true }, resources: {}, logging: {} },
|
|
25463
25573
|
instructions: INSTRUCTIONS
|
|
25464
25574
|
}
|
|
25465
25575
|
);
|
|
@@ -25598,6 +25708,27 @@ Run the \`login\` tool to re-authenticate.` : `Error: ${message}`
|
|
|
25598
25708
|
};
|
|
25599
25709
|
}
|
|
25600
25710
|
});
|
|
25711
|
+
server2.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
25712
|
+
resources: RESOURCES.map(({ uri, name, description, mimeType }) => ({
|
|
25713
|
+
uri,
|
|
25714
|
+
name,
|
|
25715
|
+
description,
|
|
25716
|
+
mimeType
|
|
25717
|
+
}))
|
|
25718
|
+
}));
|
|
25719
|
+
server2.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
25720
|
+
const { uri } = request.params;
|
|
25721
|
+
const resource = RESOURCES.find((r) => r.uri === uri);
|
|
25722
|
+
if (!resource) {
|
|
25723
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
25724
|
+
}
|
|
25725
|
+
log(`Reading resource: ${uri}`);
|
|
25726
|
+
const result = await deps.executeScript(resource.script);
|
|
25727
|
+
const text = result === void 0 || result === null ? "{}" : typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
25728
|
+
return {
|
|
25729
|
+
contents: [{ uri, mimeType: resource.mimeType, text }]
|
|
25730
|
+
};
|
|
25731
|
+
});
|
|
25601
25732
|
return server2;
|
|
25602
25733
|
}
|
|
25603
25734
|
async function startServer(server2) {
|