Figma Bridge
Figma 没有公开的写入 API。此技能使用一个本地桥接:通过 HTTP 命名操作,由插件在 Figma 内部通过 Figma 插件 API 执行。无需 OAuth,无需令牌。
仓库: https://github.com/iggypcnfsky/figma-bridge — AGENTS.md、docs/ops-reference.md。
前提条件——执行任何操作前请检查
figma 在 PATH 中,因此永远不要搜索文件系统来查找仓库:
figma doctor # 一次检查仓库、服务器、已连接窗口、img-forge + FAL 密钥
figma clients # 每个已连接 Figma 窗口的 id、文件、页面
figma rules # 项目约束:类型层级、网格、框架大小、版本命名
在设计之前阅读 figma rules。 它包含了非显而易见的限制——例如“Poppins 20/25 是基础”——这些信息仅通过阅读画布是无法获得的。将 "confirmed": false 视为需要与用户验证的假设,将 {} 视为“询问,不要自行发明”。
如果命令缺失,请在询问后使用 node "$(figma where)/server.mjs";不要 find 仓库,也不要猜测路径。
pluginConnected: false→ 停止。请用户在他们想要操作的文件窗口中运行Plugins → Development → Figma Bridge。重试无济于事。/run返回503表示同样的问题。504 PLUGIN_GONE表示他们关闭了插件窗口。409表示多个 Figma 窗口已连接且未选择任何一个。选择用户命名的文件(--file <NAME>)或客户端 ID(--client <ID>);永远不要盲目重试,否则可能会编辑错误的文件。
工作循环
- 定位.
./bin/figma status→ 文件名、当前页面、选区、所有页面。 - 读取.
selection 2、node <id> 2、find {"name":"Header","allPages":true}。 永远不要猜测nodeId——Figma ID 看起来像4022:2,并且无法推导。 - 规划. 决定父元素、大小、顺序。除非指定了
parentId,否则新节点将放到当前页面。 - 写入.
./bin/figma run '[{...},{...}]'。 - 验证.
./bin/figma export <id> out/check.png PNG 2并实际查看图像。
命令
./bin/figma op '{"op":"status"}'
./bin/figma run '[{"op":"createFrame","name":"Card","width":320,"height":200,"fill":"#0B0B0F",
"props":{"cornerRadius":16,"layoutMode":"VERTICAL","paddingTop":24,"paddingLeft":24,"itemSpacing":12}}]'
./bin/figma run '[{"op":"createText","parentId":"12:34","characters":"Title","fontSize":24,"color":"#FFFFFF"}]'
./bin/figma op '{"op":"varFill","nodeId":"12:34","variableName":"color/bg/surface"}'
./bin/figma export 12:34 out/card.png PNG 2
./bin/figma image 12:34 out/cover.png FILL # 本地图像文件 → 填充(无需云上传)
操作:ping status pages variables styles fonts inventory · selection node find · createFrame createRect createEllipse createText createPage createInstance · update clone reparent remove goto group ungroup select · grids setGrids align distribute effects · applyStyle varFill · export imageFill · exec。
完整参数:docs/ops-reference.md。
值得了解的设计工作快捷方式:
- grid 意味着 GRID:当用户说“设置网格”时,他们想要方形布局网格,而不是列:
{"op":"setGrids","nodeId":"…","grid":true}(默认 40 像素单元格,40% 的透明度)或"grid":40。 仅当用户提到列、12 列网格或给出间隔/边距值时,才使用columns/rows。 永远不要用矩形伪造网格;使用applyStyle配合kind:"grid"可应用已保存的网格样式。 - 行中的项目:先构建它们,然后使用
{"op":"align","nodeIds":[…],"mode":"TOP"}和{"op":"distribute","nodeIds":[…],"axis":"HORIZONTAL","gap":24}。 - 深度:
{"op":"effects","nodeId":"…","shadow":{"y":8,"blur":24,"color":"#00000033"}}或{"op":"effects","nodeId":"…","blur":{"type":"BACKGROUND","radius":20}}。 - 渐变:
{"op":"gradient","nodeId":"…","kind":"linear","angle":135,"stops":[[0,"#8B76FF"],[1,"#0E0E12"]]}— 角度 0 表示从左到右,90 表示从上到下。永远不要用堆叠的矩形伪造渐变。 - 设计系统:使用
COMPONENTID 调用createInstance,然后使用instanceProps设置变体(先读取名称)和instanceText更改文本而不解除实例链接;使用applyStyle应用样式。不要重绘库中已有的内容,也不要为了“修复”其文本而解除实例链接。 - 间距检查:
{"op":"measure","a":"1:2","b":"1:3"}返回每侧间隙和中心距离;用它代替导出屏幕截图来判断对齐情况。 - 样式:
saveStyle按名称创建或更新text/paint/effect/grid样式,applyStyle应用它。创建一次令牌,随处应用。 - 响应式:
sizing按轴设置HUG/FILL/FIXED以及最小/最大值;constraints覆盖非自动布局的子元素(STRETCH/MIN/MAX/CENTER/SCALE)。在更改它们之前,先从node读取两者——当某个属性不适用时,sizing会返回 Figma 的解释skipped。
图像
需要艺术作品?使用配套的 img-forge CLI (https://github.com/iggypcnfsky/img-forge) 生成,然后放置——生成过程保持在 Figma 之外,因此 API 密钥永远不会进入插件代码,并且缓慢的渲染不会阻塞画布:
cd ../img-forge
./img gen --prompt "abstract gradient cover, purple to black" --preset 16:9@2k --out out/cover.png
./img edit --prompt "make it night with neon accents" --ref out/cover.png # 或编辑现有资源
cd ../figma-bridge
./bin/figma image 12:34 ../img-forge/out/cover.png FILL
FILL 裁剪到节点,FIT 添加黑边,TILE 重复,CROP 尊重现有裁剪。
要编辑用户选中的内容,首先导出它(./bin/figma export <nodeId> out/sel.png PNG 2),然后将该文件作为 --ref 传递。
插件窗口还有一个供人类使用的生成/编辑面板(模型、尺寸预设、成本、插入)。不要以编程方式驱动面板——使用 CLI;它暴露了更多功能(蒙版、变体、试运行)。在该面板中保存的预设存放在 figma-bridge/presets.json 中。
在现有草稿上构建
一份“未完成”的草稿仍然编码了决策:什么放在什么旁边,什么被分组,元素的顺序,间距的韵律。在设计之前先读取它,然后保留它。
{"op":"inventory"}— 框架、它们的尺寸和排版统计信息。{"op":"structure","nodeId":"<frame>"}— 子元素按行分组,带有间隙和readingOrder;这就是作者选择的分组。{"op":"node",…,"depth":2}提供相同但更深层的树结构。- 注意模式(重复出现的左边距、始终位于三列上方的标题)并保留它。更改请求所需的内容,其余保持不变。
- 报告你所做的更改——例如“将页脚上移了 40 像素”,而不仅仅是“改进了布局”。
永远不要默默地规范化草稿。如果一致性需要移动用户放置的元素,请说明并询问;一个看起来整洁但丢失了他们分组的幻灯片是退步,而不是改进。
规则
- 先读后写。 至少两次调用:检查,然后修改。
- 一个批次 = 一个想法。 一个操作不能引用前一个操作的结果,因此如果你需要新节点的
id,请分成两次调用。 - 尊重你继承的布局:兄弟顺序、邻近性和分组都是意图。除非被要求,否则不要重新排列草稿使其看起来更整洁;如果一致性会移动他们的元素,请先询问。
- 未经明确指示,永远不要修改现有的用户图层(
update、remove、reparent)。在你自己的创建内容前添加前缀:Bridge / …。 - 重做版本应尽可能靠近原始版本。 如果用户要求重做幻灯片/屏幕/部分:复制它,调用
{"op":"placeNear","nodeId":"<new id>","move":true,"gap":80,"name":"Bridge / v2 — <name>"},使副本落在第一个空闲侧而不是猜测的坐标上,并保持原始版本不变。 如果工具报告free: false,询问它应该放在哪里。仅当用户明确要求时才原地替换元素。 goto会更改用户的视图——仅在用户请求时使用。exec是最后的手段。 它有效(Figma 的沙盒允许new Function)并且绕过所有参数验证。仅返回可 JSON 序列化的值。- 清理测试产物,或者询问是否保留它们。
陷阱
- 使用
localhost,永远不要使用127.0.0.1:插件清单仅将http://localhost:3847列入白名单(devAllowedDomains);Figma 会拒绝 IP。服务器同时在127.0.0.1和::1上侦听。 figma.currentPage是该 Figma 窗口中打开的页面——不是保存的指针。切换标签页会更改下次调用时看到的内容。- 位置是相对于父元素的,省略
parentId意味着当前页面。 - 排版问题(使用哪些字体、大小、计数)一次调用即可解决:
{"op":"fonts"};添加"allPages":true用于整个文件。不要用自己的脚本遍历框架。 - 幻灯片概览也是一次调用:
{"op":"inventory"}返回每个框架的几何信息、minFontSize、字体、网格计数和填充——足以选择“框架 11–19”或查找小于最小大小的文本。 - 永远不要索引参考文档未列出的字段。
summary为 TEXT 携带font、fontSize、lineHeight和letterSpacing;其他所有内容都来自使用真实 API 名称的exec。混合样式是字符串"MIXED",因此使用.get()和后备值进行保护。 - 矩形不能有子元素;当文本或图标必须位于形状内部时,请使用框架。
- 没有本地变量 →
variables返回collections: [],并且varFill会失败。请先检查。 - 文本需要字体:
createText通过loadFontAsync加载字体;如果缺失该系列,请使用Inter。 export上限为 8 MB——请降低scale而不是重试。- 读取非当前页面的子元素需要
loadAllPagesAsync;桥接会自动重试一次,但最好使用带allPages: true的find。 - 编辑插件文件(
code.js、ui.html)后,用户必须重新运行插件;对manifest.json的更改需要重新导入。