faux-studio 0.3.8 → 0.3.10
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 +162 -151
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2267,7 +2267,7 @@ var require_websocket = __commonJS({
|
|
|
2267
2267
|
var protocolVersions = [8, 13];
|
|
2268
2268
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
2269
2269
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
2270
|
-
var
|
|
2270
|
+
var WebSocket2 = class _WebSocket extends EventEmitter {
|
|
2271
2271
|
/**
|
|
2272
2272
|
* Create a new `WebSocket`.
|
|
2273
2273
|
*
|
|
@@ -2637,35 +2637,35 @@ var require_websocket = __commonJS({
|
|
|
2637
2637
|
}
|
|
2638
2638
|
}
|
|
2639
2639
|
};
|
|
2640
|
-
Object.defineProperty(
|
|
2640
|
+
Object.defineProperty(WebSocket2, "CONNECTING", {
|
|
2641
2641
|
enumerable: true,
|
|
2642
2642
|
value: readyStates.indexOf("CONNECTING")
|
|
2643
2643
|
});
|
|
2644
|
-
Object.defineProperty(
|
|
2644
|
+
Object.defineProperty(WebSocket2.prototype, "CONNECTING", {
|
|
2645
2645
|
enumerable: true,
|
|
2646
2646
|
value: readyStates.indexOf("CONNECTING")
|
|
2647
2647
|
});
|
|
2648
|
-
Object.defineProperty(
|
|
2648
|
+
Object.defineProperty(WebSocket2, "OPEN", {
|
|
2649
2649
|
enumerable: true,
|
|
2650
2650
|
value: readyStates.indexOf("OPEN")
|
|
2651
2651
|
});
|
|
2652
|
-
Object.defineProperty(
|
|
2652
|
+
Object.defineProperty(WebSocket2.prototype, "OPEN", {
|
|
2653
2653
|
enumerable: true,
|
|
2654
2654
|
value: readyStates.indexOf("OPEN")
|
|
2655
2655
|
});
|
|
2656
|
-
Object.defineProperty(
|
|
2656
|
+
Object.defineProperty(WebSocket2, "CLOSING", {
|
|
2657
2657
|
enumerable: true,
|
|
2658
2658
|
value: readyStates.indexOf("CLOSING")
|
|
2659
2659
|
});
|
|
2660
|
-
Object.defineProperty(
|
|
2660
|
+
Object.defineProperty(WebSocket2.prototype, "CLOSING", {
|
|
2661
2661
|
enumerable: true,
|
|
2662
2662
|
value: readyStates.indexOf("CLOSING")
|
|
2663
2663
|
});
|
|
2664
|
-
Object.defineProperty(
|
|
2664
|
+
Object.defineProperty(WebSocket2, "CLOSED", {
|
|
2665
2665
|
enumerable: true,
|
|
2666
2666
|
value: readyStates.indexOf("CLOSED")
|
|
2667
2667
|
});
|
|
2668
|
-
Object.defineProperty(
|
|
2668
|
+
Object.defineProperty(WebSocket2.prototype, "CLOSED", {
|
|
2669
2669
|
enumerable: true,
|
|
2670
2670
|
value: readyStates.indexOf("CLOSED")
|
|
2671
2671
|
});
|
|
@@ -2678,10 +2678,10 @@ var require_websocket = __commonJS({
|
|
|
2678
2678
|
"readyState",
|
|
2679
2679
|
"url"
|
|
2680
2680
|
].forEach((property) => {
|
|
2681
|
-
Object.defineProperty(
|
|
2681
|
+
Object.defineProperty(WebSocket2.prototype, property, { enumerable: true });
|
|
2682
2682
|
});
|
|
2683
2683
|
["open", "error", "close", "message"].forEach((method) => {
|
|
2684
|
-
Object.defineProperty(
|
|
2684
|
+
Object.defineProperty(WebSocket2.prototype, `on${method}`, {
|
|
2685
2685
|
enumerable: true,
|
|
2686
2686
|
get() {
|
|
2687
2687
|
for (const listener of this.listeners(method)) {
|
|
@@ -2703,9 +2703,9 @@ var require_websocket = __commonJS({
|
|
|
2703
2703
|
}
|
|
2704
2704
|
});
|
|
2705
2705
|
});
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
module.exports =
|
|
2706
|
+
WebSocket2.prototype.addEventListener = addEventListener;
|
|
2707
|
+
WebSocket2.prototype.removeEventListener = removeEventListener;
|
|
2708
|
+
module.exports = WebSocket2;
|
|
2709
2709
|
function initAsClient(websocket, address, protocols, options) {
|
|
2710
2710
|
const opts = {
|
|
2711
2711
|
allowSynchronousEvents: true,
|
|
@@ -2893,7 +2893,7 @@ var require_websocket = __commonJS({
|
|
|
2893
2893
|
});
|
|
2894
2894
|
req.on("upgrade", (res, socket, head) => {
|
|
2895
2895
|
websocket.emit("upgrade", res);
|
|
2896
|
-
if (websocket.readyState !==
|
|
2896
|
+
if (websocket.readyState !== WebSocket2.CONNECTING) return;
|
|
2897
2897
|
req = websocket._req = null;
|
|
2898
2898
|
const upgrade = res.headers.upgrade;
|
|
2899
2899
|
if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
|
|
@@ -2965,7 +2965,7 @@ var require_websocket = __commonJS({
|
|
|
2965
2965
|
}
|
|
2966
2966
|
}
|
|
2967
2967
|
function emitErrorAndClose(websocket, err) {
|
|
2968
|
-
websocket._readyState =
|
|
2968
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
2969
2969
|
websocket._errorEmitted = true;
|
|
2970
2970
|
websocket.emit("error", err);
|
|
2971
2971
|
websocket.emitClose();
|
|
@@ -2982,7 +2982,7 @@ var require_websocket = __commonJS({
|
|
|
2982
2982
|
return tls.connect(options);
|
|
2983
2983
|
}
|
|
2984
2984
|
function abortHandshake(websocket, stream, message) {
|
|
2985
|
-
websocket._readyState =
|
|
2985
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
2986
2986
|
const err = new Error(message);
|
|
2987
2987
|
Error.captureStackTrace(err, abortHandshake);
|
|
2988
2988
|
if (stream.setHeader) {
|
|
@@ -3057,9 +3057,9 @@ var require_websocket = __commonJS({
|
|
|
3057
3057
|
}
|
|
3058
3058
|
function senderOnError(err) {
|
|
3059
3059
|
const websocket = this[kWebSocket];
|
|
3060
|
-
if (websocket.readyState ===
|
|
3061
|
-
if (websocket.readyState ===
|
|
3062
|
-
websocket._readyState =
|
|
3060
|
+
if (websocket.readyState === WebSocket2.CLOSED) return;
|
|
3061
|
+
if (websocket.readyState === WebSocket2.OPEN) {
|
|
3062
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
3063
3063
|
setCloseTimer(websocket);
|
|
3064
3064
|
}
|
|
3065
3065
|
this._socket.end();
|
|
@@ -3079,7 +3079,7 @@ var require_websocket = __commonJS({
|
|
|
3079
3079
|
this.removeListener("close", socketOnClose);
|
|
3080
3080
|
this.removeListener("data", socketOnData);
|
|
3081
3081
|
this.removeListener("end", socketOnEnd);
|
|
3082
|
-
websocket._readyState =
|
|
3082
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
3083
3083
|
if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
|
|
3084
3084
|
const chunk = this.read(this._readableState.length);
|
|
3085
3085
|
websocket._receiver.write(chunk);
|
|
@@ -3101,7 +3101,7 @@ var require_websocket = __commonJS({
|
|
|
3101
3101
|
}
|
|
3102
3102
|
function socketOnEnd() {
|
|
3103
3103
|
const websocket = this[kWebSocket];
|
|
3104
|
-
websocket._readyState =
|
|
3104
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
3105
3105
|
websocket._receiver.end();
|
|
3106
3106
|
this.end();
|
|
3107
3107
|
}
|
|
@@ -3110,7 +3110,7 @@ var require_websocket = __commonJS({
|
|
|
3110
3110
|
this.removeListener("error", socketOnError);
|
|
3111
3111
|
this.on("error", NOOP);
|
|
3112
3112
|
if (websocket) {
|
|
3113
|
-
websocket._readyState =
|
|
3113
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
3114
3114
|
this.destroy();
|
|
3115
3115
|
}
|
|
3116
3116
|
}
|
|
@@ -3121,7 +3121,7 @@ var require_websocket = __commonJS({
|
|
|
3121
3121
|
var require_stream = __commonJS({
|
|
3122
3122
|
"node_modules/ws/lib/stream.js"(exports, module) {
|
|
3123
3123
|
"use strict";
|
|
3124
|
-
var
|
|
3124
|
+
var WebSocket2 = require_websocket();
|
|
3125
3125
|
var { Duplex } = __require("stream");
|
|
3126
3126
|
function emitClose(stream) {
|
|
3127
3127
|
stream.emit("close");
|
|
@@ -3271,7 +3271,7 @@ var require_websocket_server = __commonJS({
|
|
|
3271
3271
|
var extension = require_extension();
|
|
3272
3272
|
var PerMessageDeflate = require_permessage_deflate();
|
|
3273
3273
|
var subprotocol = require_subprotocol();
|
|
3274
|
-
var
|
|
3274
|
+
var WebSocket2 = require_websocket();
|
|
3275
3275
|
var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
|
|
3276
3276
|
var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
|
3277
3277
|
var RUNNING = 0;
|
|
@@ -3331,7 +3331,7 @@ var require_websocket_server = __commonJS({
|
|
|
3331
3331
|
host: null,
|
|
3332
3332
|
path: null,
|
|
3333
3333
|
port: null,
|
|
3334
|
-
WebSocket:
|
|
3334
|
+
WebSocket: WebSocket2,
|
|
3335
3335
|
...options
|
|
3336
3336
|
};
|
|
3337
3337
|
if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) {
|
|
@@ -11071,56 +11071,35 @@ async function probeCdpPorts() {
|
|
|
11071
11071
|
}
|
|
11072
11072
|
return null;
|
|
11073
11073
|
}
|
|
11074
|
-
function
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
} catch {
|
|
11087
|
-
return false;
|
|
11074
|
+
function launchFigmaProcess(figmaPath, port) {
|
|
11075
|
+
if (process.platform === "darwin") {
|
|
11076
|
+
const binary = `${figmaPath}/Contents/MacOS/Figma`;
|
|
11077
|
+
spawn2(binary, [`--remote-debugging-port=${port}`], {
|
|
11078
|
+
detached: true,
|
|
11079
|
+
stdio: "ignore"
|
|
11080
|
+
}).unref();
|
|
11081
|
+
} else {
|
|
11082
|
+
spawn2(figmaPath, [`--remote-debugging-port=${port}`], {
|
|
11083
|
+
detached: true,
|
|
11084
|
+
stdio: "ignore"
|
|
11085
|
+
}).unref();
|
|
11088
11086
|
}
|
|
11089
11087
|
}
|
|
11090
|
-
|
|
11088
|
+
function launchFigma() {
|
|
11091
11089
|
const figmaPath = findFigmaPath();
|
|
11092
11090
|
if (!figmaPath) {
|
|
11093
11091
|
throw new Error(
|
|
11094
11092
|
"Figma Desktop is not installed. Download from https://figma.com/downloads"
|
|
11095
11093
|
);
|
|
11096
11094
|
}
|
|
11097
|
-
|
|
11098
|
-
log(`Launching Figma Desktop (port ${port})...`);
|
|
11099
|
-
launchFigmaProcess(figmaPath, port);
|
|
11100
|
-
const startTime = Date.now();
|
|
11101
|
-
while (Date.now() - startTime < CDP_WAIT_TIMEOUT_MS) {
|
|
11102
|
-
await new Promise((r) => setTimeout(r, CDP_POLL_INTERVAL_MS));
|
|
11103
|
-
const { alive, isFigma } = await isCdpAlive(port);
|
|
11104
|
-
if (alive && isFigma) {
|
|
11105
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
11106
|
-
const targets = await listTargets(port);
|
|
11107
|
-
log("Figma Desktop started");
|
|
11108
|
-
return { port, targets };
|
|
11109
|
-
}
|
|
11110
|
-
}
|
|
11111
|
-
throw new Error(
|
|
11112
|
-
`Figma did not respond on port ${port} within ${CDP_WAIT_TIMEOUT_MS / 1e3}s. Try again.`
|
|
11113
|
-
);
|
|
11114
|
-
}
|
|
11115
|
-
function launchFigmaProcess(figmaPath, port) {
|
|
11095
|
+
log("Launching Figma Desktop...");
|
|
11116
11096
|
if (process.platform === "darwin") {
|
|
11117
|
-
|
|
11118
|
-
spawn2(binary, [`--remote-debugging-port=${port}`], {
|
|
11097
|
+
spawn2("open", ["-a", figmaPath], {
|
|
11119
11098
|
detached: true,
|
|
11120
11099
|
stdio: "ignore"
|
|
11121
11100
|
}).unref();
|
|
11122
11101
|
} else {
|
|
11123
|
-
spawn2(figmaPath, [
|
|
11102
|
+
spawn2(figmaPath, [], {
|
|
11124
11103
|
detached: true,
|
|
11125
11104
|
stdio: "ignore"
|
|
11126
11105
|
}).unref();
|
|
@@ -11212,6 +11191,7 @@ var PluginWsServer = class {
|
|
|
11212
11191
|
// Server Lifecycle
|
|
11213
11192
|
// -------------------------------------------------------------------------
|
|
11214
11193
|
async start() {
|
|
11194
|
+
await this.shutdownExisting();
|
|
11215
11195
|
for (let port = DEFAULT_PORT; port < DEFAULT_PORT + PORT_RANGE; port++) {
|
|
11216
11196
|
try {
|
|
11217
11197
|
await this.listen(port);
|
|
@@ -11227,6 +11207,53 @@ var PluginWsServer = class {
|
|
|
11227
11207
|
`No available port in range ${DEFAULT_PORT}-${DEFAULT_PORT + PORT_RANGE - 1}. Another faux-studio instance may be running.`
|
|
11228
11208
|
);
|
|
11229
11209
|
}
|
|
11210
|
+
/**
|
|
11211
|
+
* Connect to any existing faux-studio WS servers and tell them to shut down
|
|
11212
|
+
* their listener. This ensures the plugin reconnects to the newest instance.
|
|
11213
|
+
*/
|
|
11214
|
+
async shutdownExisting() {
|
|
11215
|
+
const promises = [];
|
|
11216
|
+
for (let port = DEFAULT_PORT; port < DEFAULT_PORT + PORT_RANGE; port++) {
|
|
11217
|
+
promises.push(this.sendShutdown(port));
|
|
11218
|
+
}
|
|
11219
|
+
await Promise.allSettled(promises);
|
|
11220
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
11221
|
+
}
|
|
11222
|
+
sendShutdown(port) {
|
|
11223
|
+
return new Promise((resolve) => {
|
|
11224
|
+
try {
|
|
11225
|
+
const ws = new import_websocket.default(`ws://127.0.0.1:${port}`);
|
|
11226
|
+
const timer = setTimeout(() => {
|
|
11227
|
+
try {
|
|
11228
|
+
ws.close();
|
|
11229
|
+
} catch {
|
|
11230
|
+
}
|
|
11231
|
+
resolve();
|
|
11232
|
+
}, 1e3);
|
|
11233
|
+
ws.on("open", () => {
|
|
11234
|
+
ws.send(JSON.stringify({ type: "shutdown" }));
|
|
11235
|
+
clearTimeout(timer);
|
|
11236
|
+
setTimeout(() => {
|
|
11237
|
+
try {
|
|
11238
|
+
ws.close();
|
|
11239
|
+
} catch {
|
|
11240
|
+
}
|
|
11241
|
+
resolve();
|
|
11242
|
+
}, 200);
|
|
11243
|
+
});
|
|
11244
|
+
ws.on("error", () => {
|
|
11245
|
+
clearTimeout(timer);
|
|
11246
|
+
resolve();
|
|
11247
|
+
});
|
|
11248
|
+
ws.on("close", () => {
|
|
11249
|
+
clearTimeout(timer);
|
|
11250
|
+
resolve();
|
|
11251
|
+
});
|
|
11252
|
+
} catch {
|
|
11253
|
+
resolve();
|
|
11254
|
+
}
|
|
11255
|
+
});
|
|
11256
|
+
}
|
|
11230
11257
|
listen(port) {
|
|
11231
11258
|
return new Promise((resolve, reject) => {
|
|
11232
11259
|
const wss = new import_websocket_server.default({ port, host: "127.0.0.1" });
|
|
@@ -11287,6 +11314,13 @@ var PluginWsServer = class {
|
|
|
11287
11314
|
} catch {
|
|
11288
11315
|
return;
|
|
11289
11316
|
}
|
|
11317
|
+
if (!identified && msg.type === "shutdown") {
|
|
11318
|
+
clearTimeout(handshakeTimeout);
|
|
11319
|
+
ws.close(1e3, "Shutdown requested");
|
|
11320
|
+
log("Shutdown requested by newer faux-studio instance \u2014 releasing port");
|
|
11321
|
+
this.close();
|
|
11322
|
+
return;
|
|
11323
|
+
}
|
|
11290
11324
|
if (!identified && msg.type === "handshake") {
|
|
11291
11325
|
clearTimeout(handshakeTimeout);
|
|
11292
11326
|
identified = true;
|
|
@@ -25591,7 +25625,7 @@ Resources provide quick read-only access to Figma state without tool calls:
|
|
|
25591
25625
|
- Create components for reusable UI patterns.`;
|
|
25592
25626
|
function createMcpServer(deps) {
|
|
25593
25627
|
const server2 = new Server(
|
|
25594
|
-
{ name: "faux-studio", version: "0.3.
|
|
25628
|
+
{ name: "faux-studio", version: "0.3.10" },
|
|
25595
25629
|
{
|
|
25596
25630
|
capabilities: { tools: { listChanged: true }, resources: {}, logging: {} },
|
|
25597
25631
|
instructions: INSTRUCTIONS
|
|
@@ -25606,15 +25640,10 @@ function createMcpServer(deps) {
|
|
|
25606
25640
|
},
|
|
25607
25641
|
{
|
|
25608
25642
|
name: "setup_figma",
|
|
25609
|
-
description: "Ensure Figma Desktop is running and connected. Call this before any design work.
|
|
25643
|
+
description: "Ensure Figma Desktop is running and the plugin is connected. Call this before any design work. Launches Figma if needed, waits for the plugin to connect, and provides setup guidance. Idempotent \u2014 safe to call multiple times. Returns connection status and active file info.",
|
|
25610
25644
|
inputSchema: {
|
|
25611
25645
|
type: "object",
|
|
25612
|
-
properties: {
|
|
25613
|
-
force_restart: {
|
|
25614
|
-
type: "boolean",
|
|
25615
|
-
description: "Restart Figma even if it is already running (e.g., to enable the debug port). Figma auto-recovers open files on restart. Only set this if instructed or if the previous call returned needs_restart."
|
|
25616
|
-
}
|
|
25617
|
-
},
|
|
25646
|
+
properties: {},
|
|
25618
25647
|
required: []
|
|
25619
25648
|
}
|
|
25620
25649
|
}
|
|
@@ -25674,9 +25703,7 @@ function createMcpServer(deps) {
|
|
|
25674
25703
|
}
|
|
25675
25704
|
if (name === "setup_figma") {
|
|
25676
25705
|
log("setup_figma called");
|
|
25677
|
-
const result2 = await deps.setupFigma(
|
|
25678
|
-
force_restart: params.force_restart === true
|
|
25679
|
-
});
|
|
25706
|
+
const result2 = await deps.setupFigma(params);
|
|
25680
25707
|
return {
|
|
25681
25708
|
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }],
|
|
25682
25709
|
isError: result2.status === "not_installed"
|
|
@@ -25842,16 +25869,31 @@ async function generateWithAuth(toolName, params) {
|
|
|
25842
25869
|
throw err;
|
|
25843
25870
|
}
|
|
25844
25871
|
}
|
|
25872
|
+
var PLUGIN_URL = "https://faux.design/plugin";
|
|
25873
|
+
var PLUGIN_WAIT_MS = 1e4;
|
|
25874
|
+
var PLUGIN_POLL_MS = 1e3;
|
|
25875
|
+
async function waitForPlugin() {
|
|
25876
|
+
if (pluginServer.hasConnections) return true;
|
|
25877
|
+
const start = Date.now();
|
|
25878
|
+
while (Date.now() - start < PLUGIN_WAIT_MS) {
|
|
25879
|
+
await new Promise((r) => setTimeout(r, PLUGIN_POLL_MS));
|
|
25880
|
+
if (pluginServer.hasConnections) return true;
|
|
25881
|
+
}
|
|
25882
|
+
return false;
|
|
25883
|
+
}
|
|
25884
|
+
function pluginReadyResult() {
|
|
25885
|
+
return {
|
|
25886
|
+
status: "ready",
|
|
25887
|
+
transport: "plugin",
|
|
25888
|
+
message: `Connected via plugin.${pluginServer.activeFileId ? ` Active file: ${pluginServer.activeFileId}` : ""} Ready to design.`,
|
|
25889
|
+
activeFile: pluginServer.activeFileId || void 0,
|
|
25890
|
+
pluginFiles: pluginServer.connectedFiles,
|
|
25891
|
+
port: pluginServer.port
|
|
25892
|
+
};
|
|
25893
|
+
}
|
|
25845
25894
|
async function setupFigma(params) {
|
|
25846
|
-
if (
|
|
25847
|
-
return
|
|
25848
|
-
status: "ready",
|
|
25849
|
-
transport: "plugin",
|
|
25850
|
-
message: `Connected via plugin.${pluginServer.activeFileId ? ` Active file: ${pluginServer.activeFileId}` : ""} Ready to design.`,
|
|
25851
|
-
activeFile: pluginServer.activeFileId || void 0,
|
|
25852
|
-
pluginFiles: pluginServer.connectedFiles,
|
|
25853
|
-
port: pluginServer.port
|
|
25854
|
-
};
|
|
25895
|
+
if (pluginServer.hasConnections) {
|
|
25896
|
+
return pluginReadyResult();
|
|
25855
25897
|
}
|
|
25856
25898
|
if (forceTransport !== "plugin" && cdpClient?.connected && cdpClient.hasContext) {
|
|
25857
25899
|
return {
|
|
@@ -25883,86 +25925,55 @@ async function setupFigma(params) {
|
|
|
25883
25925
|
} catch {
|
|
25884
25926
|
}
|
|
25885
25927
|
}
|
|
25886
|
-
return {
|
|
25887
|
-
status: "no_design_file",
|
|
25888
|
-
transport: "none",
|
|
25889
|
-
message: "Figma is running with the debug port, but no design file is open. Ask the user to open a .fig file in Figma, then call setup_figma again.",
|
|
25890
|
-
port: existing.port
|
|
25891
|
-
};
|
|
25892
25928
|
}
|
|
25893
25929
|
}
|
|
25894
|
-
if (isFigmaRunning()) {
|
|
25895
|
-
if (params.force_restart) {
|
|
25896
|
-
log("Restarting Figma with debug port...");
|
|
25897
|
-
killFigma();
|
|
25898
|
-
await new Promise((r) => setTimeout(r, 3e3));
|
|
25899
|
-
try {
|
|
25900
|
-
const conn = await launchFigmaWithCdp();
|
|
25901
|
-
const target = findFigmaDesignTarget(conn.targets);
|
|
25902
|
-
if (target) {
|
|
25903
|
-
try {
|
|
25904
|
-
const client = new CdpClient();
|
|
25905
|
-
await client.connect(target.webSocketDebuggerUrl);
|
|
25906
|
-
await client.discoverFigmaContext();
|
|
25907
|
-
cdpClient = client;
|
|
25908
|
-
} catch {
|
|
25909
|
-
}
|
|
25910
|
-
}
|
|
25911
|
-
return {
|
|
25912
|
-
status: "restarted",
|
|
25913
|
-
transport: target ? "cdp" : "none",
|
|
25914
|
-
message: target ? `Figma restarted with debug port. Connected to: ${target.title}. Ready to design.` : "Figma restarted with debug port. Waiting for a design file to be opened. Call setup_figma again once a file is open.",
|
|
25915
|
-
activeFile: target?.title,
|
|
25916
|
-
port: conn.port
|
|
25917
|
-
};
|
|
25918
|
-
} catch (err) {
|
|
25919
|
-
return {
|
|
25920
|
-
status: "not_installed",
|
|
25921
|
-
transport: "none",
|
|
25922
|
-
message: err instanceof Error ? err.message : "Failed to restart Figma."
|
|
25923
|
-
};
|
|
25924
|
-
}
|
|
25925
|
-
}
|
|
25926
|
-
return {
|
|
25927
|
-
status: "needs_restart",
|
|
25928
|
-
transport: "none",
|
|
25929
|
-
message: "Figma is running but the debug port is not enabled. Figma needs to restart with the debug port for AI tools to work. Figma auto-recovers all open files on restart, so no work will be lost. Ask the user for permission, then call setup_figma again with force_restart: true."
|
|
25930
|
-
};
|
|
25931
|
-
}
|
|
25932
25930
|
if (!findFigmaPath()) {
|
|
25933
25931
|
return {
|
|
25934
25932
|
status: "not_installed",
|
|
25935
25933
|
transport: "none",
|
|
25936
|
-
message:
|
|
25934
|
+
message: `Figma Desktop is not installed.
|
|
25935
|
+
|
|
25936
|
+
1. Download Figma: https://figma.com/downloads
|
|
25937
|
+
2. Install and open it
|
|
25938
|
+
3. Install the faux-studio plugin: ${PLUGIN_URL}
|
|
25939
|
+
4. Call setup_figma again`
|
|
25937
25940
|
};
|
|
25938
25941
|
}
|
|
25939
|
-
|
|
25940
|
-
|
|
25941
|
-
|
|
25942
|
-
|
|
25943
|
-
|
|
25944
|
-
|
|
25945
|
-
|
|
25946
|
-
|
|
25947
|
-
|
|
25948
|
-
cdpClient = client;
|
|
25949
|
-
} catch {
|
|
25950
|
-
}
|
|
25942
|
+
if (!isFigmaRunning()) {
|
|
25943
|
+
try {
|
|
25944
|
+
launchFigma();
|
|
25945
|
+
} catch (err) {
|
|
25946
|
+
return {
|
|
25947
|
+
status: "not_installed",
|
|
25948
|
+
transport: "none",
|
|
25949
|
+
message: err instanceof Error ? err.message : "Failed to launch Figma."
|
|
25950
|
+
};
|
|
25951
25951
|
}
|
|
25952
|
+
}
|
|
25953
|
+
log("Waiting for plugin connection...");
|
|
25954
|
+
const connected = await waitForPlugin();
|
|
25955
|
+
if (connected) {
|
|
25952
25956
|
return {
|
|
25953
|
-
|
|
25954
|
-
|
|
25955
|
-
message: target ? `Figma launched and connected. Active file: ${target.title}. Ready to design.` : "Figma launched. Open a design file to start designing, then call setup_figma again.",
|
|
25956
|
-
activeFile: target?.title,
|
|
25957
|
-
port: conn.port
|
|
25958
|
-
};
|
|
25959
|
-
} catch (err) {
|
|
25960
|
-
return {
|
|
25961
|
-
status: "not_installed",
|
|
25962
|
-
transport: "none",
|
|
25963
|
-
message: err instanceof Error ? err.message : "Failed to launch Figma."
|
|
25957
|
+
...pluginReadyResult(),
|
|
25958
|
+
status: isFigmaRunning() ? "ready" : "launched"
|
|
25964
25959
|
};
|
|
25965
25960
|
}
|
|
25961
|
+
return {
|
|
25962
|
+
status: "waiting_for_plugin",
|
|
25963
|
+
transport: "none",
|
|
25964
|
+
message: `Figma is running but the faux-studio plugin is not connected.
|
|
25965
|
+
|
|
25966
|
+
If this is your first time:
|
|
25967
|
+
1. Download the plugin: ${PLUGIN_URL}
|
|
25968
|
+
2. Unzip and import in Figma: Plugins \u2192 Development \u2192 Import plugin from manifest
|
|
25969
|
+
|
|
25970
|
+
Then run the plugin:
|
|
25971
|
+
3. Open a design file in Figma
|
|
25972
|
+
4. Plugins \u2192 Development \u2192 faux-studio
|
|
25973
|
+
|
|
25974
|
+
Call setup_figma again once the plugin shows "Ready".`,
|
|
25975
|
+
port: pluginServer.port
|
|
25976
|
+
};
|
|
25966
25977
|
}
|
|
25967
25978
|
async function main() {
|
|
25968
25979
|
try {
|