--- name: react-to-figma-make description: "Use when 需要把现有 React、Vite、Next.js、V0 或 AI Studio 页面转换为 Figma Make 可导入的 .fig 资产,或补齐、更新、验证已有 Figma Make 导出壳和 canvas.fig。" --- # React 导出到 Figma Make ## 概述 本技能将帮助你把 React 组件或应用经过兼容改造后,转换为符合 Figma Make 项目规范的标准结构。转换完成后,项目将具备: - **Figma Make 可编辑性**:可在 Figma Make 平台中打开、编辑和迭代 - **`.fig` 导出能力**:生成可下载的 `.fig` 文件 - **双入口架构**:同时支持独立运行和 Figma Make 导出 - **设计 Token 保留**:保持原始设计系统的 CSS 变量和主题定义 - **资产完整性**:包含 `meta.json`、`ai_chat.json`、`images/` 等 Figma Make 要求的元文件 本技能产出一个完整的 Figma Make 兼容项目目录,并默认把可被宿主工具消费的产物写入通用 artifact 目录: ```text .axhub/make/artifacts/figma// ├── canvas.fig ├── meta.json ├── ai_chat.json ├── canvas.code-manifest.json ├── manifest.json ├── images/ └── thumbnail.png # 可选 ``` 如果目标项目使用 `.axhub/make/project.json` 描述资源,需要在对应 `resources.prototypes[].artifacts.figma` 中登记这些路径,便于 make-server 或其他宿主直接导出 `.fig`。 ## 核心原则与当前能力边界 **`canvas-fig-sync.mjs pack` 是模板节点同步器,不是通用的文件系统导入器。** - `pack` 只更新 `canvas.fig` 中已经存在的 `CODE_FILE.logicalPath`。 - 磁盘上新增但模板中不存在的路径不会自动变成新的 `CODE_FILE`。 - 使用 `--prune-missing` 时,模板中存在、磁盘上不存在的路径会被删除。 - 使用 `--sanitize-for-export` 时,聊天历史和代码快照会被清空;如果源码映射错误,无法依赖清理后的 `.fig` 恢复源码。 - `inspect` 命令退出成功只证明二进制可解析,不证明源码完整或能够运行。 因此,首次生成前必须先 inspect 模板,并让导出源码路径与模板已有逻辑路径对齐。路径由实际 template manifest 决定,不是 Figma Make 的固定文件名。当前 bundled `empty-canvas.fig` 中可以确认的通用入口和可选路径包括: | 磁盘路径(相对 `--from`) | `CODE_FILE.logicalPath` | 职责 | |---|---|---| | `src/App.tsx` | `App.tsx` | 当前模板的 Figma Make 入口,必须保留 | | `src/styles/globals.css` | `styles/globals.css` | 当前模板已有的可选样式节点 | | `src/` | 对应 `logicalPath` | 可选业务模块或其他资源 | 当前模板中的 `components/Dashboard.tsx` 只是一个历史示例槽位,不是通用规范,也不代表目标页面必须是 Dashboard。只有当本次转换明确选择该现有槽位时才能使用它。如果原页面依赖多个模板未覆盖的本地模块,优先使用项目现有构建工具把业务代码机械打包到 `src/App.tsx`,或打包到本次从 manifest 中明确选定的已有槽位。不要把业务逻辑手工重写成另一套实现;保留原页面入口,把导出壳作为可重复生成、可持续同步的独立适配层。 ## 何时使用 - 你有一个现成的 React 项目,希望导入 Figma Make 进行可视化编辑 - 你需要从 React 代码生成 `.fig` 文件用于设计交付 - 你希望在 Figma Make 和代码编辑器之间双向协作 - 你有一个 V0 / AI Studio / 自建 React 项目,想把它纳入 Figma Make 生态 ## 环境要求 本技能分为两部分: 1. **项目结构转换**(通用):将 React 项目改造为 Figma Make 目录规范 — 任何环境均可执行 2. **canvas.fig 生成与验证**:本技能内置了相关的操作脚本,位于技能自身的目录下: - `scripts/canvas-fig-sync.mjs`:canvas.fig 回写与检查工具 - `assets/empty-canvas.fig`:空白 canvas 模板 - 脚本运行依赖 `pako` 和 `kiwi-schema`。先直接运行一次;如果出现 `ERR_MODULE_NOT_FOUND`,在脚本所属包中按宿主项目规定的包管理器安装依赖,不要向目标原型的运行时依赖中添加这两个包。 如果有额外的页面渲染验收脚本,也可以在此阶段执行。 ## 引用文件 本技能附带以下参考文件,在转换特定技术领域时按需查阅: - `references/project-structure.md`:Figma Make 项目的完整目录结构和文件职责说明 - `references/meta-json-spec.md`:`meta.json` 的完整字段定义和示例 - `references/style-migration.md`:从各种 CSS 方案迁移到 Figma Make 样式体系的指南 ## 转换工作流程 ### 第 1 步:分析源项目 分析目标 React 项目的以下方面: 1. **框架识别**: - 纯 React(Vite / CRA / 自定义) - Next.js(需要移除 SSR、`"use client"`、`next/image` 等) - AI Studio(Import Map + CDN 模式) - 其他元框架 2. **入口结构**: - 主应用组件在哪里(通常是 `App.tsx` 或 `page.tsx`) - 挂载入口在哪里(`main.tsx` / `index.tsx`) - 是否有路由(多路由需收敛为单入口) 3. **样式方案**: - Tailwind CSS(CDN / PostCSS / v4) - CSS Modules - Styled Components / Emotion - 纯 CSS / SCSS - CSS 变量 / 设计 Token 4. **依赖分析**: - 核心依赖(React、ReactDOM — 外部化,不打包) - UI 框架(Ant Design、shadcn/ui、Radix 等 — 保留) - 图表库、动画库等 — 保留并确认兼容性 - 框架特定依赖(Next.js、Vercel — 移除) 5. **静态资源盘点**(对大型项目尤其重要): - 图片:格式、数量、总大小、引用方式(import / public / CDN) - 字体:本地文件 vs CDN 引用 - 视频/音频等其他媒体 - SVG:是否作为 React 组件使用 输出一份转换清单,列出需要处理的各项转换任务。 ### 第 2 步:创建 Figma Make 项目结构 在目标目录 `//` 下创建以下固定结构;最终可消费产物同步到 `.axhub/make/artifacts/figma//`: ```text / ├── index.tsx # 项目主入口 ├── style.css # 根入口样式(桥接层) ├── canvas.fig # Figma 二进制设计数据(第 5 步生成) ├── meta.json # 项目元数据 ├── ai_chat.json # AI 聊天历史(可为空 {}) ├── canvas.code-manifest.json # CODE_FILE 索引清单(第 5 步生成) ├── package.json # Vite 项目依赖声明 ├── vite.config.ts # Vite 构建配置 ├── index.html # Vite HTML 入口 ├── images/ # 设计稿图片资源 └── src/ ├── App.tsx # Figma Make 导出薄壳(导出入口) ├── main.tsx # Vite 挂载层 ├── index.css # Figma Make 入口样式 ├── components/ # 可选;仅使用 template manifest 已存在的路径 ├── pages/ # 多页面(如需要) └── styles/ └── globals.css # 全局样式 / 设计 Token ``` `.axhub/make/project.json` 中的资源元数据示例: ```json { "artifacts": { "figma": { "resourceId": "home", "canvasFigPath": ".axhub/make/artifacts/figma/home/canvas.fig", "metaPath": ".axhub/make/artifacts/figma/home/meta.json", "aiChatPath": ".axhub/make/artifacts/figma/home/ai_chat.json", "codeManifestPath": ".axhub/make/artifacts/figma/home/canvas.code-manifest.json", "imagesDir": ".axhub/make/artifacts/figma/home/images", "manifestPath": ".axhub/make/artifacts/figma/home/manifest.json" } } } ``` **职责分层约束**(这是验收约束,不是建议): | 文件 | 职责 | 不应包含 | |------|------|----------| | `index.tsx` | 项目主入口 | 页面视觉实现 | | `src/App.tsx` | Figma 导出薄壳,复用共享组件 | 独立于根入口的业务逻辑 | | `src/main.tsx` | Vite 开发挂载,`ReactDOM.createRoot` | 业务代码 | | `src/components/**` | 页面视觉与交互的真实主体 | 入口适配逻辑 | | `style.css` | 根入口样式转发 | 大量业务样式 | | `src/index.css` | Figma 入口样式层 | 独立于 style.css 的独立样式 | ### 第 3 步:迁移和改造源代码 #### 3.1 收敛入口 将源项目的所有页面逻辑收敛为两个薄入口 + 一套共享组件。 **根目录 `index.tsx`**(项目主入口): ```tsx /** * 项目主入口 */ import './style.css'; import React from 'react'; import App from './src/App'; export default function PageName() { return ; } ``` **`src/App.tsx`**(Figma Make 导出壳): ```tsx /** * Figma Make 导出薄壳 * 将根入口及其本地依赖机械同步到这个入口, * 或同步到 template manifest 中明确存在的其他逻辑路径。 */ import React from 'react'; import './styles/globals.css'; export default function App() { return
{/* 机械同步或打包后的页面主体 */}
; } ``` **`src/main.tsx`**(Vite 挂载层): ```tsx /** * Vite 开发挂载层 * 仅用于 Figma Make 独立开发预览 */ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; ReactDOM.createRoot(document.getElementById('root')!).render( ); ``` **关键原则**: - 两个入口(`index.tsx` 和 `src/App.tsx`)必须渲染相同的页面内容 - 页面主体可以直接机械打包到 `src/App.tsx`,也可以拆分到 template manifest 已存在的逻辑路径 - 不要硬编码 `Dashboard.tsx`、`AppContent.tsx` 或其他项目语义文件名;先检查模板再选择路径 - 不要创建模板中不存在的路径后假设 `pack` 会自动加入 - 若源项目有多页路由,收敛为单页面或选择核心页面 #### 3.2 处理框架特定代码 根据源项目的框架类型,进行针对性清理: **Next.js 项目**: ```typescript // ❌ 移除 "use client" import { useRouter } from 'next/navigation' import Image from 'next/image' import Link from 'next/link' import type { Metadata } from 'next' // ✅ 替换 // 删除 "use client" // 删除 useRouter,改用 useState 管理页面状态 替代 替代 ``` **AI Studio 项目**: ```typescript // ❌ 移除 // Import Map 依赖(CDN URL) // index.html 中的 ``` #### 4.6 `images/` 目录 创建空的 `images/` 目录。此目录用于存放 Figma Make 设计稿关联的图片资源。 - 如果是从 Figma Make 导入的项目,保留 `images/` 下已有的 hash 命名图片 - 源项目的业务图片建议保留在 `src/assets/` 中,不必移入 `images/` ### 第 5 步:生成 `canvas.fig` > 在执行任何带 `--prune-missing` 的命令前,必须先完成导出壳和模板路径映射。不要用正式 `canvas.fig` 直接试错。 使用本技能自带的 `canvas-fig-sync.mjs` 脚本生成可导出的 `.fig` 文件。 以下命令假设你当前在终端中定位在目标项目根目录,且本技能的存放位置为 ``: **首次生成**(没有现有 `canvas.fig`)按以下顺序执行: 1. inspect 空白模板,确认本次准备使用的逻辑路径确实存在。 2. 在目标源码目录内创建并保留模板兼容的 `src/**` 导出壳。 3. 将空白模板复制为候选文件,在候选文件上 pack。 4. 验证候选文件的节点、引用和可运行性,通过后再替换正式 `canvas.fig`。 ```bash # ① 检查模板已有 CODE_FILE 路径 node /scripts/canvas-fig-sync.mjs inspect \ --fig /assets/empty-canvas.fig \ --manifest /template.code-manifest.json # ② 使用 Node 跨平台复制模板,避免覆盖正式产物 node -e "require('node:fs').copyFileSync(process.argv[1], process.argv[2])" \ /assets/empty-canvas.fig \ /canvas.candidate.fig # ③ 将已经对齐模板路径的源码写入候选文件 node /scripts/canvas-fig-sync.mjs pack \ --fig /canvas.candidate.fig \ --from \ --prune-missing \ --sanitize-for-export \ --manifest /canvas.pack-manifest.json # ④ 生成候选文件的最终 CODE_FILE 清单 node /scripts/canvas-fig-sync.mjs inspect \ --fig /canvas.candidate.fig \ --manifest /canvas.code-manifest.json ``` `` 是包含持久化 `src/App.tsx` 的页面源码目录;`` 是 `.axhub/make/artifacts/figma//`。两者通常不是同一个目录。Make 服务端下载时会再次以 `` 执行 pack,因此不能在首次生成后删除导出壳。 在提升候选文件为正式 `canvas.fig` 前,读取两个 manifest 并强制满足: - `canvas.pack-manifest.json.updatedLogicalPathCount > 0` - `canvas.code-manifest.json.summary.totalCodeFiles > 0` - `canvas.code-manifest.json.entries` 包含 `App.tsx` 以及 `App.tsx` 的全部本地相对依赖 - pack warnings 中不存在 `Unresolved relative import` - 除 `App.tsx` 外,只要求本次导出入口实际引用的模板已有路径;不要要求固定存在 `Dashboard.tsx` 任何一项不满足都必须停止,不要覆盖已有 `canvas.fig`,也不要把空壳记录为成功导出。验证通过后再用跨平台文件操作把 `canvas.candidate.fig` 替换为 `canvas.fig`。 **参数说明**: - ``:页面源码目录;脚本会从其 `src/` 子目录读取代码 - ``:Figma Make 资产目录,不等同于源码目录 - `--prune-missing`:裁掉磁盘上不存在的旧 `CODE_FILE` 节点 - `--sanitize-for-export`:清空旧聊天/历史缓存,重建 `importedCodeFiles` **canvas.fig 关键特性**: - `fig-make` 专用二进制容器,内含 Figma 节点树和 CODE_FILE 节点;不是普通 ZIP - 不可手动编辑,仅能通过 `canvas-fig-sync.mjs` 的 pack / inspect / extract 命令操作 - 文件大小通常在几百 KB 到几 MB,取决于代码文件数量 - 每次页面内容变更后需要重新 pack,否则导出的 `.fig` 会与当前页面不一致 ### 第 6 步:验证清单 转换完成后,逐项检查: **结构验证**: - [ ] 项目目录符合固定结构(`index.tsx` + `src/App.tsx` + `src/main.tsx` + `src/components/`) - [ ] `index.tsx` 顶部有职责注释 - [ ] `src/App.tsx` 顶部有职责注释:"Figma Make 导出薄壳" - [ ] `src/main.tsx` 顶部有职责注释:"Vite 挂载层" - [ ] 两个入口渲染同一套 `src/components/**` 中的共享组件 **元文件验证**: - [ ] `meta.json` 存在,包含 `file_name`、`exported_at`、`client_meta` - [ ] `ai_chat.json` 存在(至少为 `{}`) - [ ] `package.json` 存在,不包含 `react` / `react-dom` 依赖 - [ ] `vite.config.ts` 存在 - [ ] `index.html` 存在 - [ ] `images/` 目录存在 **canvas.fig 验证**: - [ ] `canvas.fig` 存在 - [ ] `canvas.code-manifest.json` 存在 - [ ] inspect 命令可成功执行 - [ ] pack manifest 的 `updatedLogicalPathCount > 0` - [ ] `summary.totalCodeFiles > 0` - [ ] manifest 包含入口及其全部本地相对依赖 - [ ] pack warnings 不包含 `Unresolved relative import` - [ ] 用 `extract` 反向提取到临时目录,关键源码与导出壳内容一致 - [ ] 实际下载接口返回的二进制与正式 `canvas.fig` 一致;不能只以 `probe.hasMakeAssets === true` 作为内容验收 **样式验证**: - [ ] `style.css` 和 `src/index.css` 使用同一套样式来源 - [ ] 不直接搬运 Tailwind 构建产物作为最终样式 **依赖验证**: - [ ] 不依赖 `@/` 或 `package@version` 别名才能运行 - [ ] 不残留 Next.js / Vercel 特定代码 - [ ] 所有必要依赖已安装 **资源完整性验证**: - [ ] 所有图片引用路径正确(无 404 断链) - [ ] 字体文件已复制或 CDN 链接可访问 - [ ] 不包含 `node_modules/`、`build/`、`.next/` 等冗余目录 **渲染验证**: 在目标项目目录中: ```bash cd / npm install # 或 yarn / pnpm install npm run dev # 启动 Vite 开发服务器 # 浏览器中检查页面渲染是否正常 ``` - [ ] 页面正常渲染 - [ ] 无控制台错误 - [ ] 主视觉与原项目基本一致 ## 常见场景 ### 场景 A:纯 React + Vite 项目 这是最简单的场景,因为源项目和目标结构都基于 Vite。 1. inspect 模板,确认可用的 `CODE_FILE.logicalPath` 2. 保留原业务入口,创建模板兼容的薄导出壳 3. 将业务主体及本地依赖机械打包到 `src/App.tsx`,或同步到本次选定的模板已有路径 4. 创建根目录 `index.tsx` 5. 迁移样式到 `src/styles/globals.css` 6. 补齐元文件 7. 生成 `canvas.fig` ### 场景 B:Next.js 项目(V0 等) 1. 移除所有 Next.js 特定代码(`"use client"`、`next/image`、`next/link`、路由等) 2. 将 `app/page.tsx` 或 `pages/index.tsx` 的内容迁移到 `src/components/` 3. 将 `app/globals.css` 迁移到 `src/styles/globals.css` 4. 将 `public/` 下的静态资源迁移到 `src/assets/` 或 `images/` 5. 排除 Next.js 专属依赖(`next`、`@vercel/*`),保留 UI 组件依赖 6. 创建双入口(`index.tsx` + `src/App.tsx`),补齐元文件 ### 场景 C:AI Studio 项目 1. 从 `index.html` 的 Import Map 提取依赖,转为 npm 包安装 2. 提取 `