@createlex/figma-swiftui-mcp 1.2.7 → 1.2.9

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.
@@ -115,7 +115,25 @@ function startBridgeServer(options = {}) {
115
115
  app.use(cors({ origin: '*' }));
116
116
  app.use(express.json({ limit: '50mb' }));
117
117
 
118
+ // Auth state exposed to the plugin UI via /ping
119
+ let authRequired = false;
120
+ let authRequiredReason = null;
121
+
122
+ function setAuthRequired(reason) {
123
+ authRequired = true;
124
+ authRequiredReason = reason || 'Token expired';
125
+ logger.warn(`[figma-swiftui-bridge] Auth required: ${authRequiredReason}`);
126
+ }
127
+
118
128
  app.get('/ping', (req, res) => {
129
+ if (authRequired) {
130
+ return res.status(401).json({
131
+ ok: false,
132
+ authRequired: true,
133
+ authRequiredReason,
134
+ loginCommand: 'npx @createlex/figma-swiftui-mcp login',
135
+ });
136
+ }
119
137
  res.json({
120
138
  ok: true,
121
139
  projectPath: projectPath || null,
@@ -124,6 +142,13 @@ function startBridgeServer(options = {}) {
124
142
  });
125
143
  });
126
144
 
145
+ // Graceful shutdown — called by the plugin UI "Disconnect MCP" to stop the runtime
146
+ app.post('/shutdown', (req, res) => {
147
+ res.json({ ok: true, message: 'Shutting down bridge server…' });
148
+ logger.info('[figma-swiftui-bridge] Shutdown requested by plugin UI');
149
+ setTimeout(() => process.exit(0), 400);
150
+ });
151
+
127
152
  app.get('/bridge/info', (req, res) => {
128
153
  res.json({
129
154
  ok: true,
@@ -682,6 +707,7 @@ function startBridgeServer(options = {}) {
682
707
  host,
683
708
  alreadyRunning: false,
684
709
  getProjectPath: () => projectPath,
710
+ setAuthRequired,
685
711
  getBridgeInfo: () => ({
686
712
  protocolVersion: BRIDGE_PROTOCOL_VERSION,
687
713
  pluginConnected: !!pluginBridgeClient,
@@ -727,6 +753,7 @@ function startBridgeServer(options = {}) {
727
753
  host,
728
754
  alreadyRunning: false,
729
755
  getProjectPath: () => projectPath,
756
+ setAuthRequired,
730
757
  getBridgeInfo: () => ({
731
758
  protocolVersion: BRIDGE_PROTOCOL_VERSION,
732
759
  pluginConnected: !!pluginBridgeClient,
@@ -37,6 +37,7 @@ RULES:
37
37
  11. If reusableComponents are present, output each as a separate <file name="ComponentName.swift"> tag.
38
38
  12. FONTS: Always use .font(.system(size: X, weight: .bold)) — NEVER reference custom font names like "Inter-Bold", "Roboto", or any fontName from Figma. Custom fonts are not bundled in the Xcode project and will cause runtime errors. System font only.
39
39
  13. IMAGES: Every node listed in assetExportPlan MUST be referenced as Image("assetName").resizable().scaledToFill() — NEVER replace with Rectangle(), Color, or shapes. These PNGs are pre-exported to Assets.xcassets.
40
+ 14. BLEND MODES: If an assetExportPlan candidate has a non-null blendModeSwiftUI value, you MUST append that modifier to the Image() call, e.g.: Image("Mockup_GroupView2").resizable().scaledToFit().frame(...).blendMode(.multiply). Without this, the image background will not blend with the dark frame background and will show as an opaque rectangle. Map: MULTIPLY→.multiply, SCREEN→.screen, OVERLAY→.overlay, DARKEN→.darken, LIGHTEN→.lighten, COLOR_DODGE→.colorDodge, COLOR_BURN→.colorBurn, HARD_LIGHT→.hardLight, SOFT_LIGHT→.softLight, DIFFERENCE→.difference, EXCLUSION→.exclusion.
40
41
 
41
42
  RESPONSIVE LAYOUT RULES:
42
43
  - Root frame with FILL sizing → .frame(maxWidth: .infinity)
@@ -30,12 +30,13 @@ async function main() {
30
30
  const validation = await validateRuntimeSession(authState);
31
31
  if (!validation.valid) {
32
32
  console.error(`[figma-swiftui-bridge] Authorization lost: ${validation.error}`);
33
- if (bridgeRuntime && typeof bridgeRuntime.close === 'function' && !bridgeRuntime.alreadyRunning) {
34
- await bridgeRuntime.close().catch((error) => {
35
- console.error('[figma-swiftui-bridge] Failed to close bridge cleanly:', error.message);
36
- });
33
+ // Signal the bridge to return authRequired from /ping so the plugin UI
34
+ // can show a "Login required" panel instead of just "runtime off".
35
+ if (bridgeRuntime && typeof bridgeRuntime.setAuthRequired === 'function') {
36
+ bridgeRuntime.setAuthRequired(validation.error || 'Token expired. Run: npx @createlex/figma-swiftui-mcp login');
37
+ } else {
38
+ process.exit(1);
37
39
  }
38
- process.exit(1);
39
40
  return;
40
41
  }
41
42
 
@@ -44,13 +45,13 @@ async function main() {
44
45
  console.log('[figma-swiftui-bridge] Refreshed CreateLex MCP authorization');
45
46
  }
46
47
  } catch (error) {
47
- console.error(`[figma-swiftui-bridge] Authorization revalidation failed: ${error instanceof Error ? error.message : 'unknown_error'}`);
48
- if (bridgeRuntime && typeof bridgeRuntime.close === 'function' && !bridgeRuntime.alreadyRunning) {
49
- await bridgeRuntime.close().catch((closeError) => {
50
- console.error('[figma-swiftui-bridge] Failed to close bridge cleanly:', closeError.message);
51
- });
48
+ const reason = error instanceof Error ? error.message : 'unknown_error';
49
+ console.error(`[figma-swiftui-bridge] Authorization revalidation failed: ${reason}`);
50
+ if (bridgeRuntime && typeof bridgeRuntime.setAuthRequired === 'function') {
51
+ bridgeRuntime.setAuthRequired(`Session revalidation failed: ${reason}`);
52
+ } else {
53
+ process.exit(1);
52
54
  }
53
- process.exit(1);
54
55
  }
55
56
  }, AUTH_REVALIDATION_INTERVAL_MS);
56
57
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@createlex/figma-swiftui-mcp",
3
- "version": "1.2.7",
3
+ "version": "1.2.9",
4
4
  "description": "CreateLex MCP runtime for Figma-to-SwiftUI generation and Xcode export",
5
5
  "bin": {
6
6
  "figma-swiftui-mcp": "bin/figma-swiftui-mcp.js"