coderio 1.0.1 → 1.0.3-alpha.1

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/README.md CHANGED
@@ -54,12 +54,12 @@ https://github.com/user-attachments/assets/bd0c3f18-e98a-4050-bf22-46b198fadac2
54
54
 
55
55
  CodeRio can be seamlessly integrated into Cursor as a Skill. Simply input a prompt like **"Create a React project and restore this design with high fidelity,"** along with your output directory, Figma URL([Design Link](https://www.figma.com/design/c0UBII8lURfxZIY8W6tSDR/Top-16-Websites-of-2024---Awwwards--Community-?node-id=30-8264&t=FB3Hohq2nsH7ZFts-4)), and Token. The Agent will guide you step-by-step through the page generation process. For Landing Pages, it achieves **high-fidelity restoration**, accurately reproducing images and styles. It also automatically encapsulates reusable components (such as cards) and strictly adheres to **frontend development best practices**.
56
56
 
57
-
58
57
  https://github.com/user-attachments/assets/43817e97-ffd2-40e3-9d33-78ee55b2ec2d
59
58
 
60
59
  ## 🚀 Quick Start
61
60
 
62
61
  ### Option 1: CLI (Recommended 👍🏻)
62
+
63
63
  Best for one-click generation.
64
64
 
65
65
  #### 1. Prerequisites
@@ -79,48 +79,104 @@ npm install -g coderio
79
79
  pnpm add -g coderio
80
80
  ```
81
81
 
82
- > **Note for pnpm v9+ users**: If you see a warning about "Ignored build scripts", run:
83
- >
84
- > ```bash
85
- > pnpm approve-builds
86
- > ```
87
- >
88
- > This allows native dependencies (better-sqlite3) to compile properly.
82
+ > **Note for pnpm v9+ users**: If you see a warning about "Ignored build scripts", run: `pnpm approve-builds` to allow native dependencies (better-sqlite3) to compile properly.
89
83
  >
90
84
  > **Note**: Validation features (e.g., `d2c --mode full`) require optional dependencies `playwright` and `sharp`. They are not bundled with coderio by default to keep installation lightweight. Please install them globally beforehand for smoother execution:
91
85
  >
92
86
  > ```bash
93
87
  > npm install -g playwright sharp
94
88
  > npx playwright install chromium
95
- > ```
89
+ > ```
96
90
 
97
91
  #### 3. Configuration
98
92
 
99
- > **Important**: This tool requires an LLM with **multimodal (vision) capabilities** to analyze design screenshots.
100
- > We highly recommend using **`gemini-3-pro-preview`** for the best balance of performance and cost.
93
+ > **Important**: Requires a **multimodal (vision)** model (Recommended: `gemini-3-pro-preview`).
101
94
 
102
- Create `~/.coderio/config.yaml` (Windows: `%USERPROFILE%\.coderio\config.yaml`) with:
95
+ Create config file at `~/.coderio/config.yaml` (Windows: `%USERPROFILE%\.coderio\config.yaml`):
103
96
 
104
97
  ```yaml
105
98
  model:
106
- provider: openai # anthropic | openai | google
107
- model: gemini-3-pro-preview
108
- baseUrl: https://api.anthropic.com
109
- apiKey: your-api-key-here
99
+ provider: openai # anthropic | openai | google
100
+ model: gemini-3-pro-preview
101
+ baseUrl: https://api.anthropic.com
102
+ apiKey: your-api-key-here
110
103
 
111
104
  figma:
112
- token: your-figma-token-here
105
+ token: your-figma-token-here
113
106
 
114
107
  debug:
115
- enabled: false
108
+ enabled: false # set 'true', if you want to save model and request information
109
+ ```
110
+
111
+ #### 4. Usage
112
+
113
+ ```bash
114
+ # Convert Figma design to code (default mode: code only)
115
+ coderio d2c -s 'https://www.figma.com/design/your-file-id/...'
116
+
117
+ # Full mode: Generate code + visual validation + auto-refinement
118
+ coderio d2c -s 'https://www.figma.com/design/your-file-id/...' -m full
119
+ ```
120
+
121
+ #### 5. Run Your Project
122
+
123
+ ```bash
124
+ # Navigate to generated project
125
+ cd coderio/<design-name_node-id>/my-app
126
+
127
+ # Install dependencies
128
+ pnpm install
129
+
130
+ # Start dev server
131
+ pnpm dev
132
+
133
+ # 🎉 Open http://localhost:5173
134
+ ```
135
+
136
+ #### 6. View Validation Report
137
+
138
+ report path: coderio/<design-name_node-id>/process/validation/index.html
139
+
140
+ #### 📖 All Commands
141
+
142
+ | Command | Alias | Description |
143
+ | ----------------- | ----- | --------------------------------------------------- |
144
+ | `design2code` | `d2c` | Full pipeline: Figma → Protocol → Code → Validation |
145
+ | `design2protocol` | `d2p` | Extract design protocol only |
146
+ | `protocol2code` | `p2c` | Generate code from existing protocol |
147
+ | `validate` | `val` | Run validation on generated code |
148
+ | `images` | - | Download and process Figma assets |
149
+
150
+ ### Option 2: Docker
151
+
152
+ Best for portable environments without Node.js installation.
153
+
154
+ #### 1. Prerequisites
155
+
156
+ - [Docker](https://docs.docker.com/get-docker/)
157
+ - [Figma Personal Access Token](https://www.figma.com/developers/api#access-tokens)
158
+ - LLM API Key ([Anthropic](https://console.anthropic.com/) | [OpenAI](https://platform.openai.com/) | [Google](https://aistudio.google.com/))
159
+
160
+ > **For Windows Users:** The commands below use bash syntax (heredoc, `${PWD}`, `--network=host`, etc.) which are not compatible with CMD or PowerShell. Please use **WSL2** to run them:
161
+ >
162
+ > 1. Install [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) and a Linux distribution (e.g. Ubuntu)
163
+ > 2. Install [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/) and enable **WSL2 integration** in Settings → Resources → WSL Integration
164
+ > 3. Open a WSL2 terminal (run `wsl` in CMD/PowerShell, or open Ubuntu from the Start menu)
165
+ > 4. Run all the following commands inside the WSL2 terminal
166
+
167
+ #### 2. Installation
168
+
169
+ ```bash
170
+ docker pull crpi-p4hwwrt00km3axuk.cn-shanghai.personal.cr.aliyuncs.com/coderio/coderio
116
171
  ```
117
172
 
118
- <details>
119
- <summary><strong>macOS / Linux One-click Command</strong></summary>
173
+ #### 3. Configuration
174
+
175
+ Create a working directory and `config.yaml`:
120
176
 
121
177
  ```bash
122
- mkdir -p ~/.coderio
123
- cat > ~/.coderio/config.yaml << 'EOF'
178
+ mkdir -p ./coderio-app && cd ./coderio-app
179
+ cat > config.yaml << 'EOF'
124
180
  model:
125
181
  provider: openai # anthropic | openai | google
126
182
  model: gemini-3-pro-preview
@@ -134,10 +190,19 @@ debug:
134
190
  enabled: false
135
191
  EOF
136
192
  ```
137
- </details>
138
193
 
139
194
  #### 4. Usage
140
195
 
196
+ ```bash
197
+ docker run -ti --rm \
198
+ --network=host \
199
+ -v ${PWD}:/app \
200
+ -v ./config.yaml:/root/.coderio/config.yaml \
201
+ crpi-p4hwwrt00km3axuk.cn-shanghai.personal.cr.aliyuncs.com/coderio/coderio bash
202
+ ```
203
+
204
+ Once inside the container, use CodeRio commands:
205
+
141
206
  ```bash
142
207
  # Convert Figma design to code (default mode: code only)
143
208
  coderio d2c -s 'https://www.figma.com/design/your-file-id/...'
@@ -163,37 +228,29 @@ pnpm dev
163
228
 
164
229
  #### 6. View Validation Report
165
230
 
166
- ```bash
167
- # Open validation report in browser
168
- open coderio/<design-name_node-id>/process/validation/index.html
169
- ```
231
+ Generated files are mounted to your host machine. Open the validation report in your browser:
170
232
 
171
- #### 📖 All Commands
233
+ ```
234
+ ./coderio/<design-name_node-id>/process/validation/index.html
235
+ ```
172
236
 
173
- | Command | Alias | Description |
174
- | ----------------- | ----- | --------------------------------------------------- |
175
- | `design2code` | `d2c` | Full pipeline: Figma → Protocol → Code → Validation |
176
- | `design2protocol` | `d2p` | Extract design protocol only |
177
- | `protocol2code` | `p2c` | Generate code from existing protocol |
178
- | `validate` | `val` | Run validation on generated code |
179
- | `images` | - | Download and process Figma assets |
237
+ ### Option 3: Skill (Portable Embedded Workflow)
180
238
 
181
- ### Option 2: Skill (Portable Embedded Workflow)
182
239
  Best for control and precision using AI Agents.
183
240
 
184
241
  **Prerequisites**:
185
242
  Copy the Skill file to your Cursor configuration directory:
186
- ```bash
187
- mkdir -p ~/.cursor/skills/design-to-code
188
- cp docs/skills/SKILL.md ~/.cursor/skills/design-to-code/SKILL.md
189
- ```
243
+
244
+ Copy `skills\design-to-code` folder to `~\.cursor\skills` (Windows: `%USERPROFILE%\.cursor\skills`)
190
245
 
191
246
  **Using in Cursor**:
192
- 1. Open Cursor Chat (`Cmd` + `L`).
247
+
248
+ 1. Open Cursor Chat.
193
249
  2. Type: **"Use design-to-code skill to convert this design: [Your Figma URL]"**
194
250
  3. The Agent will guide you step-by-step through protocol extraction and code generation.
195
251
 
196
252
  **Using in Claude Code**:
253
+
197
254
  1. Start Claude Code.
198
255
  2. Type: **"Read docs/skills/SKILL.md and perform design conversion: [Your Figma URL]"**
199
256
 
package/dist/cli.js CHANGED
@@ -475,7 +475,7 @@ var AGENT_CONTEXT_WINDOW_TOKENS = 128e3;
475
475
 
476
476
  // src/cli/init.ts
477
477
  function registerCommands(program) {
478
- const version = false ? "0.0.1" : "1.0.1";
478
+ const version = false ? "0.0.1" : "1.0.3-alpha.1";
479
479
  program.name(CLI_NAME).description(`${CLI_NAME} - Convert Figma designs to code`).version(version, "-v, -V, --version", "Output the version number").showHelpAfterError();
480
480
  }
481
481
 
@@ -594,16 +594,37 @@ var Workspace = class {
594
594
  }
595
595
  /**
596
596
  * Delete all files and directories inside the workspace
597
+ * @param workspace - The workspace structure
598
+ * @param preserve - Optional list of relative paths (from workspace root) to preserve from deletion
597
599
  */
598
- deleteWorkspace(workspace) {
600
+ deleteWorkspace(workspace, preserve = []) {
599
601
  try {
600
- if (fs2.existsSync(workspace.root)) {
601
- const entries = fs2.readdirSync(workspace.root);
602
- for (const entry of entries) {
603
- const fullPath = path2.join(workspace.root, entry);
604
- fs2.rmSync(fullPath, { recursive: true, force: true });
602
+ if (!fs2.existsSync(workspace.root)) return;
603
+ const preserveFiles = new Set(preserve.map((p) => path2.normalize(p)));
604
+ const preserveDirs = /* @__PURE__ */ new Set();
605
+ for (const p of preserveFiles) {
606
+ let dir = path2.dirname(p);
607
+ while (dir !== ".") {
608
+ preserveDirs.add(dir);
609
+ dir = path2.dirname(dir);
605
610
  }
606
611
  }
612
+ const deleteRecursive = (dirPath, relativeTo = "") => {
613
+ const entries = fs2.readdirSync(dirPath);
614
+ for (const entry of entries) {
615
+ const fullPath = path2.join(dirPath, entry);
616
+ const relPath = relativeTo ? path2.join(relativeTo, entry) : entry;
617
+ if (preserveFiles.has(relPath)) {
618
+ continue;
619
+ }
620
+ if (preserveDirs.has(relPath)) {
621
+ deleteRecursive(fullPath, relPath);
622
+ } else {
623
+ fs2.rmSync(fullPath, { recursive: true, force: true });
624
+ }
625
+ }
626
+ };
627
+ deleteRecursive(workspace.root);
607
628
  } catch (error) {
608
629
  const errorMessage = error instanceof Error ? error.message : String(error);
609
630
  logger.printWarnLog(`Failed to delete workspace: ${errorMessage}`);
@@ -653,7 +674,7 @@ function saveDebugLog(requestInfo, responseInfo) {
653
674
  "------------response------------",
654
675
  JSON.stringify(responseInfo, null, 2)
655
676
  ].join("\n");
656
- writeFile(workspaceManager.path?.debug ?? "", `fetch_${(/* @__PURE__ */ new Date()).toISOString()}.md`, debugContent);
677
+ writeFile(workspaceManager.path?.debug ?? "", `fetch_${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}.md`, debugContent);
657
678
  }
658
679
  async function get(url, config) {
659
680
  const response = await axios.get(url, config);
@@ -1418,10 +1439,13 @@ var FigmaTool = class {
1418
1439
  }
1419
1440
  const document2 = await fetchFigmaNode(fileId, nodeId, token);
1420
1441
  if (!document2 || !document2?.children?.length) {
1421
- return void 0;
1442
+ throw new Error("Failed to fetch Figma document");
1422
1443
  }
1423
1444
  const images = await fetchFigmaImages(fileId, nodeId, token);
1424
1445
  const thumbnail = images?.[nodeId] || "";
1446
+ if (!thumbnail) {
1447
+ throw new Error("Failed to fetch Figma document thumbnail");
1448
+ }
1425
1449
  document2.thumbnailUrl = thumbnail;
1426
1450
  const cleanedDocument = cleanFigma(document2);
1427
1451
  return cleanedDocument;
@@ -1553,7 +1577,7 @@ async function callModel(options) {
1553
1577
  "------------response------------",
1554
1578
  JSON.stringify(message.text, null, 2)
1555
1579
  ].join("\n");
1556
- writeFile(workspaceManager.path?.debug ?? "", `model_${(/* @__PURE__ */ new Date()).toISOString()}.md`, debugContent);
1580
+ writeFile(workspaceManager.path?.debug ?? "", `model_${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}.md`, debugContent);
1557
1581
  }
1558
1582
  return message.text;
1559
1583
  } catch (error) {
@@ -2012,7 +2036,7 @@ var parseFigmaUrl = (url) => {
2012
2036
  if (!fileId || !nodeId) {
2013
2037
  throw new Error("Invalid Figma URL");
2014
2038
  }
2015
- return { fileId, name, nodeId, projectName: `${name}_${nodeId}` };
2039
+ return { fileId, name, nodeId, projectName: `${name}_${nodeId.replace(/:/g, "_")}` };
2016
2040
  };
2017
2041
 
2018
2042
  // src/cli/d2p.ts
@@ -5706,21 +5730,45 @@ var FILE_NAMING_CONVENTION = `
5706
5730
  - NEVER use PascalCase or other names for filenames (e.g., DO NOT use \`MainFrame.tsx\` or \`Button.tsx\`).`;
5707
5731
  var OUTPUT_FORMAT = `
5708
5732
  <output_format>
5709
- If only one file (TSX) is needed:
5733
+ **CRITICAL - Output Format Requirements:**
5734
+
5735
+ **CASE 1: Single File (TSX only)**
5736
+ - Return code wrapped in triple backticks with language identifier
5737
+ - NO file name header needed
5738
+ - Example:
5710
5739
  \`\`\`tsx
5711
- // code...
5740
+ export default function Component() {
5741
+ return <div>...</div>;
5742
+ }
5712
5743
  \`\`\`
5713
5744
 
5714
- If multiple files are needed (e.g., TSX + Styles):
5745
+ **CASE 2: Multiple Files (TSX + Styles)**
5746
+ - **REQUIRED**: Each file MUST start with EXACTLY \`## \` (two hash symbols + one space) followed by filename
5747
+ - **REQUIRED**: Filename must be complete with extension (e.g., \`index.tsx\`, \`index.module.css\`)
5748
+ - **FORBIDDEN**: Do NOT use single \`#\`, do NOT omit filename, do NOT use other markers
5749
+ - Follow this exact structure:
5750
+
5715
5751
  ## index.tsx
5716
5752
  \`\`\`tsx
5717
- // code...
5753
+ export default function Component() {
5754
+ return <div>...</div>;
5755
+ }
5718
5756
  \`\`\`
5719
5757
 
5720
5758
  ## index.module.[css|less|scss]
5721
5759
  \`\`\`[css|less|scss]
5722
- // styles...
5760
+ .container {
5761
+ /* styles */
5762
+ }
5723
5763
  \`\`\`
5764
+
5765
+ **VALIDATION CHECKLIST (for multiple files):**
5766
+ \u2713 Each file section starts with \`## \` (two hashes + space)
5767
+ \u2713 Filename includes full extension
5768
+ \u2713 Code wrapped in triple backticks with language
5769
+ \u2717 DO NOT use \`# filename\` (single hash)
5770
+ \u2717 DO NOT omit file headers
5771
+ \u2717 DO NOT use other separators
5724
5772
  </output_format>`;
5725
5773
  function generateChildrenPropsInstructions(modes) {
5726
5774
  const instructions = [];
@@ -6333,6 +6381,14 @@ async function checkpointExists(checkpointer, threadId) {
6333
6381
  return false;
6334
6382
  }
6335
6383
  }
6384
+ async function clearCheckpoint(checkpointer, threadId) {
6385
+ try {
6386
+ await checkpointer.deleteThread(threadId);
6387
+ } catch (error) {
6388
+ const errorMessage = error instanceof Error ? error.message : String(error);
6389
+ logger.printWarnLog(`Failed to clear checkpoint: ${errorMessage}`);
6390
+ }
6391
+ }
6336
6392
  async function promptCheckpointChoice(checkpointer, threadId) {
6337
6393
  const hasCheckpoint = await checkpointExists(checkpointer, threadId);
6338
6394
  if (!hasCheckpoint) {
@@ -6341,6 +6397,9 @@ async function promptCheckpointChoice(checkpointer, threadId) {
6341
6397
  const choice = await promptUserChoice();
6342
6398
  return choice === "resume";
6343
6399
  }
6400
+ async function clearThreadCheckpoint(checkpointer, threadId) {
6401
+ await clearCheckpoint(checkpointer, threadId);
6402
+ }
6344
6403
  function initializeSqliteSaver(dbPath) {
6345
6404
  const dbDir = path14.dirname(dbPath);
6346
6405
  if (!fs11.existsSync(dbDir)) {
@@ -6356,13 +6415,13 @@ async function design2code(url, mode) {
6356
6415
  const urlInfo = parseFigmaUrl(url);
6357
6416
  const threadId = urlInfo.projectName;
6358
6417
  const workspace = workspaceManager.initWorkspace(threadId);
6359
- let checkpointer = initializeSqliteSaver(workspace.db);
6418
+ const checkpointer = initializeSqliteSaver(workspace.db);
6360
6419
  const resume = await promptCheckpointChoice(checkpointer, threadId);
6361
6420
  logger.printInfoLog(`Starting design-to-code process for: ${urlInfo.projectName}`);
6362
6421
  if (resume !== true) {
6363
- workspaceManager.deleteWorkspace(workspace);
6422
+ workspaceManager.deleteWorkspace(workspace, ["checkpoint/coderio-cli.db"]);
6364
6423
  logger.printInfoLog("Starting fresh...");
6365
- checkpointer = initializeSqliteSaver(workspace.db);
6424
+ await clearThreadCheckpoint(checkpointer, threadId);
6366
6425
  } else {
6367
6426
  logger.printInfoLog("Resuming from cache...");
6368
6427
  }