Novu 仓库 figma-use 技能详解:Agent 如何借助 use_figma 工具与 Figma Plugin API 完成程序化设计
本文基于 Novu 开源仓库 .agents/skills/figma-use/SKILL.md 编写。该技能文件是为 AI Agent 准备的「强制前置」规范:任何使用 use_figma 工具在 Figma 文件中执行 JavaScript 之前,都必须先加载它,以规避字体加载、页面切换、Auto Layout 顺序等一系列难以排查的失败模式。读完本文,你将掌握 use_figma 的执行模型、17 条关键 API 使用规则、query/set/createAutoLayout 等高效 API、增量式设计工作流、错误恢复机制与提交前自查清单,能够安全、可复用地让 Agent 在 Figma 中创建、编辑、校验设计产物。
1. 技能定位:为什么它是 use_figma 的强制前置
Novu 仓库在 .agents/skills/ 目录下随源码附带了面向 AI Agent 的一组开发技能(同目录还包括 frontend-design、react-email、email-best-practices 等)。其中 figma-use 技能的目标非常明确:规范 Agent 通过 use_figma 工具调用 Figma Plugin API,在 Figma 文件上下文中以代码方式执行「写操作或需要 JavaScript 的独特只读操作」——例如创建/编辑/删除节点、建立 variables 与 tokens、构建组件与变体、修改 Auto Layout 或填充、把变量绑定到属性、以编程方式检查文件结构等。
技能文件的 YAML frontmatter 中,description 字段明确将其标注为 MANDATORY prerequisite(强制前置条件):在每一次 use_figma 调用之前都必须先加载该技能,跳过它会导致常见且难以调试的失败。因此它设计的 disable-model-invocation: false,意味着模型可以在任务进行中主动加载这份技能,而不是仅在对话开头被动触发一次。
技能还规定了两条与工具链协作的基本约定:
- 调用
use_figma时必须始终传入skillNames: "figma-use"。这是一个用于记录技能使用情况的日志参数,不影响执行结果。 - 如果 Figma MCP 工具是以 deferred(延迟加载) 形式出现,应在一次
ToolSearch调用中批量加载全部 schema,例如使用select:语法:ToolSearch query="select:use_figma,get_screenshot,get_metadata,create_new_file"。一次往返远胜六次逐个加载。
在加载技能之后、动手写任何插件 API 代码之前,技能要求先浏览 references/plugin-api-standalone.index.md 了解能力全貌;需要具体类型签名时,对 references/plugin-api-standalone.d.ts 使用 grep 按需检索,而不是一次性全量加载。与设计系统相关的工作(components、variables、text styles、effect styles)则应当从 references/working-with-design-systems/wwds.md 入手。
2. 理解 use_figma 的执行模型
在深入规则前,必须理解 use_figma 与「交互式 Figma 插件」的本质差异。技能文档反复强调一个核心事实:Agent 只能看到代码 return 出去的值,其余一切输出对它不可见。这种沙箱式执行模型决定了代码的组织方式。
技能第 1 节给出 17 条关键规则,可归纳为「运行形态、数据通道、画布操作、失败处理」四组:
2.1 运行形态:顶层 await + return
- 用
return把数据送回给 Agent。 返回值会被自动 JSON 序列化(对象、数组、字符串、数字)。不要调用figma.closePlugin(),也不要包一层 async IIFE——这些都由执行环境代为处理。 - 写普通 JavaScript,允许顶层
await与return。 代码会被自动包裹进 async 上下文,无需手工写(async () => { ... })()。 console.log()不会出现在返回结果中,输出必须走return。await每一个 Promise。 被遗忘await的异步调用(例如裸写的figma.loadFontAsync(...)或figma.setCurrentPageAsync(page))会变成 fire-and-forget,导致静默失败或竞态:脚本可能在异步操作完成前就返回,产生数据缺失或半应用的修改。
2.2 数据通道:禁用 notify,避开 pluginData
figma.notify()会抛出 "not implemented",永远不要使用它。 3a.getPluginData()/setPluginData()在use_figma中不受支持,请改用getSharedPluginData()/setSharedPluginData()(这两个受支持),或者通过return节点 ID 并在后续调用中继续跟踪:
// WRONG — not supported in use_figma
node.setPluginData('my_key', 'my_value')
// CORRECT — use shared plugin data (requires a namespace)
node.setSharedPluginData('my_namespace', 'my_key', 'my_value')
// ALSO CORRECT — return node IDs and track them across calls
const rect = figma.createRectangle()
return { nodeId: rect.id }
// 下一次 use_figma 调用中以字符串字面量传入 nodeId
2.3 画布操作:从颜色到布局的类型纪律
- 增量、小步工作。 把大操作拆成多次
use_figma调用,每步都校验。这是避免 bug 最重要的一条实践。 - 颜色取值区间是 0–1(不是 0–255):
{r: 1, g: 0, b: 0}即红色。{r: 255, g: 0, b: 0}会触发ZeroToOne校验错误。 - fills/strokes 是只读数组:不能原地
node.fills[0].color = ...,必须「克隆 → 修改 → 重新赋值」。且 paint 的color只接受{r, g, b},透明应放在 paint 层级的opacity,写成{ type: 'SOLID', color: {...}, opacity: 0.5 }。 - 字体必须先加载。不仅是设置文本时才需要——只要节点包含未加载字体,
appendChild、insertChild、setBoundVariable、setExplicitVariableModeForCollection、setValueForMode乃至findAll回调都会失败。建议脚本开头用await figma.listAvailableFontsAsync()发现可用字体与样式,再逐个await figma.loadFontAsync({ family, style })加载。 - 页面是增量加载的:用
await figma.setCurrentPageAsync(page)切换页面并加载其内容;同步 setterfigma.currentPage = page会抛错(详见第 3 节)。 setBoundVariableForPaint会返回一个「新」paint,必须捕获并重新赋值,直接使用原 paint 对象是无效的。createVariable接受 collection 对象或 ID 字符串,优先传对象。layoutSizingHorizontal/Vertical = 'FILL'必须在parent.appendChild(child)之后设置——先设置再 append 会抛错;非 auto-layout 节点设置'HUG'同理。- 新创建的顶层节点默认落在 (0,0),直接 append 到 page 会与既有内容重叠。应先扫描
figma.currentPage.children,把新节点放到最右侧节点右侧等空白处(仅对 page 直属节点有意义;嵌套在 frame/auto-layout 里的子节点由父级定位)。 - 创建变量时必须显式设置
variable.scopes。默认的ALL_SCOPES会让变量出现在每一个属性选择器里,几乎从来不是你想要的。例如背景色用["FRAME_FILL", "SHAPE_FILL"]、文本色用["TEXT_FILL"]、间距用["GAP"],完整的取值清单见 references/variable-patterns.md。
2.4 失败处理与产物追踪
use_figma报错时立即停下,不要马上重试。 失败的脚本是原子性的——脚本一旦出错就完全不会执行,文件不会被改动。仔细阅读错误信息、修正脚本后再重试(详见第 7 节)。- 必须
return全部创建/变更的节点 ID。 凡是创建了新节点或改动了画布上既有节点的脚本,都要把受影响的 ID 收集进结构化对象(如return { createdNodeIds: [...], mutatedNodeIds: [...] }),供后续调用引用、校验或清理。
每一条规则的 WRONG/CORRECT 对照示例都收录在 references/gotchas.md 中,该文件是
use_figma场景下最值得先读的避坑清单。
3. 页面规则:跨调用状态管理与页面切换
use_figma 的页面上下文有两条硬性规则:
- 每次调用之间页面上下文会重置——每次调用开始时
figma.currentPage都停在第一个页面。 - 同步 setter 不可用。 写
figma.currentPage = page会抛出"Setting figma.currentPage is not supported"。读figma.currentPage是安全的,只有赋值会抛错;切换页面必须使用await figma.setCurrentPageAsync(page)。
// Switch to a specific page (loads its content)
const targetPage = figma.root.children.find((p) => p.name === "My Page");
await figma.setCurrentPageAsync(targetPage);
// targetPage.children is now populated
// Iterate over all pages
for (const page of figma.root.children) {
await figma.setCurrentPageAsync(page);
// page.children is now loaded — read or modify them here
}
由于每次调用都从第一页开始,跨多步工作流如果要操作非默认页面,必须在每次调用开头执行 await figma.setCurrentPageAsync(page)。反过来也可以利用这一特性:多次调用 use_figma 在文件状态上增量构建,或先写一段只读脚本获取既有节点的元数据并 return,再在下一个脚本里利用这些数据去修改节点。
页面上下文还影响排查方式:icons、variables、components 可能位于第一页以外的页面,get_metadata 只返回你所传节点的子树;因此技能建议用不带 nodeId 的 get_metadata 以最廉价方式列出文档顶层页面({guid, name}),需要更多细节(子节点数、顶层节点类型)时再退回 use_figma 脚本枚举:
const pages = figma.root.children.map(p => `${p.name} id=${p.id} children=${p.children.length}`);
return pages.join('\n');
4. return 是唯一的输出通道
技能的第三条总原则可以单独成节,因为它是整个开发循环的支点:Agent 只能看到你 return 的值,其余一切都是不可见的。
- 返回 ID(关键):每个创建或变更画布节点的脚本都必须返回全部受影响节点 ID,这是硬性要求而非可选。推荐结构:
return { createdNodeIds: [...], mutatedNodeIds: [...] }
- 进度汇报:多步任务中每步都带可执行信息,例如
return { createdNodeIds: [...], count: 5, errors: [] }。 - 错误信息:抛出的错误会被自动捕获并返回——让异常自然传播或显式
throw即可,无需自行捕获后格式化。 console.log()的输出永远不会回到 Agent 手里。- 总是返回可执行的数据(ID、计数、状态),让后续调用能引用已创建的对象。
「返回 ID」与「脚本原子性」两条规则共同支撑起安全的多步设计循环:前一步失败文件不变,前一步成功则返回 ID 作为下一步的输入字面量。
5. 编辑器模式:design / FigJam / Slides 的节点差异
use_figma 默认工作在 design 模式(editorType: "figma")。FigJam("figjam")与 Slides("slides")拥有不同的可用节点集合:design 的大部分节点类型在 FigJam 中被封锁,而 FigJam 专属节点在 Slides 中也被封锁。
Design 模式(默认)
| 类别 | 节点类型 |
|---|---|
| 可用 | Rectangle, Frame, Component, Text, Ellipse, Star, Line, Vector, Polygon, BooleanOperation, Slice, Page, Section, TextPath |
| 被封锁 | Sticky, Connector, ShapeWithText, CodeBlock, Slide, SlideRow, SlideGrid, InteractiveSlideElement, Webpage |
Slides 模式
| 类别 | 节点类型 |
|---|---|
| 可用 | Rectangle, Frame, Component, Text, Ellipse, Star, Line, Vector, Polygon, BooleanOperation, Slice, Section, TextPath, Slide, SlideRow, SlideGrid, InteractiveSlideElement |
| 被封锁 | Sticky, Connector, ShapeWithText, CodeBlock, Webpage, Page |
几点补充:
- Slides 文件目前没有独立的读取工具,需要用
use_figma的只读脚本做检查(即第 6 节 "Inspect first" 模式),并用get_screenshot或await node.screenshot()获取视觉上下文。 - 涉及 Slides 专属 API 的指导,原技能约定另行加载
figma-use-slides技能(该技能不在当前 Novu 仓库快照中)。
6. 高效 API:优先于啰嗦的等价写法
use_figma 环境提供了一批能显著减少样板代码、消除排序错误并压缩 token 输出的 API。技能明确规定:总是优先使用它们,而不是冗长的传统写法。
6.1 node.query(selector):CSS 式节点搜索
在某个子树内用 CSS 式选择器查找节点,替代啰嗦的 findAll + 过滤循环:
// BEFORE — verbose traversal
const texts = frame.findAll(n => n.type === 'TEXT' && n.name === 'Title')
// AFTER — one-liner with query
const texts = frame.query('TEXT[name=Title]')
选择器语法一览:
- 类型:
FRAME、TEXT、RECTANGLE、ELLIPSE、COMPONENT、INSTANCE、SECTION(大小写不敏感); - 属性精确匹配:
[name=Card]、[visible=true]、[opacity=0.5]; - 属性子串匹配:
[name*=art](包含)、[name^=Header](前缀)、[name$=Nav](后缀); - 点路径遍历:
[fills.0.type=SOLID]、[fills.*.type=SOLID](通配下标); - 实例匹配:
[mainComponent=nodeId]、[mainComponent.name=Button]; - 组合器:
FRAME > TEXT(直接子节点)、FRAME TEXT(任意后代)、A + B(相邻兄弟)、A ~ B(一般兄弟); - 伪类:
:first-child、:last-child、:nth-child(2)、:not(TYPE)、:is(FRAME, RECTANGLE)、:where(TEXT, ELLIPSE); - 节点 ID:
#nodeId或裸 GUID; - 并集:
TEXT, RECTANGLE;通配:*。
QueryResult 提供的方法:
| 方法 | 说明 |
|---|---|
.length |
匹配节点数 |
.first() |
第一个匹配节点(无则 null) |
.last() |
最后一个匹配节点 |
.toArray() |
转为普通数组 |
.each(fn) |
带回调迭代,返回 this 以支持链式调用 |
.map(fn) |
映射为新数组 |
.filter(fn) |
过滤为新 QueryResult |
.values(keys) |
抽取属性值:.values(['name', 'x', 'y']) → [{name, x, y}, ...] |
.set(props) |
给所有匹配节点批量设属性(见 6.2) |
.query(selector) |
在匹配结果内做子查询 |
for...of |
可迭代,可用于 for 循环 |
作用域:node.query() 只在节点自身子树内搜索;要搜索整个页面用 figma.currentPage.query('...'),不存在全局的 figma.query()。代码示例:
// Recolor all text inside cards
figma.currentPage.query('FRAME[name^=Card] TEXT').set({
fills: [{type: 'SOLID', color: {r: 0.2, g: 0.2, b: 0.8}}]
})
// Get names and positions of all frames
return figma.currentPage.query('FRAME').values(['name', 'x', 'y'])
// Find the first component named "Button"
const btn = figma.currentPage.query('COMPONENT[name=Button]').first()
// Find all instances of a specific component
figma.currentPage.query(`INSTANCE[mainComponent=${compId}]`)
// Find nodes with solid fills using dot-path traversal
figma.currentPage.query('[fills.0.type=SOLID]')
6.2 node.set(props):批量属性更新
一次调用设置多个属性,返回 this 以便链式调用:
// BEFORE — one line per property
frame.opacity = 0.5
frame.cornerRadius = 8
frame.name = "Card"
// AFTER — single call
frame.set({ opacity: 0.5, cornerRadius: 8, name: "Card" })
两个实现细节值得注意:
- 优先级键排序:
layoutMode无论出现在对象中的哪个键位置,都会在其它属性(如width/height)之前被应用。这规避了经典的顺序 bug——过去resize()的行为会随layoutMode是否已设置而不同。 - 宽高处理:
width与height会被自动路由到node.resize()——设置{ width: 200 }等价于调用resize(200, currentHeight)。
与 query 链式配合即可完成「找到一批节点并统一更新」:
// Find all rectangles named "Divider" and update them
figma.currentPage.query('RECTANGLE[name=Divider]').set({
fills: [{type: 'SOLID', color: {r: 0.9, g: 0.9, b: 0.9}}],
cornerRadius: 2
})
6.3 figma.createAutoLayout(direction?, props?):即开即用的自动布局容器
创建已启用 Auto Layout、两条轴都 HUG 内容的 frame。任何需要 Auto Layout 的容器都应优先用它,而不是 figma.createFrame():
// BEFORE — manual setup, easy to get ordering wrong
const frame = figma.createFrame()
frame.layoutMode = 'VERTICAL'
frame.primaryAxisSizingMode = 'AUTO'
frame.counterAxisSizingMode = 'AUTO'
frame.layoutSizingHorizontal = 'HUG'
frame.layoutSizingVertical = 'HUG'
// AFTER — one call, layout ready
const frame = figma.createAutoLayout('VERTICAL')
子节点被 append 之后可以立即使用 layoutSizingHorizontal/Vertical = 'FILL',无需手工设置 sizing mode。它还接受可选的 props 对象作为第一个或第二个参数:
figma.createAutoLayout({ name: 'Card', itemSpacing: 12 }) // HORIZONTAL + props
figma.createAutoLayout('VERTICAL', { name: 'Column', itemSpacing: 8 }) // VERTICAL + props
6.4 node.placeholder:进行中的 shimmer 反馈
给节点设置一层视觉 shimmer 叠加,用于向用户表达「正在处理中」。完成后必须移除——残留的 shimmer 会让用户误以为工作尚未结束:
// Mark as in-progress
frame.placeholder = true
// ... build out the content ...
// MUST remove when done — never leave shimmers on finished nodes
frame.placeholder = false
构建复杂版式时推荐做法是:填充各 section 之前先 placeholder = true,每个 section 完成后立即置回 false。
6.5 await node.screenshot(opts?):内联截图校验
把某个节点渲染为 PNG 并内联在工具响应中返回,省去一次独立的 get_screenshot 调用:
// Take a screenshot of a frame (returned inline in the tool response)
await frame.screenshot()
// Custom scale (default auto-scales: 0.5x or capped so max dimension ≤ 1024px)
await frame.screenshot({ scale: 2 })
// Include overlapping content from sibling nodes
await frame.screenshot({ contentsOnly: false })
- 何时用:创建或修改节点后,在同一脚本内用
screenshot()做视觉验证,不必再单独调get_screenshot。 - 自动命名:图片标题携带节点元数据——
"Card (300x150 at 0,60).png"——不需要解析图片就能获得空间上下文。 - 默认缩放:默认 0.5x,并自动封顶使最长边不超过 1024px;显式传入
{ scale: N }则绕过封顶。
7. 增量式工作流:如何从根本上避免 bug
技能文档直言:bug 最常见的成因就是试图在一次 use_figma 调用里做得太多。正确的姿势是小步推进、逐步校验。
关键规则
- 每次
use_figma调用最多 10 个逻辑操作。 一个「逻辑操作」指创建节点、设置属性并将其挂到父级之下。要建 20 个节点,就拆成 2~3 次调用。 - 自顶向下构建,先用占位符。 先用
placeholder = true标记各 section 建出外层结构,再在后续调用中用真实内容逐段替换占位符。
标准模式(6 步)
- 先检查(Inspect first)。 创建任何东西之前,先跑一段只读
use_figma,弄清文件里已有哪些页面、组件、变量与命名约定,让新代码匹配现状。 - 搭骨架。 用带 placeholder section 的结构建出顶层骨架,每个 section 都置
placeholder = true,让用户看到进度。 - 逐段填充。 每次后续调用填充一个 section,完成后置
placeholder = false,并用screenshot()验证。 - 每次调用都返回 ID。 始终以对象形式
return创建的节点 ID、变量 ID、collection ID(如return { createdNodeIds: [...] }),它们是后续调用的输入。 - 每步后校验。 用
get_metadata验证结构(数量、名称、层级、位置);在关键节点之后用内联await node.screenshot()或get_screenshot捕捉视觉问题。 - 先修复再继续。 校验发现问题就当场修复,绝不要在坏地基上继续搭建。
复杂任务的推荐步骤顺序
Step 1: Inspect file — discover existing pages, components, variables, conventions
Step 2: Create tokens/variables (if needed)
→ validate with get_metadata
Step 3: Create individual components
→ validate with get_metadata + get_screenshot
Step 4: Compose layouts from component instances
→ validate with get_screenshot
Step 5: Final verification
每步该校验什么
| 步骤之后 | 用 get_metadata 查 |
用 get_screenshot 查 |
|---|---|---|
| 创建变量 | collection 数量、变量数量、mode 名称 | — |
| 创建组件 | 子节点数量、variant 名称、属性定义 | 变体可见、未被折叠、网格可读 |
| 绑定变量 | 节点属性反映绑定关系 | 颜色/token 正确解析 |
| 拼装版式 | 实例节点具备 mainComponent、层级正确 | 无文字裁切/遮挡、无元素重叠、间距正确 |
更完整的校验与恢复流程见 references/validation-and-recovery.md。
8. 错误恢复与自纠错:善用脚本的原子性
use_figma 是原子性的——失败的脚本不会执行。 一旦脚本出错,文件完全保持调用前的状态:没有半成品节点、没有孤儿元素,修正后重试是安全的。
当 use_figma 返回错误时:
- 停。 不要立即「改一改就重试」。
- 仔细阅读错误信息,判断根因是 API 用法错误、缺少字体还是属性值非法。
- 错误不明时,调用
get_metadata或get_screenshot弄清当前文件状态。 - 根据错误信息修正脚本。
- 重试修正后的脚本。
常见错误与对策速查
| 错误信息 | 可能原因 | 修法 |
|---|---|---|
"not implemented" |
使用了 figma.notify() |
删除它——用 return 输出 |
"node must be an auto-layout frame..." |
在 append 到 auto-layout 父级之前设置了 FILL/HUG |
把 appendChild 移到 layoutSizingX = 'FILL' 之前 |
"Setting figma.currentPage is not supported" |
使用了同步 setter(figma.currentPage = page) |
改用 await figma.setCurrentPageAsync(page)——这是切换页面的唯一方式 |
| 属性值超出范围 | 颜色通道大于 1(用了 0–255 而非 0–1) | 除以 255 |
"Cannot read properties of null" |
节点不存在(ID 错误、页面错误) | 检查页面上下文,核对 ID |
| 脚本挂起 / 无响应 | 死循环或未 resolve 的 Promise | 检查 while(true) 或缺失的 await,确保代码能终止 |
"The node with id X does not exist" |
子节点 detachInstance() 隐式 detach 了父实例并改变其 ID |
从稳定的(非 instance)父 frame 出发按遍历重新发现节点 |
当脚本成功但结果看起来不对时:
- 调用
get_metadata检查结构正确性(层级、数量、位置)。 - 调用
get_screenshot检查视觉正确性——重点看文字是否被裁切/裁剪(行高切断内容)与元素是否重叠,这两类问题常见又容易被忽略。 - 判断差异属于结构性(层级错误、节点缺失)还是视觉性(颜色错误、布局破损、内容被裁切)。
- 写只针对破损部分做修改的修复脚本——不要全部重建。
上述错误表中涉及的底层坑(paint 不可变数组、setBoundVariableForPaint 返回新 paint、combineAsVariants 不自动布局、resize() 会把 sizing mode 重置回 FIXED、lineHeight/letterSpacing 必须用 {unit, value} 对象、counterAxisAlignItems 不支持 'STRETCH'、模式数量受套餐限制、变量默认 ALL_SCOPES 等)均有完整的 WRONG/CORRECT 对照,见 references/gotchas.md。
9. 提交前的 Pre-Flight 自查清单
技能要求在任何一次 use_figma 调用被提交之前,逐一核对以下事项——这张清单相当于把第 2、3、4、5 节的规则浓缩成可勾选项:
- [ ] 代码使用
return回传数据(而不是figma.closePlugin()); - [ ] 代码没有包在 async IIFE 里(执行环境会自动包裹);
- [ ]
return值包含结构化、可执行的数据(ID、计数); - [ ] 任何地方都没有使用
figma.notify(); - [ ] 没有用
console.log()作为输出(一律用return); - [ ] 所有颜色使用 0–1 区间(不是 0–255);
- [ ] paint 的
color只含{r, g, b},不带a字段(不透明度放 paint 层:{ type: 'SOLID', color: {...}, opacity: 0.5 }); - [ ] fills/strokes 以新数组重新赋值(没有原地修改);
- [ ] 页面切换使用
await figma.setCurrentPageAsync(page)(同步figma.currentPage = page不可用); - [ ]
layoutSizingVertical/Horizontal = 'FILL'在parent.appendChild(child)之后设置; - [ ] 修改文本属性前已调用
loadFontAsync()(不确定字体可用性时先用listAvailableFontsAsync()确认); - [ ] 样式名称已通过
listAvailableFontsAsync()核实——绝不凭记忆猜测("SemiBold"与"Semi Bold"是常见陷阱); - [ ] 对
FONT_FAMILY作用域的变量:调用setBoundVariable("fontFamily", …)、setValueForMode或setExplicitVariableModeForCollection前,相关 mode 的每个值都已加载; - [ ]
lineHeight/letterSpacing使用{unit, value}格式(不是裸数字); - [ ]
resize()在设置 sizing mode 之前调用(resize 会把 mode 重置为 FIXED); - [ ] 多步工作流中,上一步的 ID 以字符串字面量传入(而不是变量);
- [ ] 新的顶层节点已避开 (0,0),不会与既有内容重叠;
- [ ] 所有创建/变更的节点 ID 都已收集并包含在
return值里; - [ ] 每个异步调用(
loadFontAsync、setCurrentPageAsync、importComponentByKeyAsync等)都被await——没有 fire-and-forget 的 Promise。
10. 动手前先勘察约定:三个即用型巡检脚本
技能强调:在创建任何东西之前,总是先检查 Figma 文件。不同文件有不同的命名约定、变量结构与组件模式,代码应匹配已有约定而不是强加新约定;当文件里没有可参考的约定时,再看向用户代码库,最后才回退到通用模式。
技能提供的三个巡检脚本可以直接复用:
列出全部页面与顶层节点:
const pages = figma.root.children.map(p => `${p.name} id=${p.id} children=${p.children.length}`);
return pages.join('\n');
跨页面列出既有组件:
const results = [];
for (const page of figma.root.children) {
await figma.setCurrentPageAsync(page);
page.findAll(n => {
if (n.type === 'COMPONENT' || n.type === 'COMPONENT_SET')
results.push(`[${page.name}] ${n.name} (${n.type}) id=${n.id}`);
return false;
});
}
return results.join('\n');
列出变量集合及其约定:
const collections = await figma.variables.getLocalVariableCollectionsAsync();
const results = collections.map(c => ({
name: c.name, id: c.id,
varCount: c.variableIds.length,
modes: c.modes.map(m => m.name)
}));
return results;
11. 技能自带的可运行模式库与类型参考
snippets 贯穿整个技能文档,其下方的各参考文件本身就是可以直接复制或作为起点的代码库,也是后续深入 use_figma 编程的主入口:
| 参考文档 | 何时加载 | 覆盖内容 |
|---|---|---|
| gotchas.md | 任何 use_figma 调用之前 |
每一个已知坑,带 WRONG/CORRECT 代码示例 |
| common-patterns.md | 需要可运行代码示例 | 脚本骨架:图形、文本、auto-layout、变量、组件、多步工作流 |
| plugin-api-patterns.md | 创建/编辑节点 | fills、strokes、Auto Layout、效果、分组、克隆、样式 |
| api-reference.md | 需要精确 API 面 | 节点创建、variables API、核心属性、可用与不可用项 |
| validation-and-recovery.md | 多步写入或错误恢复 | get_metadata 与 get_screenshot 工作流、强制的错误恢复步骤 |
| component-patterns.md | 创建组件/变体 | combineAsVariants、组件属性、INSTANCE_SWAP、变体布局、既有组件发现、元数据遍历 |
| variable-patterns.md | 创建/绑定变量 | collections、modes、scopes、alias、绑定模式、既有变量发现 |
| text-style-patterns.md | 创建/应用文本样式 | 字号阶梯、listAvailableFontsAsync 字体发现、样式列表、应用样式到节点 |
| effect-style-patterns.md | 创建/应用效果样式 | 投影、列出样式、把样式应用到节点 |
| plugin-api-standalone.index.md | 需要理解完整 API 面 | Plugin API 全部类型、方法、属性的索引 |
| plugin-api-standalone.d.ts | 需要精确类型签名 | 完整类型声明文件——用 grep 检索具体符号,不要一次性全量加载 |
12. 组合使用:何时联动其它技能与参考
最后,figma-use 并不是孤立存在的。技能文档明确了两条协作路径,有助于在真实 Agent 工作流里判断「该不该现在用它」:
- 涉及从代码构建或更新一整页/整屏/多 section 版式时,技能约定应额外加载
figma-generate-design技能,后者提供通过search_design_system发现设计系统组件、导入并增量拼装屏幕的工作流。二者分工清晰:figma-use管 Plugin API 规则,figma-generate-design管整屏搭建流程(该技能不在当前仓库快照内,需按实际环境加载)。 - 每次任务都先加载
figma-use本体,再依据任务类型选择上述参考文件:写代码前 grep plugin-api-standalone.d.ts 确认 API 存在且签名正确——凡是名称「听起来像」但不在类型声明中的属性,写入时几乎必然抛错。涉及设计系统时,则从 references/working-with-design-systems/wwds.md 起步,再按需深入 components、variables、text styles、effect styles 的分册参考。
把这些规则、清单与可复用脚本组合起来,Agent 就能以「小步 → 返回 ID → 校验 → 修复」的稳定节奏,在 Figma 中完成从建立 token、搭建组件变体到拼装完整页面的一系列程序化设计工作,并把失败率与返工成本压到最低。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00