@createlex/figma-swiftui-mcp 1.2.9 → 1.4.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.
|
@@ -316,8 +316,8 @@ function startBridgeServer(options = {}) {
|
|
|
316
316
|
const assetCount = data.assetExportPlan?.candidates?.length ?? 0;
|
|
317
317
|
const aiWorkflow = {
|
|
318
318
|
IF_YOU_HAVE_MCP_ACCESS: {
|
|
319
|
-
step1: 'Call the MCP tool `write_selection_to_xcode` (
|
|
320
|
-
step2: 'After write_selection_to_xcode returns, ONLY refine
|
|
319
|
+
step1: 'Call the MCP tool `write_selection_to_xcode` with generationMode="editable" (the default). It exports individual PNG assets with correct blendMode info and generates Image("name").blendMode() calls automatically.',
|
|
320
|
+
step2: 'After write_selection_to_xcode returns, ONLY refine: add GeometryReader for responsiveness, wire @State interactions. DO NOT rewrite Image("name") calls, DO NOT switch to fidelity mode unless the whole frame is a single photo with no text.',
|
|
321
321
|
mcpTool: 'write_selection_to_xcode',
|
|
322
322
|
mcpServer: 'figma-swiftui',
|
|
323
323
|
},
|
package/companion/mcp-server.mjs
CHANGED
|
@@ -765,11 +765,11 @@ server.registerTool('write_generated_swiftui_to_xcode', {
|
|
|
765
765
|
});
|
|
766
766
|
|
|
767
767
|
server.registerTool('write_selection_to_xcode', {
|
|
768
|
-
description: 'Generate SwiftUI from the connected Figma selection and write it into the configured Xcode project. THIS IS THE CORRECT TOOL TO CALL — it exports real PNG assets to Assets.xcassets and generates Image("name") references automatically.
|
|
768
|
+
description: 'Generate SwiftUI from the connected Figma selection and write it into the configured Xcode project. THIS IS THE CORRECT TOOL TO CALL — it exports real PNG assets to Assets.xcassets and generates Image("name") references automatically. Always use the default generationMode="editable" — individual assets include blendModeSwiftUI values so blend modes are applied correctly on each Image(). Only use generationMode="fidelity" when the ENTIRE frame is a single photographic image or illustration with no text, no interactive elements, and no distinct layers (e.g. a full-bleed photo background).',
|
|
769
769
|
inputSchema: {
|
|
770
770
|
nodeIds: z.array(z.string()).optional().describe('Optional list of Figma node ids. If omitted, uses the current selection'),
|
|
771
771
|
includeOverflow: z.boolean().default(false).describe('Ignore Figma clipping when generating layout'),
|
|
772
|
-
generationMode: z.enum(['editable', 'fidelity']).default('editable').describe('
|
|
772
|
+
generationMode: z.enum(['editable', 'fidelity']).default('editable').describe('Default: editable — generates individual SwiftUI elements with correct .blendMode() on each Image(). Use fidelity ONLY when the whole frame is a single photo/illustration with no text or interactive elements.'),
|
|
773
773
|
projectPath: z.string().optional().describe('Optional Xcode source folder override'),
|
|
774
774
|
},
|
|
775
775
|
}, async ({ nodeIds, includeOverflow, generationMode, projectPath }) => {
|
package/companion/setup.cjs
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Supported targets:
|
|
8
8
|
* - Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)
|
|
9
|
-
* - Claude Code (~/.claude.json → mcpServers)
|
|
9
|
+
* - Claude Code (~/.claude.json → mcpServers) (covers CLI + desktop + web)
|
|
10
10
|
* - Cursor (~/.cursor/mcp.json)
|
|
11
11
|
* - Windsurf (~/.codeium/windsurf/mcp_config.json)
|
|
12
12
|
* - VS Code (~/.vscode/mcp.json — user-level)
|
|
13
|
+
* - OpenCode (~/.config/opencode/opencode.json → mcp)
|
|
14
|
+
* - Codex CLI (~/.codex/config.toml → [mcp_servers.figma-swiftui])
|
|
15
|
+
* - Gemini CLI (~/.gemini/settings.json)
|
|
13
16
|
* - Antigravity (~/.gemini/antigravity/mcp_config.json)
|
|
14
17
|
*/
|
|
15
18
|
|
|
@@ -118,6 +121,26 @@ function getTargets({ nodePath, scriptPath }) {
|
|
|
118
121
|
entry: stdioEntry,
|
|
119
122
|
wrapKey: null, // VS Code uses { servers: { ... } } at top level
|
|
120
123
|
},
|
|
124
|
+
{
|
|
125
|
+
name: 'OpenCode',
|
|
126
|
+
path: path.join(home, '.config', 'opencode', 'opencode.json'),
|
|
127
|
+
key: 'mcp',
|
|
128
|
+
entry: scriptPath
|
|
129
|
+
? { type: 'local', command: [nodePath, scriptPath, 'start'], enabled: true }
|
|
130
|
+
: { type: 'local', command: [resolveNpxPath(), '-y', '@createlex/figma-swiftui-mcp', 'start'], enabled: true },
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'Codex CLI',
|
|
134
|
+
path: path.join(home, '.codex', 'config.toml'),
|
|
135
|
+
format: 'toml',
|
|
136
|
+
entry: { command: stdioEntry.command, args: stdioEntry.args },
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'Gemini CLI',
|
|
140
|
+
path: path.join(home, '.gemini', 'settings.json'),
|
|
141
|
+
key: 'mcpServers',
|
|
142
|
+
entry: stdioEntry,
|
|
143
|
+
},
|
|
121
144
|
{
|
|
122
145
|
name: 'Antigravity (Gemini)',
|
|
123
146
|
path: path.join(home, '.gemini', 'antigravity', 'mcp_config.json'),
|
|
@@ -145,6 +168,48 @@ function writeJsonSafe(filePath, data) {
|
|
|
145
168
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
|
|
146
169
|
}
|
|
147
170
|
|
|
171
|
+
// ── TOML helpers (minimal, for Codex config.toml) ────────────────────
|
|
172
|
+
// We only need to append/check a [mcp_servers.<name>] section — no full
|
|
173
|
+
// TOML parser required.
|
|
174
|
+
|
|
175
|
+
function tomlHasServer(raw, serverName) {
|
|
176
|
+
const pattern = new RegExp(`^\\[mcp_servers\\.${serverName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\]`, 'm');
|
|
177
|
+
return pattern.test(raw);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function tomlFormatValue(v) {
|
|
181
|
+
if (typeof v === 'string') return `"${v.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
182
|
+
if (typeof v === 'boolean') return v ? 'true' : 'false';
|
|
183
|
+
if (typeof v === 'number') return String(v);
|
|
184
|
+
if (Array.isArray(v)) return `[${v.map(tomlFormatValue).join(', ')}]`;
|
|
185
|
+
return `"${v}"`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildTomlSection(serverName, entry) {
|
|
189
|
+
const lines = [`[mcp_servers.${serverName}]`];
|
|
190
|
+
for (const [k, v] of Object.entries(entry)) {
|
|
191
|
+
if (k === 'env' && typeof v === 'object') continue; // handle env as sub-table
|
|
192
|
+
lines.push(`${k} = ${tomlFormatValue(v)}`);
|
|
193
|
+
}
|
|
194
|
+
if (entry.env && Object.keys(entry.env).length > 0) {
|
|
195
|
+
lines.push(`[mcp_servers.${serverName}.env]`);
|
|
196
|
+
for (const [ek, ev] of Object.entries(entry.env)) {
|
|
197
|
+
lines.push(`${ek} = ${tomlFormatValue(ev)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return lines.join('\n') + '\n';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function appendTomlServer(filePath, serverName, entry, dryRun) {
|
|
204
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
205
|
+
if (tomlHasServer(raw, serverName)) return false; // already present
|
|
206
|
+
const section = '\n' + buildTomlSection(serverName, entry);
|
|
207
|
+
if (!dryRun) {
|
|
208
|
+
fs.appendFileSync(filePath, section, 'utf-8');
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
148
213
|
// ── Main ──────────────────────────────────────────────────────────────
|
|
149
214
|
function runSetup(flags = {}) {
|
|
150
215
|
const force = flags.force || false;
|
|
@@ -177,6 +242,25 @@ function runSetup(flags = {}) {
|
|
|
177
242
|
continue;
|
|
178
243
|
}
|
|
179
244
|
|
|
245
|
+
// ── TOML targets (Codex) ──────────────────────────────────────
|
|
246
|
+
if (target.format === 'toml') {
|
|
247
|
+
const raw = fs.readFileSync(target.path, 'utf-8');
|
|
248
|
+
if (tomlHasServer(raw, MCP_KEY) && !force) {
|
|
249
|
+
console.log(` ✅ ${target.name} — already configured`);
|
|
250
|
+
skipped++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (dryRun) {
|
|
254
|
+
console.log(` 🟡 ${target.name} — would configure (dry run)`);
|
|
255
|
+
} else {
|
|
256
|
+
appendTomlServer(target.path, MCP_KEY, target.entry, false);
|
|
257
|
+
console.log(` ✅ ${target.name} — configured!`);
|
|
258
|
+
}
|
|
259
|
+
configured++;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── JSON targets ────────────────────────────────────────────
|
|
180
264
|
const config = readJsonSafe(target.path);
|
|
181
265
|
if (!config) {
|
|
182
266
|
console.log(` ⚠️ ${target.name} — could not parse config`);
|
package/package.json
CHANGED