Novu 仓库 figma-use 技能详解:use_figma 环境的 Figma Plugin API 能力边界与实战参考
Novu 仓库 figma-use 技能详解use_figma 环境的 Figma Plugin API 能力边界与实战参考【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu本文基于 Novu 仓库中 .agents/skills/figma-use/references/api-reference.md 展开系统梳理use_figma执行环境下 Figma Plugin API 的可用方法、Variables 绑定机制、核心属性约束与明确不支持的 API 清单。读完本文后你可以准确判断一段 Figma 插件脚本在该执行环境中什么能跑、什么会抛错并掌握变量绑定、库导入与页面切换等高频场景的正确写法。该参考文档是 figma-use 技能 的核心参考文件之一。所谓use_figma是通过工具在 Figma 文件上下文中执行 JavaScript 的插件式环境脚本会被自动包裹进带错误捕获的 async IIFE因此可以直接使用顶层await和return无需figma.closePlugin()。文档的首要价值是划清这条执行链路与标准 Figma 插件沙箱之间的能力差异——大量在插件文档中常见的 API如figma.notify()在这里是不存在或会抛异常的。一、节点创建Design Mode文档列出了use_figma环境中可用的全部节点创建方法figma.createRectangle() figma.createFrame() figma.createAutoLayout() // Frame with auto layout enabled, both axes hug — prefer over createFrame() for layout containers figma.createAutoLayout(VERTICAL) // Same but vertical direction figma.createComponent() // Creates a ComponentNode figma.createText() figma.createEllipse() figma.createStar() figma.createLine() figma.createVector() figma.createPolygon() figma.createBooleanOperation() figma.createSlice() figma.createPage() // Page node can be created, but child persistence is limited in use_figma figma.createSection() figma.createTextPath()其中两点值得特别关注figma.createAutoLayout()应优先于figma.createFrame()。createAutoLayout返回的 Frame 已开启 auto layout 且两个轴均为 hugHUG状态避免了手动设置layoutMode、primaryAxisSizingMode等一系列易错属性。SKILL.md 中的对比示例说明了差异// 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)它还可接受一个可选的属性对象作为第一或第二个参数figma.createAutoLayout({ name: Card, itemSpacing: 12 })或figma.createAutoLayout(VERTICAL, { name: Column, itemSpacing: 8 })。子节点在 append 之后即可直接设置layoutSizingHorizontal/Vertical FILL。figma.createPage()可创建页面节点但子节点的持久化能力受限。这是一个与标准插件环境不同的隐含限制跨页面操作时应优先用setCurrentPageAsync切换已有页面而非新建页面。二、分组与布尔运算figma.group(nodes, parent, index?) // Group nodes figma.flatten(nodes, parent?, index?) // Flatten to vector figma.union(nodes, parent?, index?) // Boolean union figma.subtract(nodes, parent?, index?) // Boolean subtract figma.intersect(nodes, parent?, index?) // Boolean intersect figma.exclude(nodes, parent?) // Boolean exclude figma.combineAsVariants(components, parent?) // Combine ComponentNodes into ComponentSet (Design/Sites only)这组方法的共同特征是接收(nodes, parent?, index?)参数结构。其中figma.combineAsVariants()只接受ComponentNode数组传 Frame 会抛错且仅适用于 Design/Sites 编辑器。结合 component-patterns.md 的实现细节combineAsVariants在use_figma中不会自动布局变体——合并后所有子组件堆叠在(0, 0)ComponentSet 的尺寸会退化为单个变体的大小。正确做法是合并后手动按网格排布并依据子节点实际边界而非公式重新resizeWithoutConstraintsconst cs figma.combineAsVariants(components, figma.currentPage) const colWidth 120 const rowHeight 56 cs.children.forEach((child, i) { const col i % numCols const row Math.floor(i / numCols) child.x col * colWidth child.y row * rowHeight }) // CRITICAL: resize from actual child bounds, not formula let maxX 0, maxY 0 for (const child of cs.children) { maxX Math.max(maxX, child.x child.width) maxY Math.max(maxY, child.y child.height) } cs.resizeWithoutConstraints(maxX 40, maxY 40)变体命名遵循PropertyValue约定如sizemd, styleprimary每个唯一的属性组合都必须存在对应的子组件否则变体选择器中会出现空白位。三、团队库导入组件、样式与变量文档明确区分了两类资源来源团队库team libraries与当前文件。针对当前文件内的组件应使用figma.getNodeByIdAsync()或findOne()/findAll()直接定位只有导入其他文件的已发布资产时才使用import*ByKeyAsync系列方法。3.1 组件导入// Import a published component from a team library by key const comp await figma.importComponentByKeyAsync(COMPONENT_KEY) const instance comp.createInstance() // Import a published component set from a team library by key const compSet await figma.importComponentSetByKeyAsync(COMPONENT_SET_KEY) const variant compSet.children.find((c) c.type COMPONENT c.name.includes(sizemd)) || compSet.defaultVariant const variantInstance variant.createInstance()注意导入的是组件源定义落到画布上需要createInstance()生成实例。对于组件集先按变体名称匹配、匹配不到则回退compSet.defaultVariant。3.2 样式导入// Import a published style from a team library by key const style await figma.importStyleByKeyAsync(STYLE_KEY) // Apply the imported style to a node await node.setFillStyleIdAsync(style.id) // for PaintStyle as fill await node.setStrokeStyleIdAsync(style.id) // for PaintStyle as stroke await node.setTextStyleIdAsync(style.id) // for TextStyle await node.setEffectStyleIdAsync(style.id) // for EffectStyle await node.setGridStyleIdAsync(style.id) // for GridStyle当前文件内的样式则应使用本地查询方法figma.getLocalPaintStyles()、figma.getLocalTextStyles()等。3.3 变量导入// Import a published variable from a team library by key const variable await figma.variables.importVariableByKeyAsync(VARIABLE_KEY) // Bind the imported variable to node properties node.setBoundVariable(width, variable) // FLOAT variable // Bind to fills/strokes (COLOR variable) — returns a NEW paint, must capture it const newPaint figma.variables.setBoundVariableForPaint(paintCopy, color, variable) node.fills [newPaint]variable-patterns.md 补充了判断依据若variable.remote true说明变量来自库可以直接引用已导入时或按 key 导入remote false则是本地变量直接用getVariableByIdAsync查询。四、Variables API集合、模式、作用域与绑定这是整个参考文档中技术密度最高的部分。4.1 集合与模式const collection figma.variables.createVariableCollection(Name) collection.name // Get/set name collection.modes // Array of {modeId, name} — starts with 1 mode collection.addMode(Dark) // Returns new modeId string collection.renameMode(modeId, Light)关键事实新集合自带一个模式默认名为Mode 1正确姿势是先renameMode再addModeconst collection figma.variables.createVariableCollection(Colors) collection.renameMode(collection.modes[0].modeId, Light) const darkModeId collection.addMode(Dark)另外每个集合可拥有的模式数量上限取决于订阅计划Free 仅 1 个Professional 至多 4 个Organization/Enterprise 40。需要大量模式时应拆分为多个集合并对每个集合分别调用setExplicitVariableModeForCollection。4.2 变量创建与作用域const variable figma.variables.createVariable(name, collection, COLOR) // ^ must be a collection object (passing an ID string is deprecated) // resolvedType: COLOR | FLOAT | STRING | BOOLEAN variable.setValueForMode(modeId, value)createVariable的第二参数必须传集合对象传 ID 字符串已被弃用。作用域scopes控制变量出现在哪些属性选择器中默认值ALL_SCOPES会污染所有下拉框因此创建变量后应显式设置 scopesvariable.scopes [FRAME_FILL, SHAPE_FILL] // only fill pickers variable.scopes [TEXT_FILL] // only text color picker variable.scopes [STROKE_COLOR] // only stroke picker variable.scopes [] // hidden from all pickers (use for primitives)全部合法 scope 值ALL_SCOPES, TEXT_CONTENT, CORNER_RADIUS, WIDTH_HEIGHT, GAP, ALL_FILLS, FRAME_FILL, SHAPE_FILL, TEXT_FILL, STROKE_COLOR, STROKE_FLOAT, EFFECT_FLOAT, EFFECT_COLOR, OPACITY, FONT_FAMILY, FONT_STYLE, FONT_WEIGHT, FONT_SIZE, LINE_HEIGHT, LETTER_SPACING, PARAGRAPH_SPACING, PARAGRAPH_INDENTgotchas.md 给出了典型映射背景色用[FRAME_FILL, SHAPE_FILL]、文本色用[TEXT_FILL]、边框色用[STROKE_COLOR]、间距用[GAP]而仅通过别名被引用的原始令牌primitives则设scopes []从所有选择器中隐藏。4.3 异步查询// Querying (always use the Async variants — sync versions are deprecated) await figma.variables.getVariableByIdAsync(id) await figma.variables.getLocalVariablesAsync(resolvedType?) await figma.variables.getVariableCollectionByIdAsync(id) await figma.variables.getLocalVariableCollectionsAsync()文档强调一律使用 Async 变体同步版本已弃用。配套的完整类型签名可以在 plugin-api-standalone.d.ts约 1.1 万行的 typings 文件中按符号名 grep 定位其目录索引见 plugin-api-standalone.index.md按 SKILL 文档的建议该 typings 文件很大应按需检索片段而非整体加载。4.4 变量绑定的三种返回新对象模式这是文档中最容易踩坑的一组 API——它们都返回新对象而非原地修改必须捕获返回值// Binding variables to paints (COLOR variables) const newPaint figma.variables.setBoundVariableForPaint(paintCopy, color, variable) // ⚠️ Returns a NEW paint — must capture return value! node.fills [newPaint] // Binding variables to effects (COLOR/FLOAT variables) const newEffect figma.variables.setBoundVariableForEffect(effectCopy, field, variable) // field for shadows: color (COLOR), radius | spread | offsetX | offsetY (FLOAT) // field for blurs: radius (FLOAT) // ⚠️ Returns a NEW effect — must capture return value! node.effects [newEffect] // Binding variables to layout grids (FLOAT variables) const newGrid figma.variables.setBoundVariableForLayoutGrid(gridCopy, field, variable) // field: sectionSize | offset | count | gutterSize // ⚠️ Returns a NEW layout grid — must capture return value! node.layoutGrids [newGrid]错误写法与正确写法的对照源自 gotchas.md// WRONG — ignoring return value figma.variables.setBoundVariableForPaint(paint, color, colorVar) node.fills [paint] // paint is unchanged! // CORRECT — capture the returned new paint const boundPaint figma.variables.setBoundVariableForPaint(paint, color, colorVar) node.fills [boundPaint]此外只有 SOLID 类型的 paint 支持颜色变量绑定渐变或图片 paint 会抛错。variable-patterns.md 还指出一个类型差异paint 的color只接受{r, g, b}透明度放在 paint 层的opacity字段而 COLOR 变量的值使用{r, g, b, a}——两者不要混用。4.5setBoundVariable属性级绑定清单// Binding variables to node properties (FLOAT/STRING/BOOLEAN) // Layout sizing (FLOAT): node.setBoundVariable(width, variable) node.setBoundVariable(height, variable) node.setBoundVariable(minWidth, variable) node.setBoundVariable(maxWidth, variable) node.setBoundVariable(minHeight, variable) node.setBoundVariable(maxHeight, variable) node.setBoundVariable(paddingLeft, variable) node.setBoundVariable(paddingRight, variable) node.setBoundVariable(paddingTop, variable) node.setBoundVariable(paddingBottom, variable) node.setBoundVariable(itemSpacing, variable) node.setBoundVariable(counterAxisSpacing, variable) // Corner radii (FLOAT) — use individual corners, NOT cornerRadius: node.setBoundVariable(topLeftRadius, variable) node.setBoundVariable(topRightRadius, variable) node.setBoundVariable(bottomLeftRadius, variable) node.setBoundVariable(bottomRightRadius, variable) // Other (FLOAT): node.setBoundVariable(opacity, variable) node.setBoundVariable(strokeWeight, variable) // ⚠️ fontSize, fontWeight, lineHeight are NOT bindable via setBoundVariable // — set these directly as values on text nodes三个要点圆角必须绑定四个独立角topLeftRadius等没有cornerRadius这个绑定字段fontSize、fontWeight、lineHeight三个文本属性不可绑定只能在文本节点上直接赋值。4.6 别名与显式模式// Aliases figma.variables.createVariableAlias(variable) // Explicit modes — CRITICAL for variant components node.setExplicitVariableModeForCollection(collection, modeId) // pass collection object, NOT an ID string // Without this, all nodes use the default (first) mode of the collectionsetExplicitVariableModeForCollection对变体组件至关重要若不设置所有节点都会解析到集合的默认第一个模式导致不同变体渲染出相同颜色。其参数必须是集合对象而非 ID 字符串。语义令牌引用原始令牌的别名写法见 variable-patterns.mdsemanticVar.setValueForMode(modeId, { type: VARIABLE_ALIAS, id: primitiveVar.id })五、核心属性与页面切换figma.root // DocumentNode figma.currentPage // Current page — READ ONLY; the sync setter (figma.currentPage page) does NOT work and throws figma.setCurrentPageAsync(page) // Switch page and load its content (MUST await) — this is the ONLY way to change pages figma.fileKey // File key string figma.mixed // Mixed sentinel value页面切换是本执行环境最重要的约束之一figma.currentPage只读同步赋值figma.currentPage page会抛出Setting figma.currentPage is not supported唯一合法的换页方式是await figma.setCurrentPageAsync(page)它同时完成切换与该页内容的加载页面内容是按需加载的页面上下文在每次use_figma调用之间会重置——每次调用开始时figma.currentPage都指回第一个页面因此跨调用的工作流必须在脚本开头重新切换到目标页面。SKILL.md 给出了遍历全部页面的标准模式const targetPage figma.root.children.find((p) p.name My Page); await figma.setCurrentPageAsync(targetPage); // targetPage.children is now populated for (const page of figma.root.children) { await figma.setCurrentPageAsync(page); // page.children is now loaded — read or modify them here }六、节点操作填充、描边、布局与尺寸// Fills Strokes (read-only arrays — must clone) node.fills [{ type: SOLID, color: { r: 1, g: 0, b: 0 } }] node.strokes [{ type: SOLID, color: { r: 0, g: 0, b: 0 } }] node.strokeWeight 1 node.strokeAlign INSIDE // INSIDE | CENTER | OUTSIDE // Effects node.effects [{ type: DROP_SHADOW, color: {r:0,g:0,b:0,a:0.25}, offset:{x:0,y:4}, radius:4, visible:true }] // Layout node.layoutMode HORIZONTAL // NONE | HORIZONTAL | VERTICAL node.primaryAxisAlignItems CENTER // MIN | CENTER | MAX | SPACE_BETWEEN node.counterAxisAlignItems CENTER // MIN | CENTER | MAX | BASELINE node.paddingLeft 8 node.paddingRight 8 node.paddingTop 4 node.paddingBottom 4 node.itemSpacing 4 node.layoutSizingHorizontal HUG // FIXED | HUG | FILL node.layoutSizingVertical HUG // FIXED | HUG | FILL // Sizing node.resize(width, height) // ⚠️ Resets sizing modes to FIXED node.resizeWithoutConstraints(width, height) // Doesnt affect constraints // Corner radius node.cornerRadius 8 // Visibility Opacity node.visible true node.opacity 0.5 // Naming Hierarchy node.name My Node parent.appendChild(child) parent.insertChild(index, child) node.remove()从 gotchas.md 的实现细节看这里有四个高频陷阱fills/strokes 是只读数组原地修改node.fills[0].color ...不生效必须克隆后整体重新赋值。paint 的color只接受{r, g, b}写入a字段会抛Unrecognized key(s) in object: a透明度应写在 paint 层{ type: SOLID, color: {...}, opacity: 0.5 }。颜色是 0–1 浮点域不是 0–255{r: 1, g: 0, b: 0}表示纯红。resize()会把两个轴的 sizing mode 重置为 FIXED。若先设了 HUG 再resize(280, 1)高度会被永久锁死为 1px。规则是先resize再设 sizing mode且不要对打算 HUG 的轴传入0或1这类占位值。width/height只读直接赋值抛no setter for property必须走resize()而x/y可直接写入。另外counterAxisAlignItems不支持STRETCH枚举值需要拉伸效果时应给父级设MIN再让子级layoutSizing* FILL。与 auto layout 配合的顺序约束同样关键layoutSizingHorizontal/Vertical FILL必须在parent.appendChild(child)之后设置先于 append 设置会抛FILL can only be set on children of auto-layout frames。七、描述、文档链接、SVG 与图片7.1 描述与文档链接// Description — plain text, shown in Figmas component panel node.description A short summary of this components purpose and usage. // Documentation links — array of {uri, label} shown as clickable links componentSet.documentationLinks [ { uri: https://example.com/docs, label: Component Docs } ] // ⚠️ uri MUST be a valid URL (https://...) — relative paths will throwdocumentationLinks的uri必须是完整合法 URL相对路径会抛错。7.2 SVG 导入const svgNode figma.createNodeFromSvg(svg.../svg)一行即可完成 SVG 到 FrameNode 的转换。7.3 图片upload_assets是唯一入口文档给出了一个强约束upload_assets工具是把图片写入 Figma 文件的唯一支持途径Design、FigJam、Slides 共用此路径。不要在use_figma内部使用figma.createImage()或figma.createImageAsync()——前者不是合法的上传入口后者在use_figma中无网络访问能力无法抓取 URL且脚本内的字节序列也不是文件中的持久资产。upload_assets返回一次性上传 URLPOST 原始字节后响应包含imageHash与放置信息服务端完成提交与画布放置。传nodeId配合count: 1可把上传结果直接设为已有节点的填充省略nodeId则把图片作为新图层放到画布上upload_assets({ fileKey, count: 1, nodeId, scaleMode: FILL }) → { uploads: [{ submitUrl }], instructions: ... } // Then POST the image bytes to submitUrl (multipart/form-data file field // preferred — the filename becomes the layer name).图片一旦进入文件其imageHash可在use_figma脚本中被其他节点直接引用而无需重新上传——这也是imageHash在该环境中唯一合法用途// Re-using an imageHash that already exists on another node in the file node.fills [{ type: IMAGE, scaleMode: FILL, imageHash: hash_from_existing_node }]凡是从文件外部来的内容URL、本地文件、生成的字节、截图一律先走upload_assets。八、字体、工具函数与插件生命周期8.1 字体// Discover all available fonts and their exact style strings const allFonts await figma.listAvailableFontsAsync() // Font[] — each has { fontName: { family, style } } const interStyles allFonts.filter(f f.fontName.family Inter) // MUST load a font before any text property edit await figma.loadFontAsync({ family: Inter, style: Regular }) // Check if the file has missing fonts figma.hasMissingFont // booleanSKILL 文档把字体加载的要求写得更强任何操作到含未加载字体节点的调用都必须先加载字体——不仅是文本设置还包括appendChild、insertChild、setBoundVariable、setExplicitVariableModeForCollection、setValueForMode甚至findAll回调。如果文档中已有文本节点应在脚本开头预加载它们的全部字体。字体风格名是文件相关的例如SemiBold与Semi Bold的差异永远通过listAvailableFontsAsync()发现不要凭记忆猜测。8.2 工具函数figma.base64Encode(uint8Array) // Uint8Array → base64 string figma.base64Decode(base64String) // base64 string → Uint8Array figma.createComponentFromNode(node) // Convert existing node to component (Design/Sites only)8.3 插件生命周期return是唯一输出通道return { nodeId: frame.id } // Return object — auto-serialized to JSON return success message // Return string // Errors are auto-captured — no try/catch or closePlugin needed脚本被自动包裹为带错误捕获的 async IIFE因此不要调用figma.closePlugin()不要手写 IIFEconsole.log()的输出不会返回给调用方。SKILL.md 将其列为硬性规则凡是创建/修改了画布节点的脚本必须把所有受影响的节点 ID 收集进return的结构化对象如return { createdNodeIds: [...], mutatedNodeIds: [...] }供后续调用引用、校验或清理。common-patterns.md 给出了标准脚本骨架const createdNodeIds [] const mutatedNodeIds [] // ... track every node you create or mutate ... return { success: true, createdNodeIds, mutatedNodeIds, count: createdNodeIds.length }另一个与生命周期相关的原子性保证use_figma失败即整体不执行——脚本报错时不会对文件产生任何变更因此修正后重试是安全的。错误恢复流程见 validation-and-recovery.md停止 → 读错误信息 → 必要时用get_metadata/get_screenshot查看文件状态 → 修正脚本 → 重试。九、节点遍历node.findAll(pred?) // Find all descendants matching predicate node.findOne(pred?) // Find first descendant matching predicate node.findChildren(pred?) // Find direct children matching predicate node.findChild(pred?) // Find first direct child matching predicate node.children // Direct children array node.parent // Parent node配合按类型守卫的实践对特定类型才存在的方法如getStyledTextSegments()仅限TEXT、createInstance()仅限COMPONENT、addComponentProperty()仅限COMPONENT/COMPONENT_SET调用前必须先判断node.type否则会抛TypeError: not a function。例如由于figma.getLocalComponents()并不存在在当前文件中定位组件的标准写法就是findAll(n n.type COMPONENT)/findAll(n n.type COMPONENT_SET)SKILL 文档还额外提供了node.query(COMPONENT[nameButton])这类 CSS 风格选择器作为更简明的替代。十、不可用 API 清单What Does NOT Work这是整份参考文档的负面清单与可用清单同等重要API状态figma.notify()抛 not implemented 异常——最常见错误figma.showUI()无操作被静默忽略figma.openExternal()无操作被静默忽略figma.loadAllPagesAsync()未实现figma.variables.extendLibraryCollectionByKeyAsync()未实现figma.teamLibrary.*未实现依赖 team-library 后端figma.getLocalComponents*()不存在——与样式不同没有getLocalComponents()或getLocalComponentSetsAsync()或任何getLocalComponent*变体。应使用findAll(n n.type COMPONENT)/findAll(n n.type COMPONENT_SET)定位当前文件中的组件。注意两种失败形态的差异figma.notify()是主动抛错而figma.showUI()、figma.openExternal()是静默无操作——后者更危险因为脚本看似正常执行但实际什么都没发生。此外 gotchas.md 还补充了一条文档表格未列的不可用项getPluginData()/setPluginData()在use_figma中不受支持应改用getSharedPluginData()/setSharedPluginData()需命名空间或返回节点 ID 跨调用追踪的模式。十一、配套文档地图与使用建议api-reference.md 在整个 figma-use 技能文档体系中的定位是精确的 API 面其余参考文件按场景分工见 SKILL.md 第 10 节文档适用场景gotchas.md每次use_figma前——全部已知陷阱的 WRONG/CORRECT 对照common-patterns.md需要可运行代码示例图形、文本、auto layout、变量、组件、多步工作流骨架variable-patterns.md变量创建/绑定集合、模式、作用域、别名、代码语法、发现既有变量component-patterns.md组件/变体创建combineAsVariants、组件属性、INSTANCE_SWAP、变体布局validation-and-recovery.md多步写入或错误恢复get_metadata与get_screenshot的分工、恢复步骤plugin-api-standalone.index.md / plugin-api-standalone.d.ts完整 API 面索引与全部类型签名约 1.1 万行按符号 grep勿整体加载落地时的三条总原则以小的增量步骤调用单次调用至多约 10 个逻辑操作每步之后用get_metadata校验结构、关键里程碑后再用截图做视觉校验所有异步调用必须await未 await 的loadFontAsync会造成静默竞态写入属性前对 typings 文件做存在性核验——Figma 节点对象不可扩展写入不存在的属性名会抛object is not extensible。掌握以上边界这段api-reference.md就足以作为use_figma脚本开发的权威速查手册。【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考