coderio 1.0.1 → 1.0.2
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 +18 -48
- package/dist/cli.js +27 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nodes/process/structure/prompt.ts","../src/types/graph-types.ts","../src/types/figma-types.ts","../src/agents/initial-agent/prompt.ts","../src/agents/initial-agent/instruction.ts","../src/utils/url-parser.ts","../src/tools/figma-tool/index.ts","../src/utils/axios.ts","../src/utils/config.ts","../src/utils/file.ts","../src/utils/logger.ts","../src/utils/workspace.ts","../src/tools/figma-tool/figma.ts","../src/tools/figma-tool/images.ts","../src/utils/promise-pool.ts","../src/tools/figma-tool/constants.ts","../src/tools/style-tool/index.ts","../src/tools/style-tool/color.ts","../src/tools/style-tool/utils.ts","../src/utils/call-model.ts","../src/nodes/process/structure/index.ts","../src/nodes/process/structure/utils.ts","../src/utils/naming.ts","../src/utils/parser.ts","../src/nodes/process/index.ts","../src/skill-api.ts","../src/nodes/code/prompt.ts","../src/nodes/code/constants.ts","../src/nodes/code/utils.ts","../src/utils/code-cache.ts"],"sourcesContent":["/**\n * Prompt template for structure generation.\n * Matches the style used in agents/initial-agent/prompt.ts.\n */\nexport const generateStructurePrompt = (options: { positions: string; width?: string }) => {\n const { positions, width } = options;\n const imageWidth = width ?? '1440';\n return `\n<system_instructions>\n <task>Generate a semantic React component structure from Figma design data.</task>\n\n <input_context>\n <positions_json>${positions}</positions_json>\n <image_width>${imageWidth}px</image_width>\n <visual_reference>Refer to the attached image for hierarchy and grouping.</visual_reference>\n </input_context>\n\n <rules>\n <rule name=\"Hierarchy Strategy\">\n 1. Compare 'positions_json' nesting with visual grouping.\n 2. If data contradicts visual layout, IGNORE data nesting and restructure based on VISUAL proximity.\n </rule>\n\n <rule name=\"Pattern Recognition (CRITICAL)\">\n 1. **Semantic Grouping**: Group by function (e.g., \"PricingCard\", \"UserProfile\").\n 2. **Grid/List Detection**:\n - Detect repeating identical siblings (e.g., 6 Cards).\n - Create a SINGLE container (e.g., \"CardGrid\") instead of \"Card1\", \"Card2\".\n - **MERGE IDs**: The container's \\`elementIds\\` MUST include ALL children IDs of all items in the grid to ensure complete style extraction.\n - **Reusable Instances with \\`data.componentName\\`**:\n - For each visually repeated item, keep a unique \\`id\\` / \\`data.name\\` (e.g., \"TaskCard1\", \"TaskCard2\", ...).\n - Set the SAME \\`data.componentName\\` for all these items (e.g., \"TaskCard\") to mark them as instances of one reusable component.\n - Example: 12 task cards → each child has different \\`id\\` / \\`data.name\\` (\"TaskCard1\"... \"TaskCard12\"), but the SAME \\`data.componentName\\`: \"TaskCard\".\n 3. **Fragmentation**: Avoid creating components for single atoms (Text/Icon) unless reusable (Button/Chip).\n 4. **Monoliths**: Split sections >1000px height.\n </rule>\n\n <rule name=\"Component Granularity (CRITICAL - Prevent Over-Fragmentation)\">\n 1. **Maximum Depth = 2**:\n - Component tree should NOT exceed 2 levels deep (root -> children -> grandchildren is the MAX).\n - If you find yourself creating a 3rd level, STOP and merge it into the parent instead.\n\n 2. **Minimum Element Threshold**:\n - Each component MUST contain at least 5+ elements in its \\`elementIds\\`.\n - If a potential component has fewer than 5 elements, MERGE it into its parent or sibling.\n - This prevents creating tiny components like \"LegalLinks\" (3 links) or \"CommunityLinks\" (2 links).\n\n 3. **Component Count Limit**:\n - For a single section, create at most 2-3 child components.\n - Example: Footer → FooterTop, FooterBottom (2 children, NOT 4-5 like \"Logos\", \"Links\", \"QRCode\", etc.)\n\n 4. **DO NOT Create Separate Components For**:\n - A group of links (merge into parent section's elementIds)\n - A group of logos or icons (merge into parent section's elementIds)\n - Single text, image, icon, divider, link\n - Any group with < 5 elements\n These should ALL be included in parent's \\`elementIds\\`.\n\n 5. **Merge by Visual Region, NOT by Function**:\n - Divide by visual position (Top/Bottom, Left/Right), NOT by element type (Links/Logos/QRCode).\n - Example: FooterTop contains logos + links + icons together (all elements in that visual area).\n </rule>\n\n <rule name=\"Naming & ID\">\n - \\`id\\` MUST match \\`data.name\\` (PascalCase, Unique).\n - **Containers**: \\`elementIds\\` = container background/frame IDs only.\n - **Leaves**: \\`elementIds\\` = component's direct IDs + ALL nested children IDs.\n - **Integrity**: Every input ID must be assigned to exactly one component.\n </rule>\n\n <rule name=\"Layout Data\">\n For EACH node, calculate:\n - \\`boundingBox\\`: {top, left, width, height} (Absolute)\n - \\`relativeBoundingBox\\`: {top, left, width, height} (Relative to parent)\n - \\`spacing\\`: {next: number} (Distance to next sibling)\n - \\`layoutDirection\\`: \"VERTICAL\" | \"HORIZONTAL\" | \"NONE\"\n **HOW TO DETERMINE layoutDirection (ESPECIALLY FOR ROOT NODE)**:\n 1. Look at ALL direct children's \\`boundingBox.left\\` values\n 2. **If children have 2+ DIFFERENT \\`left\\` values** (difference > 50px):\n → Set \\`layoutDirection = \"HORIZONTAL\"\\`\n → Example: Sidebar at left=0, Content at left=240 → HORIZONTAL\n 3. **If ALL children have SAME \\`left\\` value** (within ±50px tolerance):\n → Set \\`layoutDirection = \"VERTICAL\"\\`\n → Example: Header at left=0, Content at left=0, Footer at left=0 → VERTICAL\n 4. Common patterns:\n - **Sidebar Layout**: Sidebar (left=0) + Main Content (left=240+) → ROOT is HORIZONTAL\n - **Stacked Layout**: All sections at same left position → ROOT is VERTICAL\n </rule>\n </rules>\n\n <output_format>\n Return ONLY a single valid JSON object (no markdown code blocks).\n Output MUST be COMPACT (no pretty-printing): avoid indentation/newlines as much as possible.\n Follow this schema exactly:\n {\n \"id\": \"ComponentName\",\n \"data\": {\n \"name\": \"ComponentName\",\n \"componentName\": \"OptionalReusableComponentName\",\n \"purpose\": \"string\",\n \"elementIds\": [\"id1\", \"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": []\n }\n\n layoutDirection MUST be one of: \"VERTICAL\" | \"HORIZONTAL\" | \"NONE\"\n \n **Layout Groups**:\n - Do NOT generate \\`layoutGroups\\` field in your output\n - This field will be automatically added by post-processing if needed\n\n **CRITICAL: 2-Level Maximum Structure**\n\n Your output MUST follow a maximum 2-level structure. Level 2 nodes should have \\`children: []\\`.\n All primitive elements (single text, links, logos, icons, dividers) go into \\`elementIds\\`, NOT as child components.\n\n Example for ANY section:\n {\n \"id\": \"SectionName\",\n \"data\": {\n \"name\": \"SectionName\",\n \"purpose\": \"Main section description\",\n \"elementIds\": [\"container-id\"],\n \"layout\": {...}\n },\n \"children\": [\n {\n \"id\": \"SubSectionA\",\n \"data\": {\n \"name\": \"SubSectionA\",\n \"purpose\": \"Visual area with links, logos, text\",\n \"elementIds\": [\"all\", \"element\", \"ids\", \"including\", \"links\", \"logos\", \"text\"],\n \"layout\": {...}\n },\n \"children\": [] // MUST be empty - all elements via elementIds\n },\n {\n \"id\": \"SubSectionB\",\n \"data\": {\n \"name\": \"SubSectionB\",\n \"purpose\": \"Another visual area\",\n \"elementIds\": [\"all\", \"element\", \"ids\", \"in\", \"this\", \"area\"],\n \"layout\": {...}\n },\n \"children\": [] // MUST be empty\n }\n ]\n }\n\n For reusable component instances that share the same \\`data.componentName\\` but have different \\`id\\` and \\`data.name\\`, an additional expected example is:\n {\n \"id\": \"TaskCardGrid\",\n \"data\": {\n \"name\": \"TaskCardGrid\",\n \"purpose\": \"Grid container for task cards\",\n \"elementIds\": [\"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": [\n {\n \"id\": \"TaskCard1\",\n \"data\": {\n \"name\": \"TaskCard1\",\n \"componentName\": \"TaskCard\",\n \"purpose\": \"Single task card\",\n \"elementIds\": [\"id1\", \"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": []\n }\n ]\n }\n </output_format>\n</system_instructions>\n`.trim();\n};\n\n/**\n * Prompt template for extracting data list and props schema.\n */\nexport const extractDataListPrompt = (options: { containerName: string; childComponentName: string; figmaData: string }) => {\n const { containerName, childComponentName, figmaData } = options;\n return `\nYou are an expert Frontend Developer.\nYou are analyzing a container component \"${containerName}\" which contains a list of \"${childComponentName}\" components.\n\nYour task is to:\n1. Generate a **props schema** (formal parameter definitions) based on the first component instance\n2. Extract the **data array** (actual parameter values) for all component instances from the provided Figma structure data\n\nContext:\n- Container: ${containerName}\n- Child Component: ${childComponentName}\n- Figma Data:\n${figmaData}\n\nInstructions:\n1. Analyze the \"children\" in the Figma Data. Identify those that are instances of \"${childComponentName}\".\n2. For each instance, extract ALL differing content, including:\n - **Text**: Extract the **EXACT** text content found in the \"characters\" fields. Do NOT summarize or generate placeholders.\n - **Images/Icons**: Identify nodes where \\`type\\` is \"IMAGE\". These nodes will have a \\`url\\` field.\n - If a node has \\`isIcon: true\\` field, or the node name contains \"icon\", or the url ends with \".svg\", use \\`iconSrc\\` as the key.\n - Otherwise, use \\`imageSrc\\` or \\`avatarSrc\\` based on context (e.g., avatar for profile pictures).\n - **Layout/Variants**: \n - If the component has \\`layoutDirection: \"HORIZONTAL\"\\` in the layoutInfo, and contains both text and image content, determine the image position:\n - Check the \\`absoluteBoundingBox\\` positions: if image's x position is less than text's x position, image is on the **left**; otherwise on the **right**.\n - Extract as \\`imagePosition: \"left\"\\` or \\`imagePosition: \"right\"\\` prop.\n - Any other component properties or visual variants (e.g. \"variant\": \"filled\", \"active\": true).\n3. **Normalize the data keys (CRITICAL)**:\n - For text content: use \\`title\\`, \\`description\\`, \\`label\\`, etc.\n - For images/icons: ALWAYS use \\`iconSrc\\`, \\`imageSrc\\`, \\`avatarSrc\\` (with \"Src\" suffix)\n - **DO NOT** use ambiguous keys like \\`icon\\`, \\`image\\`, \\`avatar\\` alone\n - This ensures compatibility with standard React component prop naming conventions.\n - Example:\n \\`\\`\\`json\n {\n \"title\": \"Example Title\",\n \"description\": \"Example description\",\n \"imageSrc\": \"@/assets/actual-filename-from-figma.png\"\n }\n \\`\\`\\`\n4. **Generate Props Schema (CRITICAL)**:\n - Based on the first component instance, infer the prop definitions\n - For each extracted field (e.g., \"title\", \"description\", \"imageSrc\"), determine:\n * **key**: The property name (e.g., \"title\")\n * **type**: The TypeScript type - must be one of: \"string\", \"number\", \"boolean\", \"string[]\", \"number[]\"\n * **description**: A clear description of what this prop represents\n - Return as \"props\" array with objects containing { key, type, description }\n - Example:\n \\`\\`\\`json\n {\n \"key\": \"title\",\n \"type\": \"string\",\n \"description\": \"Card title text\"\n }\n \\`\\`\\`\n5. **CRITICAL Rules**:\n - **USE ACTUAL DATA ONLY**: The example above uses placeholder names. You MUST use the actual \"characters\" and \"url\" values from the Figma Data provided.\n - **NO FIGMA IDs**: Do NOT include Figma node IDs (like \"1:2859\") in the output. Only extract actual content data.\n - **NO PLACEHOLDERS**: Do NOT generate fake text or copy paths from examples. If a node does not have a \"url\" field, do not include an \"imageSrc\" property.\n - **Deep Search**: Text nodes might be nested deeply inside Frames/Groups. Look recursively for \"characters\" fields.\n - **Layout Information**: The Figma Data includes \\`layoutInfo\\` array with \\`layoutDirection\\` and \\`absoluteBoundingBox\\` for each component instance. Use this to determine layout-related props like image position (left/right) in horizontal layouts.\n - **Asset Paths - CRITICAL**: \n - Images are represented by nodes with \\`type: \"IMAGE\"\\`. These nodes MUST have a \\`url\\` field.\n - You MUST use the EXACT value from the \\`url\\` field as the value for \"imageSrc\", \"iconSrc\", or \"avatarSrc\" without any modifications.\n - The \\`url\\` field value MUST be a file path (e.g., \"@/assets/icon-name.svg\").\n - **DO NOT hallucinate or copy paths from the example above.** Every image MUST correspond to a node in the provided Figma Data.\n - CORRECT: If a node has \\`type: \"IMAGE\"\\` and \\`url: \"@/assets/real-image-123.png\"\\`, use exactly that.\n - WRONG: Using \"@/assets/start-2.svg\" if it's not in the input data.\n - If the \\`url\\` field does not exist or does not contain a valid path starting with \"@/assets/\", omit the iconSrc/imageSrc/avatarSrc field entirely.\n6. Return a JSON object with two keys:\n - \"props\": Array of prop definitions with { key, type, description }\n - \"state\": Array of data objects for each component instance\n7. Return ONLY the JSON object. Do not explain.\n\nExample Output JSON Structure (for reference only):\n{\n \"props\": [\n {\n \"key\": \"title\",\n \"type\": \"string\",\n \"description\": \"Card title text\"\n },\n {\n \"key\": \"description\",\n \"type\": \"string\",\n \"description\": \"Card description text\"\n },\n {\n \"key\": \"imageSrc\",\n \"type\": \"string\",\n \"description\": \"Path to card image\"\n }\n ],\n \"state\": [\n {\n \"title\": \"Actual Title 1\",\n \"description\": \"Actual Description 1\",\n \"imageSrc\": \"@/assets/actual-file-1.png\"\n },\n {\n \"title\": \"Actual Title 2\",\n \"description\": \"Actual Description 2\",\n \"imageSrc\": \"@/assets/actual-file-2.png\"\n }\n ]\n}\n`.trim();\n};\n","/* Enum representing the nodes in the LangGraph. */\nexport enum GraphNode {\n INITIAL = 'initial',\n PROCESS = 'process',\n VALIDATION = 'validation',\n DATA = 'data',\n CODE = 'code',\n}\n\nexport enum ValidationMode {\n CodeOnly = 'codeOnly',\n ReportOnly = 'reportOnly',\n Full = 'full',\n}\n\nexport interface GlobalFigmaInfo {\n thumbnail: string;\n}\n\n/**\n * Validation-specific configuration.\n * Controls validation behavior in the workflow.\n */\nexport interface ValidationConfig {\n /**\n * Validation execution mode.\n * - codeOnly: generate component code without validation (only commit)\n * - reportOnly: run a single validation pass and generate a report (no code edits)\n * - full: run iterative actor-critic refinement loop (may edit code + commit markers)\n */\n validationMode?: ValidationMode;\n}\n","/**\n * Figma API Types\n *\n * Type definitions for Figma design data structures.\n * These types represent the data fetched from Figma REST API and used throughout the protocol generation process.\n */\n\n/* URL and File Information */\n\n/**\n * Parsed Figma URL information\n */\nexport interface FigmaUrlInfo {\n /** The ID of the Figma file */\n fileId: string | null;\n /** The name of the Figma file */\n name: string;\n /** The ID of the Figma node */\n nodeId: string | null;\n /** The name of the project */\n projectName: string | null;\n}\n\n/* Color and Visual Properties */\n\n/**\n * RGBA color representation\n */\nexport interface FigmaColor {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/**\n * Gradient stop definition\n */\nexport interface FigmaGradientStop {\n color: FigmaColor;\n position: number;\n}\n\n/**\n * Figma paint/fill object\n * Supports solid colors, gradients, and images\n */\nexport interface FigmaColorObject {\n blendMode?: string;\n visible?: boolean;\n type: string;\n color?: FigmaColor;\n opacity?: number;\n gradientStops?: FigmaGradientStop[];\n gradientHandlePositions?: { x: number; y: number }[];\n imageRef?: string;\n}\n\n/* Position and Layout */\n\n/**\n * Position and size in Figma coordinate space\n */\nexport interface FigmaPositionAndSize {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/* Typography */\n\n/**\n * Text styling properties from Figma\n */\nexport interface FigmaTextStyle {\n fontFamily: string;\n fontPostScriptName: string;\n fontStyle: string;\n fontWeight: number;\n textAutoResize: string;\n fontSize: number;\n textAlignHorizontal: string;\n textAlignVertical: string;\n letterSpacing: number;\n lineHeightPx: number;\n lineHeightPercent: number;\n lineHeightPercentFontSize: number;\n lineHeightUnit: string;\n}\n\n/* CSS Conversion */\n\n/**\n * Computed CSS styles converted from Figma properties\n * Used for direct style application in generated components\n */\nexport interface CSSStyles {\n position?: string;\n top?: string;\n left?: string;\n width?: string;\n height?: string;\n display?: string;\n flexDirection?: string;\n justifyContent?: string;\n alignItems?: string;\n gap?: string;\n padding?: string;\n paddingLeft?: string;\n paddingRight?: string;\n paddingTop?: string;\n paddingBottom?: string;\n backgroundColor?: string;\n backgroundImage?: string;\n background?: string;\n border?: string;\n borderRadius?: string;\n boxShadow?: string;\n opacity?: string | number;\n backdropFilter?: string;\n color?: string;\n fontSize?: string;\n fontFamily?: string;\n fontWeight?: string;\n lineHeight?: string;\n letterSpacing?: string;\n textAlign?: string;\n overflow?: string;\n [key: string]: string | number | undefined;\n}\n\n/* Frame/Node Structure */\n\n/**\n * Figma frame/node data structure\n * Represents a complete Figma design node with all its properties and children\n */\nexport interface FigmaFrameInfo {\n id: string;\n name: string;\n type: string;\n url?: string;\n thumbnailUrl?: string;\n visible?: boolean;\n scrollBehavior?: string;\n children?: FigmaFrameInfo[];\n characters?: string;\n frames?: FigmaFrameInfo[];\n blendMode?: string;\n clipsContent?: boolean;\n background?: FigmaColorObject[];\n fills?: FigmaColorObject[];\n strokes?: FigmaColorObject[];\n strokeWeight?: number;\n cornerRadius?: number;\n rectangleCornerRadii?: number[];\n strokeAlign?: string;\n backgroundColor?: FigmaColor;\n absoluteBoundingBox: FigmaPositionAndSize;\n absoluteRenderBounds?: FigmaPositionAndSize;\n constraints?: {\n vertical: string;\n horizontal: string;\n };\n exportSettings?: [\n {\n suffix: string;\n format: FigmaImageFormat;\n constraint: {\n type: string;\n value: number;\n };\n },\n ];\n style?: FigmaTextStyle;\n inlineStyles?: CSSStyles;\n effects?: {\n type: string; // 'DROP_SHADOW' | 'INNER_SHADOW' | 'LAYER_BLUR' | 'BACKGROUND_BLUR'\n visible?: boolean;\n radius: number;\n color?: FigmaColor;\n offset?: {\n x: number;\n y: number;\n };\n spread?: number;\n }[];\n}\n\n/**\n * Supported Figma image export formats\n */\nexport enum FigmaImageFormat {\n PNG = 'png',\n JPG = 'jpg',\n SVG = 'svg',\n PDF = 'pdf',\n EPS = 'eps',\n WEBP = 'webp',\n}\n","/**\n * Generates the system prompt for the InitialAgent.\n * Defines project requirements, tech stack (React + Tailwind V4), and safety constraints.\n *\n * @param options - Configuration options for the prompt generation.\n * @param options.appPath - The target directory path where the project will be scaffolded.\n */\nexport const INITIAL_AGENT_SYSTEM_PROMPT = `\n<system_instructions>\n <task>Scaffold a clean React V18 + TS + Vite + TailwindCSS V4 + Less project.</task>\n\n <input_context>\n - appPath: /absolute/path/to/app\n - appName: document title name\n </input_context>\n\n <requirements>\n <tech_stack>React V18, TypeScript, Vite, TailwindCSS V4, Less</tech_stack>\n\n <directory_constraint>\n CRITICAL: All file operations MUST be performed within the directory specified by appPath.\n When using 'FileEditor.write' or other file tools, you MUST use the full path format: {appPath}/filename.\n </directory_constraint>\n\n <file_specs>\n - \\`.gitignore\\`: Standard gitignore file. MUST include:\n * node_modules/\n * dist/ and build/ directories\n * .env file\n * Editor files (.vscode/*, .DS_Store, etc.)\n * Log files and cache directories\n * Lock files (package-lock.json, yarn.lock, pnpm-lock.yaml)\n - \\`package.json\\`: Basic scripts and dependencies. MUST include \\`tailwindcss\\` (v4) and \\`@tailwindcss/vite\\`.\n * Scripts MUST use \"pnpm exec\" prefix to ensure project dependencies are prioritized:\n - \"dev\": \"pnpm exec vite\"\n - \"build\": \"pnpm exec tsc && pnpm exec vite build\"\n - \"preview\": \"pnpm exec vite preview\"\n * IMPORTANT: Do NOT add \"coderio\" as a dependency - it's only a build tool, not a runtime dependency.\n - \\`vite.config.ts\\`: Configure React and TailwindCSS V4 plugins. MUST include path alias configuration:\n * Add \\`resolve.alias\\` with \\`@\\` pointing to \\`path.resolve(__dirname, './src')\\`\n * Import \\`path\\` from 'node:path'\n - \\`tsconfig.json\\`: Standard React-Vite TS config. MUST include path alias configuration:\n * Add \\`compilerOptions.baseUrl\\` set to \".\"\n * Add \\`compilerOptions.paths\\` with \\`\"@/*\": [\"src/*\"]\\`\n - \\`index.html\\`: Basic template with #root and entry script. set document title to appName.\n - \\`src/main.tsx\\`: Entry point rendering App.\n - \\`src/vite-env.d.ts\\`: MUST include \\`/// <reference types=\"vite/client\" />\\` to support CSS/Less module imports.\n - \\`src/App.tsx\\`: MUST be an empty component (returns null or empty div), NO React import if unused.\n - \\`src/App.less\\`: MUST be an empty file.\n - \\`src/globals.css\\`: ONLY include \\`@import \"tailwindcss\";\\`.\n </file_specs>\n </requirements>\n\n <workflow>\n 1. Use 'FileEditor.write' to create each file listed in <file_specs>, using the format {appPath}/filename for all paths.\n 2. Ensure the directory structure is correct and all files are contained within the appPath directory.\n </workflow>\n\n <final_instruction>\n Create a fully working but MINIMAL project skeleton using Tailwind CSS V4. Use 'FileEditor.write' for all file creations. Use the full path format {appPath}/filename for every file. Do not provide code blocks in chat.\n </final_instruction>\n</system_instructions>\n`.trim();\n","export function initialAgentInstruction(params: { appPath: string; appName: string }): string {\n return `\nappPath: ${params.appPath}\nappName: ${params.appName}\n\nTASK:\nScaffold a clean React V18 + TS + Vite + TailwindCSS V4 + Less project.\n`.trim();\n}\n","import { FigmaUrlInfo } from '../types/figma-types';\n\n/**\n * Parse Figma URL and extract fileId, name, and nodeId in one pass\n * @param url - Figma URL to parse\n * @returns Object containing fileId, name, and nodeId\n * @example\n * parseFigmaUrl('https://www.figma.com/design/aONcu8L82l1PdcT304Q8Za/Intern?node-id=0-495')\n * // Returns: { fileId: 'aONcu8L82l1PdcT304Q8Za', name: 'intern', nodeId: '0-495' }\n */\nexport const parseFigmaUrl = (url: string): FigmaUrlInfo => {\n let fileId: string | null = null;\n let name = 'untitled';\n let nodeId: string | null = null;\n\n try {\n const urlObj = new URL(decodeURIComponent(url));\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n if (pathParts.length >= 3) {\n fileId = pathParts[pathParts.length - 2] || null;\n const fileName = pathParts[pathParts.length - 1];\n name = fileName ? encodeURI(fileName).toLowerCase() : 'untitled';\n name = name.length > 20 ? name.substring(0, 20) : name;\n }\n\n nodeId = urlObj.searchParams.get('node-id') || null;\n nodeId = nodeId ? nodeId.replace(/-/g, ':') : null;\n } catch {}\n\n if (!fileId || !nodeId) {\n throw new Error('Invalid Figma URL');\n }\n\n return { fileId, name, nodeId, projectName: `${name}_${nodeId}` };\n};\n","import { tools } from 'evoltagent';\nimport { checkBorder } from './figma';\nimport { FigmaFrameInfo } from '../../types/figma-types';\nimport { ImageNode } from './types';\nimport { executeDownloadImages, fetchImages, findImageNodes } from './images';\nimport { cleanFigma, fetchFigmaNode, fetchFigmaImages } from './figma';\nimport { styleTool } from '../style-tool';\n\n@tools({\n fetchAndClean: {\n description: 'Fetch and clean Figma document from URL',\n params: [\n { name: 'fileId', type: 'string', description: 'Figma file ID' },\n { name: 'nodeId', type: 'string', description: 'Figma node ID' },\n { name: 'token', type: 'string', description: 'Figma API token' },\n ],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Original Figma document fetched from the URL via official Figma API and cleaned by removing invisible nodes',\n },\n },\n downloadImages: {\n description: 'Detect and download image nodes from figma document',\n params: [\n { name: 'fileId', type: 'string', description: 'Figma file ID' },\n { name: 'token', type: 'string', description: 'Figma API token' },\n { name: 'imageDir', type: 'string', description: 'Output directory path' },\n {\n name: 'document',\n optional: true,\n type: 'FigmaFrameInfo',\n description: 'Figma document which is fetched and cleaned from the URL',\n },\n ],\n returns: {\n type: '{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }',\n description: 'Download results of images from the Figma document with Map structure',\n },\n },\n simplifyImageNodes: {\n description: 'Simplify image nodes in figma document by replacing redundant properties with url',\n params: [\n { name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' },\n { name: 'imageNodes', type: 'Map<string, ImageNode>', description: 'Image nodes map with id as key' },\n ],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Figma node with image nodes simplified and replaced with url',\n },\n },\n processedStyle: {\n description: 'Process styles in Figma document',\n params: [{ name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' }],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Figma node with styles processed',\n },\n },\n})\nclass FigmaTool {\n async fetchAndClean(fileId: string, nodeId: string, token: string): Promise<FigmaFrameInfo | undefined> {\n if (!fileId || !nodeId || !token) {\n return undefined;\n }\n\n const document = await fetchFigmaNode(fileId, nodeId, token);\n if (!document || !document?.children?.length) {\n return undefined;\n }\n\n const images = await fetchFigmaImages(fileId, nodeId, token);\n const thumbnail = images?.[nodeId] || '';\n document.thumbnailUrl = thumbnail;\n\n const cleanedDocument = cleanFigma(document);\n\n return cleanedDocument;\n }\n\n async downloadImages(\n fileId: string,\n token: string,\n imageDir: string,\n document?: FigmaFrameInfo\n ): Promise<{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }> {\n if (!fileId) {\n return { successCount: 0, failCount: 0, imageNodesMap: new Map() };\n }\n\n /* Detect images from the document */\n const imageNodes = findImageNodes(document?.children || [], document?.absoluteBoundingBox);\n const fetchedImages = await fetchImages(imageNodes, fileId, token);\n if (!fetchedImages.length) {\n return { successCount: 0, failCount: 0, imageNodesMap: new Map() };\n }\n\n return await executeDownloadImages(fetchedImages, imageDir);\n }\n\n simplifyImageNodes(node: FigmaFrameInfo, imageNodes: Map<string, ImageNode>): FigmaFrameInfo {\n const imageTarget = imageNodes.get(node.id);\n\n if (imageTarget) {\n const basicInfo: FigmaFrameInfo = {\n id: node.id,\n name: node.name,\n type: 'IMAGE',\n url: imageTarget.url,\n absoluteBoundingBox: node.absoluteBoundingBox,\n absoluteRenderBounds: node.absoluteRenderBounds,\n };\n\n if (node.cornerRadius) {\n basicInfo.cornerRadius = node.cornerRadius;\n }\n\n if (checkBorder(node)) {\n basicInfo.strokes = node.strokes;\n basicInfo.strokeWeight = node.strokeWeight;\n basicInfo.strokeAlign = node.strokeAlign;\n }\n return basicInfo;\n }\n\n const result: FigmaFrameInfo = { ...node };\n if (node.children && Array.isArray(node.children)) {\n result.children = node.children.map(child => this.simplifyImageNodes(child, imageNodes));\n }\n\n return result;\n }\n\n processedStyle(node: FigmaFrameInfo): FigmaFrameInfo {\n // Convert current node's styles using style-tool\n const processedNode = styleTool.convert(node);\n\n // Recursively process children\n if (processedNode.children && Array.isArray(processedNode.children)) {\n processedNode.children = processedNode.children.map(child => this.processedStyle(child));\n }\n\n return processedNode;\n }\n}\n\nexport const figmaTool = new FigmaTool();\n","import axios, { AxiosRequestConfig } from 'axios';\nimport { getDebugConfig } from './config';\nimport { writeFile } from './file';\nimport { workspaceManager } from './workspace';\n\n/**\n * Save debug log\n */\nfunction saveDebugLog(\n requestInfo: { url: string; config: AxiosRequestConfig },\n responseInfo: { status: number; statusText: string; data: unknown }\n): void {\n const debugConfig = getDebugConfig();\n if (!debugConfig.enabled) {\n return;\n }\n const debugContent = [\n '------------request------------',\n JSON.stringify(requestInfo, null, 2),\n '------------response------------',\n JSON.stringify(responseInfo, null, 2),\n ].join('\\n');\n writeFile(workspaceManager.path?.debug ?? '', `fetch_${new Date().toISOString()}.md`, debugContent);\n}\n\n/**\n * Axios get request with debug logging\n */\nexport async function get<T>(url: string, config?: AxiosRequestConfig<T>): Promise<T> {\n const response = await axios.get<T>(url, config);\n\n saveDebugLog(\n {\n url,\n config: config ?? {},\n },\n {\n status: response.status,\n statusText: response.statusText,\n data: response.data,\n }\n );\n\n return response.data;\n}\n","import { readFileSync, existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\n\n// Configuration directory in user's home\nexport const CONFIG_DIR = resolve(homedir(), '.coderio');\nconst CONFIG_FILE = resolve(CONFIG_DIR, 'config.yaml');\n\n/**\n * Model configuration interface\n */\nexport interface ModelConfig {\n provider: string;\n model: string;\n baseUrl: string;\n apiKey: string;\n}\n\n/**\n * Figma configuration interface\n */\nexport interface FigmaConfig {\n token: string;\n}\n\n/**\n * Debug configuration interface\n */\nexport interface DebugConfig {\n enabled: boolean;\n outputDir?: string;\n}\n\n/**\n * Configuration file structure\n */\ninterface Config {\n model: ModelConfig;\n figma: FigmaConfig;\n debug?: DebugConfig;\n}\n\n// Cache for loaded configuration\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from user's config directory\n * The configuration is cached after first load\n * @returns Full configuration object\n */\nexport function loadConfig(): Config {\n if (cachedConfig) {\n return cachedConfig;\n }\n\n if (!existsSync(CONFIG_FILE)) {\n throw new Error(`Configuration file not found at: ${CONFIG_FILE}\\n` + `Please create the configuration file.`);\n }\n\n try {\n const fileContent = readFileSync(CONFIG_FILE, 'utf8');\n const rawConfig = yaml.load(fileContent) as Config;\n\n if (!rawConfig) {\n throw new Error('Invalid config.yaml structure: configuration is empty');\n }\n\n cachedConfig = rawConfig;\n return cachedConfig;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to load config.yaml: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Get model configuration from config.yaml\n * @returns Model configuration\n * @throws Error if configuration is invalid\n */\nexport function getModelConfig(): ModelConfig {\n const config = loadConfig();\n if (!config.model) {\n throw new Error('Model configuration not found in config.yaml');\n }\n return config.model;\n}\n\n/**\n * Get Figma configuration from config.yaml\n * @returns Figma configuration\n * @throws Error if configuration is invalid\n */\nexport function getFigmaConfig(): FigmaConfig {\n const config = loadConfig();\n if (!config.figma) {\n throw new Error('Figma configuration not found in config.yaml');\n }\n return config.figma;\n}\n\n/**\n * Get debug configuration from config.yaml\n * @returns Debug configuration (defaults to disabled if not specified)\n */\nexport function getDebugConfig(): DebugConfig {\n const config = loadConfig();\n return config.debug || { enabled: false };\n}\n\n/**\n * Get the path to the configuration file\n */\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n\n/**\n * Check if configuration file exists\n */\nexport function configExists(): boolean {\n return existsSync(CONFIG_FILE);\n}\n/**\n * Clear the configuration cache\n * Useful for testing or when configuration needs to be reloaded\n */\nexport function clearConfigCache(): void {\n cachedConfig = null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from './logger';\nimport { FileInfo } from '../types/file-types';\n\n/**\n * Write file to the specified path\n * @param filePath - The path to the file\n * @param content - The content to write to the file\n */\nexport const writeFile = (folderPath: string, filePath: string, content: string) => {\n if (!folderPath || !filePath || !content) {\n return;\n }\n if (!fs.existsSync(folderPath)) {\n fs.mkdirSync(folderPath, { recursive: true });\n }\n fs.writeFileSync(path.join(folderPath, filePath), content);\n};\n\n/**\n * Create multiple files from parsed data\n */\nexport function createFiles({ files, filePath }: { files: FileInfo[]; filePath: string }): void {\n try {\n for (const file of files) {\n const dirPath = path.dirname(filePath);\n writeFile(dirPath, file.filename, file.content);\n }\n } catch (error) {\n logger.printErrorLog(`Failed to create files in ${filePath}: ${(error as Error).message}`);\n throw error;\n }\n}\n","import chalk from 'chalk';\n\n/**\n * Get current timestamp in YYYY-MM-DD HH:mm:ss format\n */\nfunction getTimestamp(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\n/**\n * Logger utility for consistent console output with colors\n */\nexport const logger = {\n /**\n * Print standard log (alias for info logs).\n *\n * Many subsystems (e.g. validation) use `printLog()` as the default logging method.\n */\n printLog(message: string): void {\n console.log(message);\n },\n\n /**\n * Print info log in blue\n */\n printInfoLog(message: string): void {\n console.log(chalk.blue(`[${getTimestamp()}] [INFO] ${message}`));\n },\n\n /**\n * Print warning log in yellow\n */\n printWarnLog(message: string): void {\n console.warn(chalk.yellow(`[${getTimestamp()}] [WARNING] ${message}`));\n },\n\n /**\n * Print error log in red\n */\n printErrorLog(message: string): void {\n console.error(chalk.red(`[${getTimestamp()}] [ERROR] ✖ ${message}`));\n },\n\n /**\n * Print test/debug log in gray\n * Only logs in development/test environments\n */\n printTestLog(message: string): void {\n if (process.env.NODE_ENV === 'development') {\n console.log(chalk.gray(`[${getTimestamp()}] [DEBUG] ${message}`));\n }\n },\n\n /**\n * Print success log in green\n */\n printSuccessLog(message: string): void {\n console.log(chalk.green(`[${getTimestamp()}] [SUCCESS] ✔ ${message}`));\n },\n};\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { WorkspaceStructure } from '../types/workspace-types';\nimport { logger } from './logger';\n\n/**\n * Defines the logical structure of the output workspace.\n * output directory structure\n coderio/\n └── figmaName/ # Project root directory generated from a Figma URL\n ├── my-app/ # Generated project source code\n ├── process/ # Intermediate data and cache during generation\n │ ├── validation/ # Validation reports, screenshots, and processed.json\n │ └── ... # Other process artifacts\n ├── reports.html # Validation reports summary\n └── checkpoint/ # Cache\n ├── coderio-cli.db \n └── checkpoint.json \n*/\nclass Workspace {\n path: WorkspaceStructure | null = null;\n\n initWorkspace(subPath: string, rootPath?: string, appName?: string): WorkspaceStructure {\n if (this.path) {\n return this.path;\n }\n\n const root = rootPath || (process.env.CODERIO_CLI_USER_CWD ?? process.cwd());\n const coderioRoot = path.join(root, 'coderio');\n const finalRoot = path.resolve(coderioRoot, subPath);\n const app = appName || 'my-app';\n\n const absoluteRoot = path.resolve(finalRoot);\n const processDir = path.join(absoluteRoot, 'process');\n const checkpointDir = path.join(absoluteRoot, 'checkpoint');\n const debugDir = path.join(absoluteRoot, 'debug');\n\n this.path = {\n root: absoluteRoot,\n app: path.join(absoluteRoot, app),\n process: processDir,\n debug: debugDir,\n reports: path.join(absoluteRoot, 'reports.html'),\n db: path.join(checkpointDir, 'coderio-cli.db'),\n checkpoint: path.join(checkpointDir, 'checkpoint.json'),\n };\n return this.path;\n }\n\n /**\n * Delete all files and directories inside the workspace\n */\n deleteWorkspace(workspace: WorkspaceStructure): void {\n try {\n if (fs.existsSync(workspace.root)) {\n // Read all entries in the workspace root\n const entries = fs.readdirSync(workspace.root);\n\n // Delete each entry\n for (const entry of entries) {\n const fullPath = path.join(workspace.root, entry);\n fs.rmSync(fullPath, { recursive: true, force: true });\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to delete workspace: ${errorMessage}`);\n }\n }\n\n /**\n * Resolve the absolute path to the source code directory\n * @param paths - The workspace structure\n * @param srcSubPath - The subpath to the source code directory\n * @returns The absolute path to the source code directory\n */\n resolveAppSrc(paths: WorkspaceStructure, srcSubPath: string): string {\n return path.join(paths.app, 'src', srcSubPath);\n }\n\n /**\n * Resolve component alias path to absolute filesystem path.\n *\n * Handles various path formats:\n * - @/components/Button → /workspace/my-app/src/components/Button/index.tsx\n * - @/src/components/Button → /workspace/my-app/src/components/Button/index.tsx\n * - components/Button → /workspace/my-app/src/components/Button/index.tsx\n *\n */\n resolveComponentPath(aliasPath: string): string {\n // Normalize path separators for robustness across platforms.\n const normalizedAlias = aliasPath.replace(/\\\\/g, '/');\n\n // Step 1: Strip @/ prefix if present\n let relativePath = normalizedAlias.startsWith('@/')\n ? normalizedAlias.substring(2) // '@/components/Button' → 'components/Button'\n : normalizedAlias;\n\n // Step 2: Strip 'src/' prefix if present (resolveAppSrc adds it)\n // '@/src/components/Button' → 'components/Button'\n if (relativePath.startsWith('src/')) {\n relativePath = relativePath.substring(4);\n }\n\n // Step 3: Ensure path ends with /index.tsx (all components follow this convention)\n if (!relativePath.endsWith('.tsx') && !relativePath.endsWith('.ts')) {\n relativePath = `${relativePath}/index.tsx`;\n }\n\n return relativePath;\n }\n}\nexport const workspaceManager = new Workspace();\n","import { FigmaFrameInfo, FigmaImageFormat, FigmaColorObject } from '../../types/figma-types';\nimport { get } from '../../utils/axios';\nimport { logger } from '../../utils/logger';\n\n/**\n * Fetch Figma nodes by fileId and nodeId\n * @param fileId - Figma file ID\n * @param nodeId - Figma node ID\n * @param token - Figma API token\n * @returns Figma frame information\n */\nexport const fetchFigmaNode = async (fileId: string, nodeId: string, token: string): Promise<FigmaFrameInfo | undefined> => {\n const url = `https://api.figma.com/v1/files/${fileId}/nodes?ids=${nodeId}`;\n try {\n const data = await get<{ nodes: Record<string, { document: FigmaFrameInfo }> }>(url, {\n headers: {\n 'X-Figma-Token': token,\n },\n });\n // format node id to match the format in the response\n const resData = data.nodes?.[nodeId];\n return resData?.document;\n } catch (error) {\n logger.printErrorLog(`Failed to fetch Figma node: ${error instanceof Error ? error.message : 'Unknown error'}`);\n return undefined;\n }\n};\n\n/**\n * Fetch Figma image by fileId and nodeId\n * @param fileId - Figma file ID\n * @param nodeIds - Figma node ID, multiple node ids can be passed separated by commas\n * @param token - Figma API token\n * @param format - Figma image format\n * @returns Figma image\n */\nexport const fetchFigmaImages = async (\n fileId: string,\n nodeIds: string,\n token: string,\n format?: FigmaImageFormat\n): Promise<Record<string, string>> => {\n const url = `https://api.figma.com/v1/images/${fileId}`;\n try {\n const data = await get<{ images: Record<string, string> }>(url, {\n headers: {\n 'X-Figma-Token': token,\n },\n params: {\n ids: nodeIds,\n format: format || 'png',\n },\n });\n const images = data.images || {};\n // format node id to match the format from response to request\n return Object.fromEntries(Object.entries(images));\n } catch (error) {\n logger.printErrorLog(`Failed to fetch Figma images: ${error instanceof Error ? error.message : 'Unknown error'}`);\n return {};\n }\n};\n\n/**\n * Clean Figma node and children. Remove invisible nodes and children.\n * @param node - Figma node or children\n * @returns Cleaned Figma node or children. If the node is invisible, return null.\n */\nexport const cleanFigma = (node: FigmaFrameInfo): FigmaFrameInfo | undefined => {\n // if node is invisible, return undefined\n if (node.visible === false) {\n return undefined;\n }\n\n // if node has children, recursively clean each child\n if (node.children && Array.isArray(node.children)) {\n node.children = node.children\n .map(child => cleanFigma(child)) // recursively clean each child\n .filter(child => child !== undefined); // filter out invisible nodes\n }\n\n return node;\n};\n\n/**\n * Check if node has border\n * @param node - Figma node\n * @returns True if node has border, false otherwise\n */\nexport const checkBorder = (node: FigmaFrameInfo): boolean => {\n const strokes = node.strokes;\n const strokeWeight = node.strokeWeight;\n\n if (!strokes || !strokes.length || !strokeWeight) return false;\n\n const visibleStrokes = strokes.filter((s: FigmaColorObject) => s.visible !== false);\n if (visibleStrokes.length === 0) return false;\n\n return true;\n};\n","import { FigmaColorObject, FigmaFrameInfo, FigmaImageFormat, FigmaPositionAndSize } from '../../types/figma-types';\nimport { ImageNode } from './types';\nimport fs from 'fs';\nimport path from 'path';\nimport axios from 'axios';\nimport { promisePool } from '../../utils/promise-pool';\nimport { fetchFigmaImages } from './figma';\nimport { DEFAULT_DOWNLOAD_CONCURRENCY, DOWNLOAD_TIMEOUT_MS, MAX_DOWNLOAD_RETRIES, BASE_RETRY_DELAY_MS } from './constants';\nimport { logger } from '../../utils/logger';\n\n/**\n * Fetch images from figma document\n * @param nodes - Image nodes\n * @param fileId - Figma file ID\n * @param token - Figma API token\n * @returns Image nodes\n */\nexport const fetchImages = async (nodes: ImageNode[], fileId: string, token: string): Promise<ImageNode[]> => {\n if (!fileId || !nodes?.length) {\n return [];\n }\n\n const svgs = nodes.filter(node => node.format === FigmaImageFormat.SVG);\n const pngs = nodes.filter(node => node.format === FigmaImageFormat.PNG);\n const getImagePromises: Promise<{ [key: string]: string } | undefined>[] = [];\n\n if (svgs.length > 0) {\n getImagePromises.push(fetchFigmaImages(fileId, svgs.map(node => node.id).join(','), token, FigmaImageFormat.SVG));\n }\n if (pngs.length > 0) {\n getImagePromises.push(fetchFigmaImages(fileId, pngs.map(node => node.id).join(','), token, FigmaImageFormat.PNG));\n }\n\n const images: ImageNode[] = [];\n const results = await Promise.all(getImagePromises);\n results.forEach((res: { [key: string]: string } | undefined) => {\n if (res) {\n for (const [key, value] of Object.entries(res)) {\n images.push({\n id: key,\n name: '',\n format: FigmaImageFormat.PNG,\n ...(nodes.find(n => n.id === key) || {}),\n url: value || '',\n });\n }\n }\n });\n\n return images;\n};\n\n/**\n * Download images from figma document\n * @param images - Image nodes\n * @param imageDir - Output directory path\n * @param concurrency - Concurrency level\n * @returns Download results\n */\nexport const executeDownloadImages = async (\n images: ImageNode[],\n imageDir?: string,\n concurrency: number = DEFAULT_DOWNLOAD_CONCURRENCY\n): Promise<{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }> => {\n if (!images || !images.length || !imageDir) {\n return {\n successCount: 0,\n failCount: 0,\n imageNodesMap: new Map(),\n };\n }\n\n // Process all images with dynamic concurrency control using promisePool\n const results = await promisePool(images, image => createDownloadTask(image, imageDir), concurrency);\n\n // Aggregate log after completion\n const successCount = results.filter(r => r.success).length;\n const failCount = results.length - successCount;\n\n // Convert results array to Map with id as key\n const imageNodesMap = new Map<string, ImageNode>(results.map(img => [img.id, img]));\n\n return {\n successCount,\n failCount,\n imageNodesMap,\n };\n};\n\n/**\n * Find image nodes in figma document\n * @param nodes - Figma nodes\n * @param absoluteBoundingBox - Absolute bounding box of the document\n * @returns Image nodes\n */\nexport const findImageNodes = (nodes: FigmaFrameInfo[], absoluteBoundingBox?: FigmaPositionAndSize): ImageNode[] => {\n const imageNodes: ImageNode[] = [];\n if (!nodes || !Array.isArray(nodes)) {\n return imageNodes;\n }\n\n for (const node of nodes) {\n if (node.visible === false) {\n continue;\n }\n // Rule 1: If node type is VECTOR, directly add to imageNodeIds\n else if (node.type === 'VECTOR') {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n // Rule 2: If node type is IMAGE or has imageRef, directly add to imageNodeIds\n else if (isImageNode(node) || isImageNodeViaName(node)) {\n if (isImageNode(node) || hasAnyImageNodeInDescendants(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n } else if (isMaskNode(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n }\n // Rule 3: For nodes with children, check if any leaf descendant is a TEXT node with characters\n else if (node.children && node.children.length > 0) {\n const hasAnyTextNode = hasAnyTextNodeWithCharacters(node);\n\n if (hasAnyTextNode) {\n const firstLevelChildrenHasImageNode = node.children.some((child: FigmaFrameInfo) => isImageNode(child));\n const firstLevelChildrenHasTextNode = node.children.some((child: FigmaFrameInfo) => isTextNode(child));\n if (firstLevelChildrenHasImageNode && !firstLevelChildrenHasTextNode) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n const childImageIds = findImageNodes(node.children, absoluteBoundingBox);\n imageNodes.push(...childImageIds);\n }\n } else if (hasAnyImageNodeInDescendants(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n }\n }\n\n return imageNodes;\n};\n\n/**\n * Determine whether a node should be exported as SVG or PNG\n * @param node - Figma node\n * @param absoluteBoundingBox - Absolute bounding box of the document\n * @returns Figma image format\n */\nexport const exportSvgIfNeeded = (node: FigmaFrameInfo, absoluteBoundingBox?: FigmaPositionAndSize) => {\n // Rule 1: Check if node is very large (likely a background) -> PNG\n if (node.absoluteBoundingBox && absoluteBoundingBox) {\n const { width, height } = node.absoluteBoundingBox;\n const { width: pageWidth, height: pageHeight } = absoluteBoundingBox;\n if (width >= pageWidth && height >= pageHeight) {\n return FigmaImageFormat.PNG;\n }\n }\n\n // Rule 2: Check exportSettings for explicit format specification\n if (node.exportSettings && node.exportSettings.length > 0) {\n const format = node.exportSettings[0].format;\n if (format === FigmaImageFormat.PNG) {\n return FigmaImageFormat.PNG;\n }\n if (format === FigmaImageFormat.SVG) {\n return FigmaImageFormat.SVG;\n }\n }\n\n // Rule 3: Check node name for background keywords -> PNG\n if (node.name.includes('背景') || node.name.toLowerCase().includes('background')) {\n return FigmaImageFormat.PNG;\n }\n\n // Default: Export as SVG\n return FigmaImageFormat.SVG;\n};\n\n/** Assign image object from figma node and format **/\nexport const assignImageObject = (node: { id: string; name: string }, format: FigmaImageFormat) => {\n return {\n id: node.id,\n name: node.name,\n format,\n };\n};\n\n/** Check if node has image ref in fills **/\nexport const hasImageRefInFills = (node: FigmaFrameInfo): boolean => {\n if (!node || !node.fills || node.fills.length === 0) {\n return false;\n }\n return node.fills.some((fill: FigmaColorObject) => {\n const fillWithImageRef = fill;\n return (fillWithImageRef.imageRef && fillWithImageRef.imageRef !== '') || fillWithImageRef.type === 'IMAGE';\n });\n};\n\n/** Check if node is image node **/\nexport const isImageNode = (node: FigmaFrameInfo): boolean => {\n if (!node) {\n return false;\n }\n\n if (node.type === 'IMAGE') {\n return true;\n }\n\n if (node.fills && node.fills.length > 0) {\n return hasImageRefInFills(node);\n }\n\n return false;\n};\n\n/** Check if node is image node via name **/\nexport const isImageNodeViaName = (node: FigmaFrameInfo): boolean => {\n return (node && node.name.toLowerCase().includes('img')) || node.name.toLowerCase().includes('image');\n};\n\n/** Check if node is mask node **/\nexport const isMaskNode = (node: FigmaFrameInfo): boolean => {\n return node && node.name.toLowerCase().includes('mask');\n};\n\n/** Check if node is text node **/\nexport const isTextNode = (node: FigmaFrameInfo): boolean => {\n return node && node.type === 'TEXT' && node.characters !== undefined && node.characters.trim() !== '';\n};\n\n/** Check if node has any image node in descendants **/\nexport const hasAnyImageNodeInDescendants = (node: FigmaFrameInfo): boolean => {\n if (!node) return false;\n\n if (!node.children || node.children.length === 0) {\n return isImageNode(node);\n }\n return node.children.some((child: FigmaFrameInfo) => hasAnyImageNodeInDescendants(child));\n};\n\n/** Check if node has any text node in descendants **/\nexport const hasAnyTextNodeWithCharacters = (node: FigmaFrameInfo): boolean => {\n if (!node) return false;\n\n if (!node.children || node.children.length === 0) {\n return isTextNode(node);\n }\n return node.children.some((child: FigmaFrameInfo) => hasAnyTextNodeWithCharacters(child));\n};\n\n/**\n * Download an image from URL and save to local directory\n * @param url - Image URL to download\n * @param filename - Local filename (with extension)\n * @param outputDir - Output directory path\n * @returns Local file path\n */\nexport async function downloadImage(url: string, filename?: string, imageDir?: string, base64?: boolean): Promise<string> {\n if (!url || (!base64 && (!filename || !imageDir))) {\n return '';\n }\n\n const maxRetries = MAX_DOWNLOAD_RETRIES;\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await axios.get(url, {\n responseType: 'arraybuffer',\n timeout: DOWNLOAD_TIMEOUT_MS,\n });\n\n if (base64) {\n return Buffer.from(response.data).toString('base64');\n } else {\n if (!imageDir || !filename) {\n return '';\n }\n // Create directory if it doesn't exist\n if (!fs.existsSync(imageDir)) {\n fs.mkdirSync(imageDir, { recursive: true });\n }\n const filepath = path.join(imageDir, filename);\n fs.writeFileSync(filepath, Buffer.from(response.data));\n return filepath;\n }\n } catch (error) {\n lastError = error;\n // Don't wait on the last attempt\n if (attempt < maxRetries) {\n // Wait 1s, 2s, 3s, 4s, 5s\n const delay = BASE_RETRY_DELAY_MS * (attempt + 1);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);\n throw new Error(`Failed to download image from ${url} after ${maxRetries} retries: ${errorMessage}`);\n}\n\n/**\n * Create download task for image\n * @param image - Image object\n * @param outputDir - Output directory path\n * @returns Download task object\n */\nexport const createDownloadTask = async (image: ImageNode, imageDir?: string): Promise<ImageNode> => {\n if (!image.url) {\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: image.url,\n remote: image.url,\n success: false,\n };\n }\n\n const ext = image.format || FigmaImageFormat.PNG;\n // Sanitize filename: remove special characters, replace spaces with dashes\n const sanitizedName = (image.name || image.id).replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();\n const filename = `${sanitizedName}-${image.id.replace(/:/g, '-')}.${ext}`;\n\n try {\n const localPath = await downloadImage(image.url, filename, imageDir);\n const normalizedImageDir = imageDir ? path.normalize(imageDir) : '';\n const dirParts = normalizedImageDir.split(path.sep).filter(Boolean);\n const srcIndex = dirParts.lastIndexOf('src');\n const srcDir =\n srcIndex >= 0 ? (normalizedImageDir.startsWith(path.sep) ? path.sep : '') + path.join(...dirParts.slice(0, srcIndex + 1)) : '';\n\n const relativeFromSrc = srcDir ? path.relative(srcDir, localPath) : '';\n const normalizedRelative = relativeFromSrc.split(path.sep).join('/');\n const aliasPath = `@/${normalizedRelative}`;\n\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: aliasPath,\n remote: image.url,\n success: true,\n };\n } catch {\n logger.printErrorLog(`Failed to download image: ${image.url}`);\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: image.url,\n remote: image.url,\n success: false,\n };\n }\n};\n","/**\n * Run multiple asynchronous tasks with a concurrency limit.\n *\n * @param items - Array of items to process\n * @param taskGenerator - Function that creates a promise for a single item\n * @param concurrency - Maximum number of concurrent tasks\n * @returns Array of results\n */\nexport async function promisePool<T, R>(items: T[], taskGenerator: (item: T) => Promise<R>, concurrency: number = 5): Promise<R[]> {\n if (!items || !items.length) {\n return [];\n }\n const results: R[] = [];\n let nextIndex = 0;\n const executing = new Set<Promise<void>>();\n\n const processTask = async (index: number) => {\n const item = items[index];\n if (!item) {\n results[index] = undefined as unknown as R;\n return;\n }\n const result = await taskGenerator(item);\n results[index] = result;\n };\n\n while (nextIndex < items.length || executing.size > 0) {\n while (nextIndex < items.length && executing.size < concurrency) {\n const index = nextIndex++;\n const promise = processTask(index).finally(() => {\n executing.delete(promise);\n });\n executing.add(promise);\n }\n\n if (executing.size > 0) {\n await Promise.race(executing);\n }\n }\n\n return results;\n}\n","export const MAX_DOWNLOAD_RETRIES = 5; // Maximum number of download retries\nexport const DOWNLOAD_TIMEOUT_MS = 60000; // Download timeout in milliseconds\nexport const BASE_RETRY_DELAY_MS = 1000; // Base retry delay in milliseconds\nexport const DEFAULT_DOWNLOAD_CONCURRENCY = 5; // Default download concurrency\n","import { tools } from 'evoltagent';\nimport { CSSStyles, FigmaFrameInfo } from '../../types/figma-types';\nimport { convertBorderRadius, convertEffects, convertFills, convertStrokes, convertClipContent } from './utils';\n\n@tools({\n convert: {\n description: 'Convert Figma properties to CSS styles, remove redundant properties, and return the processed Figma node',\n params: [{ name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' }],\n returns: {\n type: 'CSSStyles',\n description: 'CSS styles converted from Figma properties',\n },\n },\n})\nclass StyleTool {\n convert(node: FigmaFrameInfo): FigmaFrameInfo {\n const inlineStyles: CSSStyles = {};\n\n if (node.type !== 'TEXT') {\n convertBorderRadius(node, inlineStyles);\n convertEffects(node, inlineStyles);\n convertFills(node, inlineStyles);\n convertStrokes(node, inlineStyles);\n convertClipContent(node, inlineStyles);\n }\n\n // Create processed node with styles\n const processedNode: FigmaFrameInfo = {\n ...node,\n inlineStyles,\n };\n\n // Remove converted fields to reduce data size\n if (node.type !== 'TEXT') {\n const nodeWithOptionalStyles = processedNode as Partial<FigmaFrameInfo> & Record<string, unknown>;\n delete nodeWithOptionalStyles.fills;\n delete nodeWithOptionalStyles.background;\n delete nodeWithOptionalStyles.strokes;\n delete nodeWithOptionalStyles.strokeAlign;\n delete nodeWithOptionalStyles.backgroundColor;\n delete nodeWithOptionalStyles.cornerRadius;\n delete nodeWithOptionalStyles.rectangleCornerRadii;\n delete nodeWithOptionalStyles.effects;\n // delete nodeWithOptionalStyles.absoluteRenderBounds;\n delete nodeWithOptionalStyles.style;\n }\n return processedNode;\n }\n}\n\nexport const styleTool = new StyleTool();\n","/**\n * Color Converter\n * Convert Figma color objects (SOLID, GRADIENT_LINEAR, GRADIENT_RADIAL) to CSS color strings\n */\nimport { FigmaColorObject, FigmaColor, FigmaGradientStop } from '../../types/figma-types';\n\nexport class ColorConverter {\n // Main conversion entry point\n static convert(colorObject: FigmaColorObject): string {\n if (!colorObject) return 'transparent';\n\n const type = colorObject.type;\n\n switch (type) {\n case 'SOLID':\n return this.convertSolid(colorObject);\n case 'GRADIENT_LINEAR':\n return this.convertLinearGradient(colorObject);\n case 'GRADIENT_RADIAL':\n return this.convertRadialGradient(colorObject);\n default:\n return 'transparent';\n }\n }\n\n // Convert SOLID fill to rgba/rgb string\n private static convertSolid(fill: FigmaColorObject): string {\n if (!fill.color) return 'transparent';\n\n const opacity = this.getOpacity(fill.opacity);\n return this.rgbaToString(fill.color, opacity);\n }\n\n // Convert linear gradient to CSS linear-gradient\n private static convertLinearGradient(fill: FigmaColorObject): string {\n const stops = fill.gradientStops || [];\n const handles = fill.gradientHandlePositions || [];\n const fillOpacity = this.getOpacity(fill.opacity);\n\n if (stops.length === 0) return 'transparent';\n\n // Calculate gradient angle\n const angle = handles.length >= 2 ? this.calculateLinearGradientAngle(handles) : 180;\n\n // Calculate gradient stops with positions\n const stopsWithPositions = this.calculateLinearGradientStops(stops, handles, fillOpacity);\n\n return `linear-gradient(${angle}deg, ${stopsWithPositions})`;\n }\n\n // Convert radial gradient to CSS radial-gradient\n private static convertRadialGradient(fill: FigmaColorObject): string {\n const stops = fill.gradientStops || [];\n const handles = fill.gradientHandlePositions || [];\n const fillOpacity = this.getOpacity(fill.opacity);\n\n if (stops.length === 0) return 'transparent';\n\n // Calculate radial gradient parameters\n const { size, position } = this.calculateRadialGradientParams(handles);\n\n // Convert stops\n const stopsStr = stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const pos = Math.round(stop.position * 100);\n return `${color} ${pos}%`;\n })\n .join(', ');\n\n return `radial-gradient(${size} at ${position}, ${stopsStr})`;\n }\n\n private static getOpacity(opacity?: number): number {\n return opacity !== undefined ? opacity : 1;\n }\n\n // Helper: Convert Figma color to CSS rgba/rgb/hex string\n static rgbaToString(color: FigmaColor, opacity: number = 1): string {\n if (!color) return 'transparent';\n\n const r = Math.round((color.r || 0) * 255);\n const g = Math.round((color.g || 0) * 255);\n const b = Math.round((color.b || 0) * 255);\n const a = (color.a !== undefined ? color.a : 1) * (opacity !== undefined ? opacity : 1);\n\n // Use hex format when fully opaque, rgba when transparent\n if (Math.abs(a - 1) < 0.001) {\n // Fully opaque - use hex format\n if (r === 255 && g === 255 && b === 255) return '#FFF';\n if (r === 0 && g === 0 && b === 0) return '#000';\n // Convert to hex\n const toHex = (n: number) => n.toString(16).toUpperCase().padStart(2, '0');\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n }\n\n // Transparent - use rgba with proper formatting\n const alphaStr = a !== undefined ? a.toFixed(2) : '1';\n return `rgba(${r}, ${g}, ${b}, ${alphaStr})`;\n }\n\n /**\n * Calculate CSS gradient angle from Figma gradient handle positions\n * Figma uses a transform matrix system with 3 points:\n * - p0: gradient origin\n * - p1: gradient direction endpoint (color changes from p0 to p1)\n * - p2: perpendicular direction endpoint\n *\n * Formula: CSS angle = atan2(v1) + angleBetween(v1, v2)\n * where v1 = p1-p0, v2 = p2-p0\n */\n private static calculateLinearGradientAngle(positions: { x: number; y: number }[]): number {\n if (positions.length < 2) return 180;\n\n const [p0, p1, p2] = positions;\n\n if (!p0 || !p1) return 180;\n\n // Vector v1: gradient direction (p0 → p1)\n const v1x = p1.x - p0.x;\n const v1y = p1.y - p0.y;\n const len1 = Math.sqrt(v1x * v1x + v1y * v1y);\n\n // Calculate angle of v1\n const angle1Rad = Math.atan2(v1y, v1x);\n const angle1Deg = angle1Rad * (180 / Math.PI);\n\n // If we don't have p2, use simple formula\n if (!p2 || positions.length < 3) {\n let cssAngle = angle1Deg + 90;\n while (cssAngle < 0) cssAngle += 360;\n while (cssAngle >= 360) cssAngle -= 360;\n return Math.round(cssAngle);\n }\n\n // Vector v2: perpendicular reference (p0 → p2)\n const v2x = p2.x - p0.x;\n const v2y = p2.y - p0.y;\n const len2 = Math.sqrt(v2x * v2x + v2y * v2y);\n\n // Calculate angle between v1 and v2\n const dot = v1x * v2x + v1y * v2y;\n const cosAngle = Math.max(-1, Math.min(1, dot / (len1 * len2)));\n const angleBetweenRad = Math.acos(cosAngle);\n const angleBetweenDeg = angleBetweenRad * (180 / Math.PI);\n\n // CSS angle = angle1 + angleBetween\n let cssAngle = angle1Deg + angleBetweenDeg;\n\n // Normalize to 0-360 range\n while (cssAngle < 0) {\n cssAngle += 360;\n }\n while (cssAngle >= 360) {\n cssAngle -= 360;\n }\n\n return Math.round(cssAngle);\n }\n\n /**\n * Calculate gradient stops with correct positions\n * Based on Figma's gradient handle positions and transform matrix\n */\n private static calculateLinearGradientStops(\n stops: FigmaGradientStop[],\n handles: { x: number; y: number }[],\n fillOpacity: number\n ): string {\n if (handles.length < 2) {\n // Fallback: simple position mapping\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n // Format: remove .00 for whole numbers\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n const [p0, p1] = handles;\n\n if (!p0 || !p1) {\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Transform matrix vectors\n const m1x = p1.x - p0.x;\n const m1y = p1.y - p0.y;\n\n // Gradient length\n const gradientLength = Math.sqrt(m1x * m1x + m1y * m1y);\n\n if (gradientLength === 0) {\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Get CSS angle\n const cssAngle = this.calculateLinearGradientAngle(handles);\n const cssAngleRad = (cssAngle * Math.PI) / 180;\n\n // CSS gradient direction vector\n const gradDirX = Math.sin(cssAngleRad);\n const gradDirY = -Math.cos(cssAngleRad);\n\n // Project box corners onto gradient direction\n const corners = [\n { x: 0, y: 0 },\n { x: 1, y: 0 },\n { x: 0, y: 1 },\n { x: 1, y: 1 },\n ];\n\n const projections = corners.map(c => c.x * gradDirX + c.y * gradDirY);\n const minProj = Math.min(...projections);\n const maxProj = Math.max(...projections);\n const projRange = maxProj - minProj;\n\n // Calculate stop positions\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n\n // Point on gradient line at this stop\n const pointX = p0.x + stop.position * m1x;\n const pointY = p0.y + stop.position * m1y;\n\n // Project onto CSS gradient direction\n const projection = pointX * gradDirX + pointY * gradDirY;\n\n // Convert to percentage\n let cssPosition = ((projection - minProj) / projRange) * 100;\n\n // Round to 2 decimal places then format\n cssPosition = cssPosition !== undefined ? Math.round(cssPosition * 100) / 100 : 0;\n\n // Format position string\n let posStr: string;\n if (cssPosition === 0) {\n posStr = '0%';\n } else if (Number.isInteger(cssPosition)) {\n posStr = `${cssPosition}%`;\n } else {\n posStr = `${cssPosition.toFixed(2)}%`;\n }\n\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Calculate radial gradient parameters from handle positions\n private static calculateRadialGradientParams(handles: { x: number; y: number }[]): {\n size: string;\n position: string;\n } {\n if (handles.length < 2) {\n return { size: 'circle', position: '50% 50%' };\n }\n\n const [center, edge, perpendicular] = handles;\n\n if (!center || !edge || !perpendicular) {\n return { size: 'circle', position: '50% 50%' };\n }\n\n // Calculate radius as distance from center to edge\n const dx = edge.x - center.x;\n const dy = edge.y - center.y;\n const radiusX = Math.sqrt(dx * dx + dy * dy);\n\n // Calculate perpendicular radius if third handle exists\n let radiusY = radiusX;\n if (perpendicular) {\n const pdx = perpendicular.x - center.x;\n const pdy = perpendicular.y - center.y;\n radiusY = Math.sqrt(pdx * pdx + pdy * pdy);\n }\n\n // Convert center position to percentage\n const centerX = center.x !== undefined ? (center.x * 100).toFixed(2) : '0';\n const centerY = center.y !== undefined ? (center.y * 100).toFixed(2) : '0';\n\n // Calculate size\n const sizeX = radiusX !== undefined ? (radiusX * 100).toFixed(2) : '0';\n const sizeY = radiusY !== undefined ? (radiusY * 100).toFixed(2) : '0';\n\n return {\n size: `${sizeY}% ${sizeX}%`,\n position: `${centerX}% ${centerY}%`,\n };\n }\n\n // Convert SOLID fill to linear-gradient format (for layering multiple fills)\n static solidToGradient(fill: FigmaColorObject): string {\n const color = this.convertSolid(fill);\n return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`;\n }\n}\n","import { CSSStyles, FigmaColorObject, FigmaFrameInfo } from '../../types/figma-types';\nimport { ColorConverter } from './color';\n\n// Convert border radius\nexport const convertBorderRadius = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n if (node.cornerRadius !== undefined) {\n inlineStyles.borderRadius = `${node.cornerRadius}px`;\n }\n\n // Individual corner radius\n if (node.rectangleCornerRadii) {\n const [tl, tr, br, bl] = node.rectangleCornerRadii;\n inlineStyles.borderRadius = `${tl}px ${tr}px ${br}px ${bl}px`;\n }\n};\n\n// Convert effects (shadow, filter,backdrop-filter)\nexport const convertEffects = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const effects = node.effects;\n\n if (!effects || effects.length === 0) return;\n\n const shadows: string[] = [];\n const filters: string[] = [];\n const backdropFilters: string[] = [];\n\n for (const effect of effects) {\n if (effect.visible === false) continue;\n\n if (effect.type === 'DROP_SHADOW') {\n const x = effect.offset?.x || 0;\n const y = effect.offset?.y || 0;\n const blur = effect.radius || 0;\n const spread = effect.spread || 0;\n // Use ColorConverter for shadow color\n const color = effect.color ? ColorConverter.convert({ type: 'SOLID', color: effect.color }) : 'rgba(0, 0, 0, 0.25)';\n\n shadows.push(`${x.toFixed(0)}px ${y.toFixed(0)}px ${(blur / 2).toFixed(0)}px ${spread.toFixed(0)}px ${color}`);\n } else if (effect.type === 'INNER_SHADOW') {\n const x = effect.offset?.x || 0;\n const y = effect.offset?.y || 0;\n const blur = effect.radius || 0;\n const spread = effect.spread || 0;\n const color = effect.color ? ColorConverter.convert({ type: 'SOLID', color: effect.color }) : 'rgba(0, 0, 0, 0.25)';\n\n shadows.push(`inset ${x.toFixed(0)}px ${y.toFixed(0)}px ${(blur / 2).toFixed(0)}px ${spread.toFixed(0)}px ${color}`);\n } else if (effect.type === 'LAYER_BLUR') {\n const blur = effect.radius || 0;\n filters.push(`blur(${(blur / 2).toFixed(0)}px)`);\n } else if (effect.type === 'BACKGROUND_BLUR') {\n const blur = effect.radius || 0;\n backdropFilters.push(`blur(${(blur / 2).toFixed(0)}px)`);\n }\n }\n\n if (shadows.length > 0) {\n inlineStyles.boxShadow = shadows.join(', ');\n }\n\n if (filters.length > 0) {\n inlineStyles.filter = filters.join(' ');\n }\n\n if (backdropFilters.length > 0) {\n inlineStyles.backdropFilter = backdropFilters.join(', ');\n }\n};\n\n// Convert fills to background\n// Handles multiple fills and creates layered backgrounds\nexport const convertFills = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const fills = node.fills || node.background;\n\n if (!fills || fills.length === 0) return;\n\n // Filter visible fills\n const visibleFills = fills.filter((fill: FigmaColorObject) => fill.visible !== false);\n if (visibleFills.length === 0) return;\n\n // Convert all fills to CSS\n // For multiple fills, convert SOLID to gradient format for proper layering\n const backgrounds: string[] = visibleFills.map((fill: FigmaColorObject) => {\n if (fill.type === 'SOLID' && visibleFills.length > 1) {\n return ColorConverter.solidToGradient(fill);\n }\n return ColorConverter.convert(fill);\n });\n\n // IMPORTANT: Reverse the array!\n // Figma fills: index 0 = top layer\n // CSS background: first declared = top layer\n // But Figma's rendering order is bottom-to-top in the fills array\n // So we need to reverse to match CSS rendering order\n backgrounds.reverse();\n\n // Set background with all layers\n inlineStyles.background = backgrounds.join(', ');\n};\n\n// Convert strokes to border\nexport const convertStrokes = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const strokes = node.strokes;\n\n if (!strokes || strokes.length === 0 || !node.strokeWeight) return;\n\n const visibleStrokes = strokes.filter((s: FigmaColorObject) => s.visible !== false);\n if (visibleStrokes.length === 0) return;\n\n const width = node.strokeWeight || 1;\n let strokeColor = '';\n const isGradientStroke = visibleStrokes.some((s: FigmaColorObject) => s.type.includes('GRADIENT'));\n if (isGradientStroke) {\n const gradient = visibleStrokes.find((s: FigmaColorObject) => s.type.includes('GRADIENT'));\n if (gradient) {\n strokeColor = ColorConverter.convert(gradient);\n }\n inlineStyles.strokeColor = strokeColor;\n inlineStyles.borderRadius = `${node.cornerRadius !== undefined ? node.cornerRadius.toFixed(0) : '0'}px`;\n inlineStyles.strokeWidth = `${width}px`;\n } else {\n const solid = visibleStrokes.find((s: FigmaColorObject) => s.type === 'SOLID');\n if (solid) {\n strokeColor = ColorConverter.convert(solid);\n }\n inlineStyles.border = `${width}px solid ${strokeColor}`;\n }\n};\n\n// Convert clip content\nexport const convertClipContent = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n if (node.clipsContent) {\n inlineStyles.overflow = 'hidden';\n }\n};\n","import { HumanMessage } from '@langchain/core/messages';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { logger } from './logger';\nimport { getDebugConfig, getModelConfig, type ModelConfig } from './config';\nimport { writeFile } from './file';\nimport { workspaceManager } from './workspace';\n\n/**\n * Content part types for multimodal messages\n */\ntype ContentPart = { type: 'text'; text: string } | { type: 'image_url'; image_url: { url: string } };\n\n/**\n * Options for calling the model\n */\nexport interface CallModelOptions {\n question: string;\n imageUrls?: string | string[];\n streaming?: boolean;\n responseFormat?: { type: 'json_object' | 'text' };\n maxTokens?: number;\n}\n\n/**\n * Validate model configuration\n * @param config - Model configuration to validate\n * @throws Error if required fields are missing\n */\nfunction validateModelConfig(config: ModelConfig | null | undefined): asserts config is ModelConfig {\n if (!config || !config.provider || !config.model || !config.baseUrl || !config.apiKey) {\n throw new Error(\n `Invalid model configuration. Required fields: provider, model, baseUrl, apiKey. Please check your config.yaml file.`\n );\n }\n}\n\n/**\n * Call an LLM model with the given options\n * @param options - Configuration options for the model call\n * @returns The model's text response\n */\nexport async function callModel(options: CallModelOptions): Promise<string> {\n const { question, imageUrls, streaming = false, responseFormat, maxTokens } = options;\n\n // Validate input\n if (!question || !question.trim()) {\n throw new Error('Question must be a non-empty string');\n }\n\n // Get and validate config separately\n let config: ModelConfig;\n try {\n config = getModelConfig();\n validateModelConfig(config);\n } catch (error) {\n if (error instanceof Error) {\n logger.printErrorLog(`Configuration error: ${error.message}`);\n }\n throw error;\n }\n\n try {\n const requestConfig = {\n modelName: config.model,\n apiKey: config.apiKey,\n configuration: {\n baseURL: config.baseUrl,\n },\n ...(maxTokens && { maxTokens }),\n temperature: 0.1,\n streaming,\n ...(streaming && {\n streamingOptions: {\n includeUsage: true,\n },\n }),\n ...(!streaming && { streamUsage: true }),\n ...(responseFormat && { modelKwargs: { response_format: responseFormat } }),\n };\n const agentModel = new ChatOpenAI(requestConfig);\n\n // Build multimodal content parts: text + image_url\n const contentParts: ContentPart[] = [];\n contentParts.push({ type: 'text', text: question });\n\n // Add images if provided\n if (imageUrls) {\n const urls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];\n for (const url of urls) {\n if (url && typeof url === 'string' && url.trim()) {\n contentParts.push({ type: 'image_url', image_url: { url: url.trim() } });\n }\n }\n }\n\n // Create user message - use array if multimodal, string if text-only\n const userMessage = new HumanMessage({\n content: contentParts.length > 1 ? contentParts : question,\n });\n\n const message = await agentModel.invoke([userMessage]);\n\n if (!message.text) {\n throw new Error('Model returned empty response');\n }\n\n const debugConfig = getDebugConfig();\n const isDebugEnabled = debugConfig.enabled;\n if (isDebugEnabled) {\n const debugContent = [\n '------------config------------',\n JSON.stringify(requestConfig, null, 2),\n '------------request------------',\n JSON.stringify(contentParts, null, 2),\n '------------response------------',\n JSON.stringify(message.text, null, 2),\n ].join('\\n');\n writeFile(workspaceManager.path?.debug ?? '', `model_${new Date().toISOString()}.md`, debugContent);\n }\n\n return message.text;\n } catch (error) {\n if (error instanceof Error) {\n logger.printErrorLog(`[${config.model}] Error details: ${error.message}`);\n if (error.stack) {\n logger.printTestLog(`[${config.model}] Stack trace: ${error.stack}`);\n }\n }\n throw new Error(`${config.model} model request failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n","import type { FigmaFrameInfo } from '../../../types/figma-types';\nimport type { Protocol } from '../../../types';\nimport { callModel } from '../../../utils/call-model';\nimport { logger } from '../../../utils/logger';\nimport { generateStructurePrompt } from './prompt';\nimport { extractNodePositionsHierarchical, postProcessStructure, populateComponentProps } from './utils';\nimport { extractJSON } from '../../../utils/parser';\n\n/**\n * Structure node - generates component hierarchy from Figma design\n *\n * Responsibilities:\n * 1. Analyzes Figma frame structure using AI model\n * 2. Extracts component relationships and data\n * 3. Generates file paths and naming conventions\n * 4. Populates component props and states for code generation\n *\n * @param state - Current graph state\n * @returns Updated state with protocol\n */\nexport const generateStructure = async (figma: FigmaFrameInfo) => {\n const frames = figma.frames || figma.children;\n const imageWidth = figma.absoluteBoundingBox?.width;\n const thumbnailUrl = figma.thumbnailUrl;\n\n if (!frames || frames.length === 0) {\n logger.printErrorLog('No processed frames found in state');\n throw new Error('No processed frames found');\n }\n\n logger.printInfoLog('Starting structure analysis...');\n\n try {\n // Extract hierarchical position data from Figma frames\n const positions = extractNodePositionsHierarchical(frames);\n const positionsJson = JSON.stringify(positions);\n\n // Generate structure using AI\n const prompt = generateStructurePrompt({\n positions: positionsJson,\n width: imageWidth ? String(imageWidth) : '1440',\n });\n\n logger.printInfoLog('Calling AI model to generate component structure...');\n\n const structureResult = await callModel({\n question: prompt,\n imageUrls: thumbnailUrl,\n responseFormat: { type: 'json_object' },\n maxTokens: 20240,\n });\n\n // Parse AI response\n const jsonContent = extractJSON(structureResult);\n const parsedStructure = JSON.parse(jsonContent) as Protocol | Protocol[];\n\n // Post-process structure: normalize names, populate elements, annotate paths\n logger.printInfoLog('Processing structure tree...');\n postProcessStructure(parsedStructure, frames);\n\n const protocol = (Array.isArray(parsedStructure) ? parsedStructure[0] : parsedStructure) as Protocol;\n\n // Extract component props and states for reusable components\n if (frames && protocol) {\n logger.printInfoLog('Extracting component properties and states...');\n await populateComponentProps(protocol, frames, thumbnailUrl);\n }\n\n logger.printSuccessLog('Component structure generated successfully');\n\n return protocol;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printErrorLog(`Error generating component structure: ${errorMessage}`);\n throw new Error(`Failed to parse component structure: ${errorMessage}`);\n }\n};\n","import type { FigmaFrameInfo, Protocol, FrameData } from '../../../types';\nimport type { SimplifiedFigmaNode, ExtendedFrameStructNode, ParsedDataListResponse } from './types';\nimport path from 'node:path';\nimport { toKebabCase } from '../../../utils/naming';\nimport { extractJSON } from '../../../utils/parser';\nimport { callModel } from '../../../utils/call-model';\nimport { logger } from '../../../utils/logger';\n\n// ============= Figma Node Utilities =============\n/**\n * Simplifies Figma nodes for content extraction, retaining essential fields for AI processing\n * Removes heavy vector data while keeping text content, images, and layout information\n *\n * @param node - The Figma frame node to simplify\n * @returns Simplified node with only essential fields\n */\nexport function simplifyFigmaNodeForContent(node: FigmaFrameInfo): SimplifiedFigmaNode {\n const simple: SimplifiedFigmaNode = {\n id: node.id,\n name: node.name,\n type: node.type,\n };\n\n // Check both url (set by Asset node) and thumbnailUrl (original Figma field)\n const imageUrl = (node as FigmaFrameInfo & { url?: string }).url || node.thumbnailUrl;\n if (imageUrl) {\n simple.url = imageUrl;\n }\n\n if (node.cornerRadius !== undefined) {\n simple.cornerRadius = node.cornerRadius;\n }\n\n if (node.characters !== undefined && node.characters !== null) {\n simple.characters = node.characters;\n }\n\n if (node.visible !== undefined) simple.visible = node.visible;\n\n if (node.absoluteBoundingBox) simple.absoluteBoundingBox = node.absoluteBoundingBox;\n\n if (node.children && Array.isArray(node.children)) {\n simple.children = node.children.map(simplifyFigmaNodeForContent);\n }\n\n if (node.inlineStyles) {\n simple.inlineStyles = node.inlineStyles as Record<string, unknown>;\n }\n\n if (node.style) {\n simple.style = node.style as unknown as Record<string, unknown>;\n }\n\n if (node.strokes && Array.isArray(node.strokes) && node.strokes.length > 0) {\n simple.hasStrokes = true;\n }\n\n return simple;\n}\n\n/**\n * Extract node positions with hierarchical structure preserved\n * Returns nested position data maintaining parent-child relationships\n *\n * @param data - Figma frame data (single frame or array of frames)\n * @returns Hierarchical position data with node information\n */\nexport function extractNodePositionsHierarchical(data: FigmaFrameInfo[] | FigmaFrameInfo | undefined): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n if (!data) {\n return result;\n }\n\n const list = Array.isArray(data) ? data : [data];\n\n for (const item of list) {\n if (item && typeof item === 'object' && item.id) {\n const nodeData: Record<string, unknown> = {};\n\n // Extract position information\n const bounds = item.absoluteBoundingBox || item.absoluteRenderBounds;\n if (bounds) {\n nodeData.x = bounds.x;\n nodeData.y = bounds.y;\n nodeData.w = bounds.width;\n nodeData.h = bounds.height;\n }\n\n // Recursively process children\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n nodeData.children = extractNodePositionsHierarchical(item.children);\n }\n\n result[item.id] = nodeData;\n }\n }\n\n return result;\n}\n\n/**\n * Extract nodes by IDs, including their full subtrees (all children)\n * This allows inspecting the full content of specific component instances\n */\nfunction extractNodesWithSubtreeByIds(tree: FigmaFrameInfo | FigmaFrameInfo[], idList: string[]): FigmaFrameInfo[] {\n const idSet = new Set(idList);\n const result: FigmaFrameInfo[] = [];\n\n const findNodes = (nodes: FigmaFrameInfo[]) => {\n for (const node of nodes) {\n if (idSet.has(node.id)) {\n // Deep clone the node to ensure all fields (including url) are preserved\n const clonedNode = JSON.parse(JSON.stringify(node)) as FigmaFrameInfo;\n result.push(clonedNode);\n // Do not recurse into children of a match, because the match already contains them.\n } else if (node.children && Array.isArray(node.children)) {\n findNodes(node.children);\n }\n }\n };\n\n const nodeArray = Array.isArray(tree) ? tree : [tree];\n findNodes(nodeArray);\n return result;\n}\n\n/**\n * Extract nodes by IDs while preserving hierarchical structure\n * Nodes in idList are kept; if a deep child is in idList but parent isn't, the child is still extracted\n *\n * @param tree - The Figma frame tree to search\n * @param idList - Array of node IDs to extract\n * @param options - Optional settings\n * @param options.includeSubtree - If true, includes all descendants of matched nodes\n * @returns Array of extracted nodes with hierarchy preserved\n */\nexport function extractHierarchicalNodesByIds(\n tree: FigmaFrameInfo | FigmaFrameInfo[],\n idList: string[],\n options?: { includeSubtree?: boolean }\n): FigmaFrameInfo[] {\n if (options?.includeSubtree) {\n return extractNodesWithSubtreeByIds(tree, idList);\n }\n\n const idSet = new Set(idList);\n const result: FigmaFrameInfo[] = [];\n\n // Helper function to check if a node or any of its descendants are in idList\n const hasDescendantInList = (node: FigmaFrameInfo): boolean => {\n if (idSet.has(node.id)) return true;\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n if (hasDescendantInList(child)) {\n return true;\n }\n }\n }\n }\n\n return false;\n };\n\n // Helper function to recursively process a single node\n const processNode = (node: FigmaFrameInfo): FigmaFrameInfo[] => {\n // First check if this node or any descendant is in the list\n if (!hasDescendantInList(node)) {\n return [];\n }\n\n // If current node is in the list, keep it with filtered children\n if (idSet.has(node.id)) {\n const clonedNode: FigmaFrameInfo = { ...node };\n\n // Process children if they exist\n if (node.children && Array.isArray(node.children)) {\n const filteredChildren: FigmaFrameInfo[] = [];\n\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n const processedChildren = processNode(child);\n filteredChildren.push(...processedChildren);\n }\n }\n\n clonedNode.children = filteredChildren.length > 0 ? filteredChildren : [];\n } else {\n clonedNode.children = [];\n }\n\n return [clonedNode];\n } else {\n // Current node is not in the list, but some descendants are\n // Collect all matching descendants and return them (flattened)\n const matchingDescendants: FigmaFrameInfo[] = [];\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n const processedChildren = processNode(child);\n matchingDescendants.push(...processedChildren);\n }\n }\n }\n\n return matchingDescendants;\n }\n };\n\n // Process tree (handle both single node and array)\n const nodeArray = Array.isArray(tree) ? tree : [tree];\n\n for (const node of nodeArray) {\n const processedNodes = processNode(node);\n result.push(...processedNodes);\n }\n\n return result;\n}\n\n// ============= Structure Processing Utilities =============\n\n/**\n * Post-processes the structure tree in a single traversal\n * Performs three operations simultaneously:\n * 1. Normalizes componentName (moves from top-level to data field)\n * 2. Populates elements data from elementIds\n * 3. Annotates with file system paths (path, componentPath, kebabName)\n *\n * @param structure - The parsed structure from AI model\n * @param frames - The Figma frames tree for element extraction\n */\nexport function postProcessStructure(structure?: Protocol | Protocol[] | null, frames?: FigmaFrameInfo[]): void {\n if (!structure) {\n return;\n }\n\n // Utility to join alias path segments (always POSIX '/')\n const joinSegments = (...segments: (string | undefined)[]): string =>\n path.posix.join(...segments.filter((segment): segment is string => Boolean(segment && segment.length)));\n\n const nodes = Array.isArray(structure) ? structure : [structure];\n let rootPath = '@/components';\n\n // Convert component name to kebab-case for file naming\n const toKebabName = (node: Protocol): string => {\n const source = node.data.kebabName || node.data.name || node.id || 'component';\n const kebabName = toKebabCase(source);\n if (!node.data.kebabName) {\n node.data.kebabName = kebabName;\n }\n return kebabName;\n };\n\n const traverse = (node?: Protocol | null, parentPath?: string, level = 0): void => {\n if (!node || !node.data) {\n return;\n }\n\n // 1. Normalize componentName (from top-level to data field)\n const extendedNode = node as ExtendedFrameStructNode;\n const topLevelComponentName = extendedNode.componentName;\n if (topLevelComponentName && !node.data.componentName) {\n node.data.componentName = topLevelComponentName;\n delete extendedNode.componentName;\n }\n\n // 2. Populate elements data from elementIds\n if (frames) {\n const nodeData = node.data as FrameData & { elementIds?: string[] };\n const elementIds = nodeData.elementIds;\n if (elementIds && Array.isArray(elementIds)) {\n if (elementIds.length > 0) {\n node.data.elements = extractHierarchicalNodesByIds(frames, elementIds, { includeSubtree: true });\n } else {\n node.data.elements = [];\n }\n delete nodeData.elementIds;\n } else {\n node.data.elements = node.data.elements || [];\n }\n }\n\n // 3. Annotate with file system paths\n const segment = toKebabName(node);\n let currentPath: string;\n\n if (level === 0) {\n // Root node always uses base path\n currentPath = rootPath;\n rootPath = currentPath;\n } else {\n const ancestorPath = parentPath || rootPath;\n currentPath = joinSegments(ancestorPath, segment);\n }\n\n // For reusable components, generate flat componentPath (non-hierarchical)\n if (node.data.componentName) {\n const componentKebabName = toKebabCase(node.data.componentName);\n node.data.componentPath = joinSegments(rootPath, componentKebabName);\n node.data.path = node.data.componentPath;\n }\n\n node.data.path = currentPath;\n\n // Recursively process children\n if (Array.isArray(node.children) && node.children.length > 0) {\n node.children.forEach(child => traverse(child, node.data.path, level + 1));\n }\n };\n\n nodes.forEach(node => {\n if (!node || !node.data) {\n return;\n }\n traverse(node, undefined, 0);\n });\n}\n\n/**\n * Extract component groups from protocol children\n * Groups components by their componentName for batch processing\n *\n * @param protocol - The protocol node to analyze\n * @returns Map of componentName to array of instances\n */\nexport function extractComponentGroups(node: Protocol): Map<string, Protocol[]> {\n if (!node || !node.children || node.children.length === 0) return new Map();\n const componentGroups = new Map<string, Protocol[]>();\n const validChildren = node.children.filter(c => c && c.data);\n\n validChildren.forEach(child => {\n const name = child.data.componentName;\n if (name) {\n if (!componentGroups.has(name)) {\n componentGroups.set(name, []);\n }\n componentGroups.get(name)!.push(child);\n }\n });\n\n return componentGroups;\n}\n\n/**\n * Applies props and state to the protocol node\n * @param parsed - The parsed data list response\n * @param node - The protocol node to apply the props and state to\n * @param compName - The name of the component\n * @param group - The group of components\n * @param isList - Whether the component is a list\n */\nexport function applyPropsAndStateToProtocol(\n parsed: ParsedDataListResponse,\n node: Protocol,\n compName: string,\n group: Protocol[],\n isList: boolean\n): void {\n if (parsed && parsed.state && Array.isArray(parsed.state)) {\n if (isList) {\n if (!node.data.states) {\n node.data.states = [];\n }\n\n node.data.states.push({\n state: parsed.state,\n componentName: compName,\n componentPath: group[0]?.data.componentPath || '',\n });\n\n const originalChildren: Protocol[] = node.children || [];\n const newChildren: Protocol[] = [];\n const processedComponentNames = new Set<string>();\n\n for (const child of originalChildren) {\n const childName = child.data.componentName;\n if (childName === compName) {\n if (!processedComponentNames.has(childName)) {\n child.data.name = childName;\n child.id = childName;\n const cleanKebabName = toKebabCase(childName);\n child.data.kebabName = cleanKebabName;\n delete child.data.path;\n\n if (parsed.props && Array.isArray(parsed.props)) {\n child.data.props = parsed.props;\n }\n\n newChildren.push(child);\n processedComponentNames.add(childName);\n }\n } else {\n newChildren.push(child);\n }\n }\n\n node.children = newChildren;\n }\n }\n}\n\n/**\n * Extracts component properties and states from repeated component instances\n * For components that appear multiple times (e.g., cards in a grid), this function:\n * 1. Groups instances by componentName\n * 2. Uses AI to extract data variations (text, images, etc.)\n * 3. Generates props schema for the component\n * 4. Collapses duplicate instances into a single template + state array\n *\n * @param node - Current structure node to process\n * @param frames - Figma frames for reference\n * @param thumbnailUrl - Design thumbnail URL for AI visual context\n */\nexport async function populateComponentProps(node: Protocol, frames: FigmaFrameInfo[], thumbnailUrl?: string): Promise<void> {\n if (!node || !node.children || node.children.length === 0) return;\n\n const componentGroups = extractComponentGroups(node);\n\n // Process each component group to extract props and data\n for (const [compName, group] of componentGroups) {\n if (group.length === 0) continue;\n\n const isList = group.length > 1;\n const allElements = group.flatMap(g => g.data.elements || []);\n const simplifiedNodes = allElements\n .filter((n): n is FigmaFrameInfo => typeof n === 'object' && n !== null)\n .map(n => simplifyFigmaNodeForContent(n));\n const figmaDataJson = JSON.stringify(simplifiedNodes);\n const containerName = node.data.name || 'Container';\n\n try {\n const { extractDataListPrompt } = await import('./prompt');\n const prompt = extractDataListPrompt({\n containerName,\n childComponentName: compName,\n figmaData: figmaDataJson,\n });\n\n const result = await callModel({\n question: prompt,\n imageUrls: thumbnailUrl,\n responseFormat: { type: 'json_object' },\n });\n\n const json = extractJSON(result);\n const parsed = JSON.parse(json) as ParsedDataListResponse;\n applyPropsAndStateToProtocol(parsed, node, compName, group, isList);\n } catch (e) {\n logger.printErrorLog(\n `Failed to extract data list for ${compName} in ${containerName}: ${e instanceof Error ? e.message : String(e)}`\n );\n }\n }\n\n // Recursively process children\n for (const child of node.children) {\n await populateComponentProps(child, frames, thumbnailUrl);\n }\n}\n","/**\n * Standard naming utilities for the project.\n * Ensures consistency between kebab-case file paths and PascalCase component names.\n */\n\n/**\n * Converts a string to PascalCase (e.g. \"my-component\" -> \"MyComponent\")\n * Used for Component Names and imports.\n */\nexport function toPascalCase(str: string): string {\n // 1. Replace special chars with space\n // 2. Split by space or capital letters\n // 3. Capitalize first letter of each part\n return str\n .replace(/[^a-zA-Z0-9]+/g, ' ')\n .trim()\n .split(/\\s+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Converts a string to kebab-case (e.g. \"MyComponent\" -> \"my-component\")\n * Used for file paths and directories.\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Split camelCase\n .replace(/[^a-zA-Z0-9]+/g, '-') // Replace non-alphanumeric with hyphen\n .toLowerCase()\n .replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens\n}\n","/**\n * Response parsing utilities for AI model outputs.\n * Handles extraction of structured content from markdown-formatted responses.\n */\n\nimport { FileInfo } from '../types/file-types';\n\n/**\n * Extract JSON content from markdown code blocks\n * Handles cases where AI models wrap JSON in ```json ... ``` or ``` ... ```\n * If no code block markers are found, returns the original content\n *\n * @param response - Model response that may contain markdown code blocks\n * @returns Extracted JSON string without markdown formatting\n *\n * @example\n * // Input: \"```json\\n{\\\"key\\\": \\\"value\\\"}\\n```\"\n * // Output: \"{\\\"key\\\": \\\"value\\\"}\"\n */\nexport function extractJSON(response: string): string {\n // Try to match ```json ... ``` format first\n const jsonBlockMatch = response.match(/```json\\s*\\n([\\s\\S]*?)\\n```/);\n if (jsonBlockMatch && jsonBlockMatch[1]) {\n return jsonBlockMatch[1].trim();\n }\n\n // Try to match generic ``` ... ``` format (with newlines around content)\n const codeBlockMatch = response.match(/```\\s*\\n([\\s\\S]*?)\\n```/);\n if (codeBlockMatch && codeBlockMatch[1]) {\n return codeBlockMatch[1].trim();\n }\n\n // Fallback: no proper code block; use full content but strip loose markdown fences.\n // Some models return raw JSON with trailing \"```\" (e.g. ...]```) or leading ```\\n.\n let cleaned = response.trim();\n cleaned = cleaned.replace(/^\\s*```(?:json)?\\s*\\n?/g, '').replace(/\\s*```+\\s*$/g, '');\n return cleaned;\n}\n\n/**\n * Extract code content from markdown code blocks\n * Handles cases where AI models wrap code in ```tsx ... ``` or ``` ... ```\n * If no code block markers are found, returns the original content\n */\nexport function extractCode(response: string): string {\n // 1. Try to extract content strictly within ``` fences\n // Regex captures content between ```language and ```\n const codeBlockMatch = response.match(/```(?:tsx|typescript|react|js|javascript|json|css|less|scss)?\\s*\\n([\\s\\S]*?)```/);\n\n if (codeBlockMatch && codeBlockMatch[1]) {\n return codeBlockMatch[1].trim();\n }\n\n // 2. Fallback: If no clear block structure is found (or fences are missing/malformed),\n // try to strip loose fences just in case, but usually method 1 catches the block.\n // If the model returned JUST code without fences, this preserves it.\n // If the model returned \"Here is code: code\", this returns the whole string (which might be bad, but safest fallback).\n\n // Removing loose fences if any remain (unlikely if method 1 failed but good for cleanup)\n const cleaned = response\n .replace(/```(tsx|typescript|react|js|javascript|json|css|less|scss)?/g, '')\n .replace(/```/g, '')\n .trim();\n\n return cleaned;\n}\n\n/**\n * Extract multiple files from content with file headers\n * Format:\n * ## filename.tsx\n * ```tsx\n * code...\n * ```\n *\n * ## filename.css\n * ```css\n * styles...\n * ```\n */\nexport function extractFiles(content: string): FileInfo[] {\n const files: FileInfo[] = [];\n\n // Match file sections: ## filename\\n```language\\ncode\\n```\n // Allow optional whitespace around newlines for flexibility\n const fileRegex = /##\\s+([^\\n]+)\\s*\\n\\s*```(?:\\w+)?\\s*\\n([\\s\\S]*?)\\n\\s*```/g;\n let match;\n\n while ((match = fileRegex.exec(content)) !== null) {\n if (match[1] && match[2]) {\n const filename = match[1].trim();\n const code = match[2].trim();\n files.push({ filename, content: code });\n }\n }\n\n return files;\n}\n","import { GraphState } from '../../state';\nimport { figmaTool } from '../../tools/figma-tool';\nimport { FigmaFrameInfo, FigmaUrlInfo } from '../../types/figma-types';\nimport { getFigmaConfig } from '../../utils/config';\nimport { writeFile } from '../../utils/file';\nimport { logger } from '../../utils/logger';\nimport { generateStructure } from './structure';\nimport { ImageNode } from '../../tools/figma-tool/types';\nimport { workspaceManager } from '../../utils/workspace';\n\n/**\n * 'process' node, responsible for generating the protocol for frontend code generation.\n *\n * This function serves as a unified workflow that can be consumed by:\n * 1. LangGraph Node: As part of the design2code graph workflow\n * 2. CLI Command: `f2p` (figma2protocol) - full protocol generation\n * 3. CLI Command: `images` (get-images) - partial workflow (fetch + download only)\n * 4. Script Execution: Direct execution via tsx/node\n *\n * Workflow Steps:\n * - Step 1: Fetch and clean Figma document from API\n * - Step 2: Detect and download images from Figma document\n * - Step 3: Simplify image nodes by replacing properties with URLs\n * - Step 4: Process styles (convert Figma styles to CSS)\n * - Step 5: Write protocol.json and images.json to workspace\n * @param state - The state of the graph.\n * @returns The state of the graph.\n */\nexport const generateProtocol = async (state: GraphState) => {\n const assetsDir = workspaceManager.resolveAppSrc(state.workspace, 'assets');\n const processDir = state.workspace.process;\n const { document, imageNodesMap } = await executeFigmaAndImagesActions(state.urlInfo, assetsDir, processDir);\n\n /* Simplify image nodes in Figma document by replacing redundant properties with url */\n const simplifiedDocument = figmaTool.simplifyImageNodes(document, imageNodesMap);\n /* Process styles (convert Figma styles to CSS) */\n const processedStyleDocument = figmaTool.processedStyle(simplifiedDocument);\n /* Generate structure */\n const protocol = await generateStructure(processedStyleDocument);\n\n // Write protocol.json (contains all Figma data in data.elements)\n writeFile(state.workspace.process, 'protocol.json', JSON.stringify(protocol, null, 2));\n logger.printInfoLog(`Please check the output in the workspace: ${state.workspace.process}`);\n\n return {\n protocol,\n figmaInfo: {\n thumbnail: processedStyleDocument?.thumbnailUrl || document?.thumbnailUrl || '',\n },\n };\n};\n\n/**\n * Executes the Figma and images actions.\n * @param state - The state of the graph.\n * @returns The state of the graph with imageNodesMap as Map<string, ImageNode>.\n */\nexport const executeFigmaAndImagesActions = async (\n urlInfo: FigmaUrlInfo,\n assetsDir: string,\n processDir: string\n): Promise<{ document: FigmaFrameInfo; imageNodesMap: Map<string, ImageNode> }> => {\n const { fileId, nodeId } = urlInfo;\n if (!fileId || !nodeId) {\n throw new Error('Invalid Figma URL');\n }\n\n const token = getFigmaConfig().token;\n if (!token) {\n throw new Error('Figma API token is required');\n }\n\n /* Fetch and clean Figma document */\n const document = await figmaTool.fetchAndClean(fileId, nodeId, token);\n if (!document) {\n throw new Error('Failed to fetch and clean Figma document');\n }\n writeFile(processDir, 'figma.json', JSON.stringify(document, null, 2));\n logger.printSuccessLog(`Figma document fetched and cleaned successfully`);\n\n /* Detect and download images from Figma document */\n const downloadResult: { successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> } =\n await figmaTool.downloadImages(fileId, token, assetsDir, document);\n const { successCount, failCount, imageNodesMap } = downloadResult;\n\n if (successCount) {\n logger.printSuccessLog(`Downloaded ${successCount} images`);\n }\n if (failCount) {\n logger.printWarnLog(`Failed to download ${failCount} images`);\n }\n\n /* Write images.json with array format for JSON compatibility */\n const resultsArray = Array.from(imageNodesMap.values());\n writeFile(processDir, 'images.json', JSON.stringify(resultsArray, null, 2));\n\n return {\n document,\n imageNodesMap,\n };\n};\n","/**\n * Skill API Module\n *\n * This module exports all utilities needed for SKILL execution in IDEs like Cursor or Claude Code.\n * It intentionally does NOT include model calling logic - the IDE's AI agent handles that.\n *\n * Design Philosophy:\n * - Export data processing utilities (Figma, Protocol, etc.)\n * - Export prompt generation functions (same as CLI for consistency)\n * - Let the IDE's AI agent execute the prompts using its own model\n * - Provide helper functions for protocol manipulation\n */\n\n// ============= Type Exports =============\nexport type { Protocol, FigmaFrameInfo, ParsedDataListResponse } from './types';\n\n// ============= Initial Agent Exports =============\nexport { INITIAL_AGENT_SYSTEM_PROMPT } from './agents/initial-agent/prompt';\nexport { initialAgentInstruction } from './agents/initial-agent/instruction';\n\n// ============= Protocol Agent Exports =============\nexport { parseFigmaUrl } from './utils/url-parser';\nexport { executeFigmaAndImagesActions } from './nodes/process';\nexport { figmaTool } from './tools/figma-tool';\nexport {\n extractNodePositionsHierarchical,\n postProcessStructure,\n extractComponentGroups,\n simplifyFigmaNodeForContent,\n extractHierarchicalNodesByIds,\n applyPropsAndStateToProtocol,\n} from './nodes/process/structure/utils';\nexport { generateStructurePrompt, extractDataListPrompt } from './nodes/process/structure/prompt';\nexport { extractJSON } from './utils/parser';\n\n// ============= Code Generation Constants (Same as CLI) =============\nexport { flattenPostOrder, detectRenderingModes, saveGeneratedCode } from './nodes/code/utils';\nexport { DEFAULT_STYLING } from './nodes/code/constants';\nexport { generateFramePrompt, generateComponentPrompt } from './nodes/code/prompt';\nexport { workspaceManager } from './utils/workspace';\n\n// ============= Image Utilities (reuse existing figma-tool) =============\nexport { downloadImage } from './tools/figma-tool/images';\n\n// ============= Naming Utilities (ensure path consistency) =============\nexport { toKebabCase, toPascalCase } from './utils/naming';\n","// ============================================\n// Common Prompt Sections\n// ============================================\n\nconst STYLING_GUIDELINES = `\n - **Style Consistency**: Implement styles using the technical stack and libraries listed in <styling>.\n - **Strict Restriction**: Absolutely ONLY use the technical stack and libraries listed in <styling>. Do NOT use any other styling methods, libraries, or frameworks (e.g., if clsx is not listed, do not use clsx).\n - **Default Styling**: If <styling> is empty or does not contain specific libraries, DEFAULT to standard vanilla CSS.\n \n - **Tailwind CSS + CSS Modules (CRITICAL)**:\n - If the stack includes BOTH Tailwind and CSS Modules (Less/SCSS), use them correctly:\n 1. **Tailwind utilities**: Use DIRECTLY in JSX className (e.g., \\`className=\"flex items-center gap-4\"\\`)\n 2. **CSS Modules**: Use ONLY for complex styles that can't be expressed with Tailwind utilities (e.g., gradients, animations, pseudo-elements)\n 3. **NEVER use \\`@apply\\` in CSS Module files** - it's a Tailwind-specific directive that doesn't work in Less/SCSS\n 4. Example correct usage:\n TSX: \\`<div className={\\`flex \\${styles.customGradient}\\`}>\\`\n Less: \\`.customGradient { background: linear-gradient(...); }\\`\n \n - **CSS Modules Only**: If the tech stack specifies CSS Modules without Tailwind:\n 1. Create a corresponding style file (e.g., \\`index.module.less\\`, \\`index.module.scss\\`, or \\`index.module.css\\`)\n 2. Import it as \\`import styles from './index.module.[ext]';\\` in the TSX\n 3. Define all styles in the style file using standard CSS/Less/SCSS syntax\n 4. Use \\`styles.className\\` in JSX`;\n\nconst ASSETS_HANDLING = `\n - **CRITICAL**: For any image URL starting with \\`@/assets\\`, you MUST import it at the top of the file.\n - **Asset Name Matching**: \n - Check the \\`<available_assets>\\` list for actual filenames in the project.\n - Asset filenames follow the pattern: \\`kebab-case-name-id1-id2.ext\\` (e.g., \"Star 2.svg\" → \"star-2-1-2861.svg\")\n - Match the base name (ignoring spaces, case, and ID suffix): \"@/assets/arXiv.svg\" → look for \"arxiv-*.svg\" in the list\n - Use the EXACT filename from the available assets list in your import.\n - Example: If available_assets contains \"arxiv-1-2956.svg\", use:\n \\`import ArXivIcon from '@/assets/arxiv-1-2956.svg';\\`\n - **Usage**: \\`<img src={ArXivIcon} />\\`, do not use backgroundImage property.\n - **NEVER** use the string path directly in JSX or styles.`;\n\nconst DOM_IDS_REQUIREMENT = `\n - Assign \\`id\\` attributes to the main container and any internal elements, matching \\`frame_details\\`.`;\n\nconst REACT_IMPORT_RULE = `\n - Do **NOT** include \\`import React from 'react';\\` at the top of the file.`;\n\nconst FILE_NAMING_CONVENTION = `\n - ALWAYS name the main component file \\`index.tsx\\`.\n - ALWAYS name the style file (if applicable) \\`index.module.[css|less|scss]\\`.\n - NEVER use PascalCase or other names for filenames (e.g., DO NOT use \\`MainFrame.tsx\\` or \\`Button.tsx\\`).`;\n\nconst OUTPUT_FORMAT = `\n <output_format>\n If only one file (TSX) is needed:\n \\`\\`\\`tsx\n // code...\n \\`\\`\\`\n\n If multiple files are needed (e.g., TSX + Styles):\n ## index.tsx\n \\`\\`\\`tsx\n // code...\n \\`\\`\\`\n\n ## index.module.[css|less|scss]\n \\`\\`\\`[css|less|scss]\n // styles...\n \\`\\`\\`\n </output_format>`;\n\n// ============================================\n// Prompt Functions\n// ============================================\n\n/**\n * Generate children rendering instructions based on detected modes\n */\nfunction generateChildrenPropsInstructions(modes: { hasStates: boolean; hasIndependentChildren: boolean }): string {\n const instructions: string[] = [];\n\n if (modes.hasStates) {\n instructions.push(`\n - **List Rendering (States-based)**:\n - Check if \\`<frame_details>\\` contains a \\`states\\` property (array).\n - Each state entry has: \\`state\\` (data array), \\`componentName\\`, \\`componentPath\\`.\n - Implementation:\n \\`\\`\\`tsx\n import ComponentName from 'path';\n \n {states[0].state.map((item, index) => (\n <ComponentName key={index} {...item} />\n ))}\n \\`\\`\\`\n - **CRITICAL - Only Use State Data**:\n - **ONLY** pass props that exist in the state data objects.\n - **DO NOT** add extra props like \\`content\\`, \\`className\\`, or any other fields not present in state.\n - **DO NOT** create or invent additional data - use exactly what's in the state array.\n - Example: If state has \\`{iconSrc, title, description}\\`, only pass those three props.\n - **Asset Imports**: If state data contains image paths (e.g., \\`imageSrc\\`, \\`iconSrc\\`), \n import them at the top and pass as values.`);\n }\n\n if (modes.hasIndependentChildren) {\n instructions.push(`\n - **Independent Components (No Props) - CRITICAL**:\n - If a child has NO \\`componentName\\` and NO \\`properties\\`, render as \\`<ComponentName />\\` without any props.\n - These components use default values or hardcoded content internally.`);\n }\n\n return instructions.join('\\n');\n}\n\nexport const generateFramePrompt = ({\n childrenImports,\n frameDetails,\n assetFiles,\n styling,\n renderingModes,\n}: {\n childrenImports: string;\n frameDetails: string;\n assetFiles?: string;\n styling: string;\n renderingModes: {\n hasStates: boolean;\n hasIndependentChildren: boolean;\n };\n}) => {\n return `\n<system_instructions>\n <role>\n You are a React Architect.\n Your task is to assemble a \"Frame\" component that composes multiple child components based on Figma layout data.\n </role>\n\n <input_context>\n <frame_details>${frameDetails}</frame_details>\n <children>${childrenImports}</children>\n <styling>${styling}</styling>\n ${assetFiles ? `<available_assets>Available asset files: ${assetFiles}</available_assets>` : ''}\n\n <frame_structure>\n The \\`frame_details\\` parameter contains:\n - \\`layout\\`: Layout information for the frame (flex/grid/absolute)\n - \\`elements\\`: Array of CSS styles and asset URLs for all elements in this component (colors, spacing, fonts, backgrounds, etc.)\n - \\`states\\` (optional): If present, this frame contains reusable components. Each state entry includes:\n * \\`state\\`: Array of data objects to be used as props for component instances\n * \\`componentName\\`: Name of the reusable component\n * \\`componentPath\\`: Import path for the component\n Use \\`states\\` data to render multiple instances of reusable components via \\`.map()\\`.\n </frame_structure>\n </input_context>\n\n <requirements>\n <req_1>\n **Children Components & Props (CRITICAL)**:\n - The \\`<children>\\` field describes child components with their import paths and prop types.\n${generateChildrenPropsInstructions(renderingModes)}\n \n - **Component Imports (CRITICAL)**:\n - You MUST use the exact import path provided in the \\`<children>\\` list for each component.\n - **Example**:\n - Provided: \\`{\"name\": \"TaskGrid\", \"path\": \"@/components/tasks-section/task-grid\"}\\`\n - CORRECT: \\`import TaskGrid from \"@/components/tasks-section/task-grid\";\\`\n - WRONG: \\`import TaskGrid from \"@/components/task-grid\";\\` (Do NOT do this!)\n </req_1>\n \n <req_2>\n **Layout & Styling**:\n - Use \\`layout_data\\` for dimensions, spacing, and flow (flex/grid).\n${STYLING_GUIDELINES}\n - Use responsive utilities provided by the chosen libraries to ensure the component is adaptive.\n - Use \\`css_context\\` for exact background styles, gradients, and shadows.\n - Use \\`relative\\` positioning for the container.\n - Use \\`spacing\\` field in <frame_details> to set the spacing between elements\n </req_2>\n \n <req_3>\n **Images & Assets**:\n${ASSETS_HANDLING}\n </req_3>\n \n <req_4>\n **DOM IDs**:\n${DOM_IDS_REQUIREMENT}\n </req_4>\n \n <req_5>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_5>\n \n <req_6>\n **File Naming**:\n${FILE_NAMING_CONVENTION}\n </req_6>\n </requirements>\n\n${OUTPUT_FORMAT}\n</system_instructions>\n`.trim();\n};\n\nexport const generateComponentPrompt = ({\n componentName,\n componentDetails,\n styling,\n assetFiles,\n}: {\n componentName: string;\n componentDetails: string;\n styling: string;\n assetFiles?: string;\n}) => {\n return `\n<system_instructions>\n <role>\n You are a Pixel-Perfect React Frontend Engineer.\n Your goal is to implement a specific UI component from Figma design data with 100% visual fidelity while ensuring header scroll to sections on click.\n </role>\n\n <input_context>\n <component_details>${componentDetails}</component_details>\n ${assetFiles ? `<available_assets>Available asset files: ${assetFiles}</available_assets>` : ''}\n <styling>${styling}</styling>\n\n <component_structure>\n The \\`component_details\\` parameter contains:\n - \\`elements\\`: Array of CSS styles and asset URLs for all elements in this component (colors, spacing, fonts, backgrounds, etc.)\n - \\`componentName\\` (optional): If present, indicates this is a **reusable component**; if absent, it's a **regular component**\n - \\`props\\` (optional): Props interface definition, only present when \\`componentName\\` exists\n </component_structure>\n\n <global_styles>\n .gradientBorder {\n position: relative;\n z-index: 0;\n &::before {\n content: '';\n position: absolute;\n inset: 0;\n padding: 1px; // From strokeWidth\n border-radius: inherit;\n background: linear-gradient(278deg, rgba(170, 255, 248, 0.60) 1.63%, rgba(71, 169, 255, 0.60) 104.34%); // From strokeColor\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: destination-out;\n mask-composite: exclude;\n z-index: -1;\n }\n } \n </global_styles>\n\n <visual_reference>\n Refer to the conversation history for the page thumbnail and context.\n Use it to verify visual details like shadows, gradients, and spacing.\n </visual_reference>\n </input_context>\n\n <requirements>\n <req_1>\n **High Fidelity & Responsive**:\n${STYLING_GUIDELINES}\n - **CRITICAL**: Use exact design values from \\`component_details.elements\\` (colors, spacing, font sizes, gradients, shadows, etc.)\n - Use responsive utilities provided by the chosen libraries\n - For complex styles (gradients, shadows), use values from \\`elements\\` directly in CSS Modules or inline styles\n - For gradient rounded borders, CHECK \\`global_styles\\` for \\`.gradientBorder\\` mixin\n </req_1>\n\n <req_2>\n **DOM Identification**:\n - EVERY DOM element corresponding to a Figma node MUST have an \\`id\\` attribute.\n - The \\`id\\` value must match the \\`id\\` in the Figma Data exactly.\n - This is crucial for automated position validation.\n </req_2>\n\n <req_3>\n **Images & Assets**:\n${ASSETS_HANDLING}\n </req_3>\n\n <req_4>\n **Semantic HTML**:\n - Use semantic tags: \\`<header>\\`, \\`<footer>\\`, \\`<nav>\\`, \\`<article>\\`, \\`<section>\\`, \\`<button>\\`.\n </req_4>\n\n <req_5>\n **Layout Strategy**:\n - PREFER relative/flex/grid layout.\n - Use absolute positioning ONLY for decoration/overlays or if Figma structure explicitly implies overlay.\n </req_5>\n\n <req_6>\n **Naming Conventions**:\n - Component Name: **${componentName}** (PascalCase).\n - Export default.\n </req_6>\n\n <req_7>\n **Component Type & Props (CRITICAL)**:\n \n **IF \\`component_details.componentName\\` exists:**\n - This is a **reusable component**\n - Generate props interface from \\`component_details.props\\`: \\`interface ${componentName}Props { ... }\\`\n - Reference props in JSX: \\`{title}\\`, \\`<img src={iconSrc} />\\` (NOT hardcoded values)\n \n **IF \\`component_details.componentName\\` does NOT exist:**\n - This is a **regular component**\n - Do NOT generate props interface\n - Directly hardcode content from \\`component_details.elements\\` into JSX\n \n **Both types**: Do NOT repeat JSX to simulate grids; parent components handle iteration.\n </req_7>\n\n <req_9>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_9>\n \n <req_10>\n **File Naming**:\n${FILE_NAMING_CONVENTION}\n </req_10>\n </requirements>\n\n${OUTPUT_FORMAT}\n</system_instructions>\n`.trim();\n};\n\nexport const injectRootComponentPrompt = ({\n appContent,\n componentName,\n componentPath,\n}: {\n appContent: string;\n componentName: string;\n componentPath: string;\n}) => {\n return `\n<system_instructions>\n <role>\n You are a React code refactoring expert.\n Your task is to inject a root component into an existing App.tsx file with minimal changes.\n </role>\n\n <input_context>\n <current_app_content>\n${appContent}\n </current_app_content>\n <component_to_inject>\n - Component Name: ${componentName}\n - Import Path: ${componentPath}\n </component_to_inject>\n </input_context>\n\n <requirements>\n <req_1>\n **Import Statement**:\n - Add the import statement at the top of the file: \\`import ${componentName} from '${componentPath}';\\`\n - Place it after existing imports, before the component definition.\n - Do NOT remove or modify existing imports.\n </req_1>\n\n <req_2>\n **Component Rendering**:\n - Inside the App component's return statement, replace the existing content with \\`<${componentName} />\\`.\n - If the return contains a wrapper div or other container, keep it and place the component inside.\n - Preserve any existing className, styles, or other attributes on wrapper elements.\n </req_2>\n\n <req_3>\n **Preserve Existing Code**:\n - Keep all existing imports (CSS, Less, etc.)\n - Preserve any hooks, state, or logic inside the App component\n - Maintain the component structure and export statement\n - Do NOT add comments or explanatory text\n </req_3>\n\n <req_4>\n **Minimal Changes**:\n - Only add the necessary import and render the component\n - Do NOT refactor or optimize existing code\n - Do NOT change formatting or styling unless necessary\n - Do NOT add TypeScript types unless they already exist\n </req_4>\n\n <req_5>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_5>\n </requirements>\n\n <output_format>\n Return ONLY the complete updated App.tsx code without markdown code blocks or explanation.\n The output should be valid TypeScript/React code that can be directly written to the file.\n </output_format>\n</system_instructions>\n`.trim();\n};\n","export const DEFAULT_STYLING = {\n approach: 'Tailwind V4 and Less',\n libraries: [\n {\n name: 'Tailwind V4',\n role: 'utility_first',\n },\n {\n name: 'Less',\n role: 'css_preprocessor',\n },\n ],\n};\n\nexport const DEFAULT_APP_CONTENT = `function App() {\n return (\n <div>\n {/* Component will be injected here */}\n </div>\n );\n}\n\nexport default App;`;\n","import fs from 'fs';\nimport { GraphState } from '../../state';\nimport { logger } from '../../utils/logger';\nimport { callModel } from '../../utils/call-model';\nimport { promisePool } from '../../utils/promise-pool';\nimport { generateFramePrompt, generateComponentPrompt, injectRootComponentPrompt } from './prompt';\nimport { Protocol } from '../../types';\nimport { createFiles, writeFile } from '../../utils/file';\nimport { DEFAULT_APP_CONTENT, DEFAULT_STYLING } from './constants';\nimport path from 'path';\nimport { extractCode, extractFiles } from '../../utils/parser';\nimport { workspaceManager } from '../../utils/workspace';\nimport { CodeCache, isComponentGenerated, saveComponentGenerated, isAppInjected, saveAppInjected } from '../../utils/code-cache';\n\n/**\n * Process a node tree and generate code for all nodes\n * Uses post-order traversal (children first, then parent)\n */\nexport async function processNode(state: GraphState, cache: CodeCache): Promise<number> {\n // Read asset files list once for the entire generation run\n const assetFilesList = getAssetFilesList(state);\n\n // Flatten tree using post-order traversal (children first, then parent)\n const flatNodes = flattenPostOrder(state.protocol!);\n const total = flatNodes.length;\n\n if (total === 0) {\n logger.printWarnLog('No components found in structure to generate.');\n return 0;\n }\n\n logger.printInfoLog(`Processing ${total} nodes...`);\n\n let processedCount = 0;\n let skippedCount = 0;\n\n const processSingleNode = async (currentNode: Protocol) => {\n const componentName = currentNode.data.name || currentNode.data.componentName || 'UnknownComponent';\n const nodeId = currentNode.id;\n\n // Check if component is already generated\n if (isComponentGenerated(cache, nodeId)) {\n skippedCount++;\n logger.printInfoLog(`[${processedCount + skippedCount}/${total}] ⏭️ Skipping (cached): ${componentName}`);\n return;\n }\n\n const progressInfo = `[${++processedCount + skippedCount}/${total}]`;\n\n const isLeaf = !currentNode.children?.length;\n if (isLeaf) {\n await generateComponent(currentNode, state, assetFilesList, progressInfo);\n } else {\n await generateFrame(currentNode, state, assetFilesList, progressInfo);\n }\n\n // Mark component as generated and save immediately to prevent cache loss on interruption\n saveComponentGenerated(cache, nodeId, state.workspace);\n };\n\n // Process nodes with concurrency control\n await promisePool(flatNodes, processSingleNode);\n\n if (skippedCount > 0) {\n logger.printInfoLog(`⏭️ Skipped ${skippedCount} cached components`);\n }\n logger.printSuccessLog(`Generated ${processedCount} components`);\n return processedCount;\n}\n\n/**\n * Flatten tree into array using post-order traversal\n */\nexport function flattenPostOrder(node: Protocol): Protocol[] {\n const result: Protocol[] = [];\n\n function traverse(n: Protocol) {\n n.children?.forEach(child => traverse(child));\n result.push(n);\n }\n\n traverse(node);\n return result;\n}\n\n/**\n * Detect which rendering modes are used in this frame\n */\nexport function detectRenderingModes(node: Protocol): {\n hasStates: boolean;\n hasIndependentChildren: boolean;\n} {\n const hasStates = !!node.data.states?.length;\n\n let hasIndependentChildren = false;\n\n (node.children || []).forEach(child => {\n if (!child.data.componentName) {\n hasIndependentChildren = true;\n }\n });\n\n return { hasStates, hasIndependentChildren };\n}\n\n/**\n * Generate a frame/container component\n * Frames compose multiple child components based on layout\n */\nexport async function generateFrame(node: Protocol, state: GraphState, assetFilesList: string, progressInfo: string): Promise<void> {\n const frameName = node.data.name;\n logger.printInfoLog(`${progressInfo} 🖼️ Generating Frame: ${frameName}`);\n\n // Build children imports information\n const childrenImports = (node.children || []).map(child => ({\n name: child.data.name || '',\n path: child.data.path,\n }));\n\n // Detect rendering modes\n const renderingModes = detectRenderingModes(node);\n\n // Generate prompt\n const prompt = generateFramePrompt({\n frameDetails: JSON.stringify(node.data),\n childrenImports: JSON.stringify(childrenImports),\n styling: JSON.stringify(DEFAULT_STYLING),\n assetFiles: assetFilesList,\n renderingModes,\n });\n\n // Call AI model\n const code = await callModel({\n question: prompt,\n imageUrls: state.figmaInfo.thumbnail,\n });\n\n // Save generated files\n const componentPath = node.data.path || '';\n const filePath = workspaceManager.resolveAppSrc(state.workspace, workspaceManager.resolveComponentPath(componentPath));\n saveGeneratedCode(code, filePath);\n logger.printSuccessLog(`Successfully generated frame: ${frameName}`);\n}\n\n/**\n * Generate a component (leaf or reusable)\n * Components are self-contained UI elements driven by props\n */\nexport async function generateComponent(node: Protocol, state: GraphState, assetFilesList: string, progressInfo: string): Promise<void> {\n const componentName = node.data.componentName || node.data.name || 'UnknownComponent';\n const componentPath = node.data.componentPath || node.data.path || '';\n\n logger.printInfoLog(`${progressInfo} 📦 Generating Component: ${componentName}`);\n\n // Generate prompt\n const prompt = generateComponentPrompt({\n componentName,\n componentDetails: JSON.stringify(node.data),\n styling: JSON.stringify(DEFAULT_STYLING),\n assetFiles: assetFilesList,\n });\n\n // Call AI model\n const code = await callModel({\n question: prompt,\n imageUrls: state.figmaInfo.thumbnail,\n });\n\n // Save generated files\n const filePath = workspaceManager.resolveAppSrc(state.workspace, workspaceManager.resolveComponentPath(componentPath));\n saveGeneratedCode(code, filePath);\n logger.printSuccessLog(`Successfully generated component: ${componentName}`);\n}\n\n/**\n * Helper function to save generated code (handles both single and multi-file output)\n */\nexport function saveGeneratedCode(code: string, filePath: string): void {\n const files = extractFiles(code);\n\n if (files.length > 0) {\n // Multi-file output (e.g., index.tsx + index.module.less)\n createFiles({ files, filePath });\n } else {\n const extractedCode = extractCode(code);\n const folderPath = path.dirname(filePath);\n const fileName = path.basename(filePath);\n writeFile(folderPath, fileName, extractedCode);\n }\n}\n\n/**\n * Get list of available asset files for AI to match against\n */\nfunction getAssetFilesList(state: GraphState) {\n try {\n const assetsDir = workspaceManager.resolveAppSrc(state.workspace, 'assets');\n\n if (!fs.existsSync(assetsDir)) {\n return '';\n }\n\n const files = fs.readdirSync(assetsDir);\n return files.join(', ');\n } catch {\n return '';\n }\n}\n\n/**\n * Inject root component into App.tsx\n * Reads existing App.tsx, adds import and renders the root component\n */\nexport async function injectRootComponentToApp(state: GraphState, cache: CodeCache): Promise<void> {\n try {\n // Check if already injected\n if (isAppInjected(cache)) {\n logger.printInfoLog('⏭️ Skipping App.tsx injection (already injected)');\n return;\n }\n\n logger.printInfoLog('💉 Injecting root component into App.tsx...');\n\n // Construct App.tsx path\n const appTsxPath = workspaceManager.resolveAppSrc(state.workspace, 'App.tsx');\n\n // Read existing App.tsx or use default template\n let appContent: string;\n try {\n appContent = fs.readFileSync(appTsxPath, 'utf8');\n } catch {\n // Use default template if App.tsx doesn't exist\n logger.printWarnLog('App.tsx not found, using default template');\n appContent = DEFAULT_APP_CONTENT;\n }\n\n // Get root component information\n const rootNode = state.protocol!;\n const componentName = rootNode.data.name || 'RootComponent';\n const componentPath = rootNode.data.path || '';\n\n // Generate prompt\n const prompt = injectRootComponentPrompt({\n appContent,\n componentName,\n componentPath,\n });\n\n // Call AI model\n const updatedCode = await callModel({\n question: prompt,\n });\n\n // Extract code (no markdown blocks expected based on prompt requirements)\n const finalCode = updatedCode.includes('```') ? extractCode(updatedCode) : updatedCode.trim();\n\n // Write updated App.tsx\n const appFolderPath = path.dirname(appTsxPath);\n writeFile(appFolderPath, 'App.tsx', finalCode);\n\n // Mark as injected and save immediately\n saveAppInjected(cache, state.workspace);\n\n logger.printSuccessLog(`Successfully injected ${componentName} into App.tsx`);\n } catch (error) {\n logger.printErrorLog(`Failed to inject root component: ${(error as Error).message}`);\n // Don't throw - allow the process to continue even if injection fails\n }\n}\n","import fs from 'node:fs';\nimport { WorkspaceStructure } from '../types/workspace-types';\nimport { logger } from './logger';\n\n/**\n * Code generation cache structure\n * Tracks which components have been generated and whether App.tsx has been injected\n */\nexport interface CodeCache {\n generatedComponents: string[]; // Array of node IDs that have been generated\n appInjected: boolean;\n}\n\n/**\n * Get the path to the cache file\n */\nfunction getCachePath(workspace: WorkspaceStructure): string {\n return workspace.checkpoint;\n}\n\n/**\n * Load code generation cache from file\n * Returns empty cache if file doesn't exist or on error\n */\nexport function loadCodeCache(workspace: WorkspaceStructure): CodeCache {\n const cachePath = getCachePath(workspace);\n\n try {\n if (!fs.existsSync(cachePath)) {\n logger.printInfoLog('No code cache found, starting fresh');\n return createEmptyCache();\n }\n\n const content = fs.readFileSync(cachePath, 'utf-8');\n const cache = JSON.parse(content) as CodeCache;\n\n // Validate cache structure\n if (!Array.isArray(cache.generatedComponents) || typeof cache.appInjected !== 'boolean') {\n logger.printWarnLog('Invalid cache format, starting fresh');\n return createEmptyCache();\n }\n\n logger.printInfoLog(`Loaded code cache: ${cache.generatedComponents.length} components cached`);\n return cache;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to load code cache: ${errorMessage}. Starting fresh.`);\n return createEmptyCache();\n }\n}\n\n/**\n * Save code generation cache to file\n */\nexport function saveCodeCache(workspace: WorkspaceStructure, cache: CodeCache): void {\n const cachePath = getCachePath(workspace);\n\n try {\n // Ensure process directory exists\n if (!fs.existsSync(workspace.process)) {\n fs.mkdirSync(workspace.process, { recursive: true });\n }\n\n const content = JSON.stringify(cache, null, 2);\n fs.writeFileSync(cachePath, content, 'utf-8');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to save code cache: ${errorMessage}`);\n }\n}\n\n/**\n * Check if a component has already been generated\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to check\n */\nexport function isComponentGenerated(cache: CodeCache, nodeId: string): boolean {\n return cache.generatedComponents.includes(nodeId);\n}\n\n/**\n * Mark a component as generated\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to mark as generated\n */\nexport function markComponentGenerated(cache: CodeCache, nodeId: string): void {\n if (!isComponentGenerated(cache, nodeId)) {\n cache.generatedComponents.push(nodeId);\n }\n}\n\n/**\n * Mark a component as generated and save cache immediately\n * Combines markComponentGenerated and saveCodeCache for convenience\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to mark as generated\n * @param workspace - The workspace structure containing cache file path\n */\nexport function saveComponentGenerated(cache: CodeCache, nodeId: string, workspace: WorkspaceStructure): void {\n markComponentGenerated(cache, nodeId);\n saveCodeCache(workspace, cache);\n}\n\n/**\n * Check if root component has been injected into App.tsx\n */\nexport function isAppInjected(cache: CodeCache): boolean {\n return cache.appInjected;\n}\n\n/**\n * Mark App.tsx as injected\n */\nexport function markAppInjected(cache: CodeCache): void {\n cache.appInjected = true;\n}\n\n/**\n * Mark App.tsx as injected and save cache immediately\n * Combines markAppInjected and saveCodeCache for convenience\n */\nexport function saveAppInjected(cache: CodeCache, workspace: WorkspaceStructure): void {\n markAppInjected(cache);\n saveCodeCache(workspace, cache);\n}\n\n/**\n * Create an empty cache\n */\nfunction createEmptyCache(): CodeCache {\n return {\n generatedComponents: [],\n appInjected: false,\n };\n}\n"],"mappings":"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","names":["GraphNode","ValidationMode","FigmaImageFormat","tools","path","fs","path","fs","fs","path","axios","axios","fs","path","resolve","cssAngle","_init","tools","path","path","path","fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/nodes/process/structure/prompt.ts","../src/types/graph-types.ts","../src/types/figma-types.ts","../src/agents/initial-agent/prompt.ts","../src/agents/initial-agent/instruction.ts","../src/utils/url-parser.ts","../src/tools/figma-tool/index.ts","../src/utils/axios.ts","../src/utils/config.ts","../src/utils/file.ts","../src/utils/logger.ts","../src/utils/workspace.ts","../src/tools/figma-tool/figma.ts","../src/tools/figma-tool/images.ts","../src/utils/promise-pool.ts","../src/tools/figma-tool/constants.ts","../src/tools/style-tool/index.ts","../src/tools/style-tool/color.ts","../src/tools/style-tool/utils.ts","../src/utils/call-model.ts","../src/nodes/process/structure/index.ts","../src/nodes/process/structure/utils.ts","../src/utils/naming.ts","../src/utils/parser.ts","../src/nodes/process/index.ts","../src/skill-api.ts","../src/nodes/code/prompt.ts","../src/nodes/code/constants.ts","../src/nodes/code/utils.ts","../src/utils/code-cache.ts"],"sourcesContent":["/**\n * Prompt template for structure generation.\n * Matches the style used in agents/initial-agent/prompt.ts.\n */\nexport const generateStructurePrompt = (options: { positions: string; width?: string }) => {\n const { positions, width } = options;\n const imageWidth = width ?? '1440';\n return `\n<system_instructions>\n <task>Generate a semantic React component structure from Figma design data.</task>\n\n <input_context>\n <positions_json>${positions}</positions_json>\n <image_width>${imageWidth}px</image_width>\n <visual_reference>Refer to the attached image for hierarchy and grouping.</visual_reference>\n </input_context>\n\n <rules>\n <rule name=\"Hierarchy Strategy\">\n 1. Compare 'positions_json' nesting with visual grouping.\n 2. If data contradicts visual layout, IGNORE data nesting and restructure based on VISUAL proximity.\n </rule>\n\n <rule name=\"Pattern Recognition (CRITICAL)\">\n 1. **Semantic Grouping**: Group by function (e.g., \"PricingCard\", \"UserProfile\").\n 2. **Grid/List Detection**:\n - Detect repeating identical siblings (e.g., 6 Cards).\n - Create a SINGLE container (e.g., \"CardGrid\") instead of \"Card1\", \"Card2\".\n - **MERGE IDs**: The container's \\`elementIds\\` MUST include ALL children IDs of all items in the grid to ensure complete style extraction.\n - **Reusable Instances with \\`data.componentName\\`**:\n - For each visually repeated item, keep a unique \\`id\\` / \\`data.name\\` (e.g., \"TaskCard1\", \"TaskCard2\", ...).\n - Set the SAME \\`data.componentName\\` for all these items (e.g., \"TaskCard\") to mark them as instances of one reusable component.\n - Example: 12 task cards → each child has different \\`id\\` / \\`data.name\\` (\"TaskCard1\"... \"TaskCard12\"), but the SAME \\`data.componentName\\`: \"TaskCard\".\n 3. **Fragmentation**: Avoid creating components for single atoms (Text/Icon) unless reusable (Button/Chip).\n 4. **Monoliths**: Split sections >1000px height.\n </rule>\n\n <rule name=\"Component Granularity (CRITICAL - Prevent Over-Fragmentation)\">\n 1. **Maximum Depth = 2**:\n - Component tree should NOT exceed 2 levels deep (root -> children -> grandchildren is the MAX).\n - If you find yourself creating a 3rd level, STOP and merge it into the parent instead.\n\n 2. **Minimum Element Threshold**:\n - Each component MUST contain at least 5+ elements in its \\`elementIds\\`.\n - If a potential component has fewer than 5 elements, MERGE it into its parent or sibling.\n - This prevents creating tiny components like \"LegalLinks\" (3 links) or \"CommunityLinks\" (2 links).\n\n 3. **Component Count Limit**:\n - For a single section, create at most 2-3 child components.\n - Example: Footer → FooterTop, FooterBottom (2 children, NOT 4-5 like \"Logos\", \"Links\", \"QRCode\", etc.)\n\n 4. **DO NOT Create Separate Components For**:\n - A group of links (merge into parent section's elementIds)\n - A group of logos or icons (merge into parent section's elementIds)\n - Single text, image, icon, divider, link\n - Any group with < 5 elements\n These should ALL be included in parent's \\`elementIds\\`.\n\n 5. **Merge by Visual Region, NOT by Function**:\n - Divide by visual position (Top/Bottom, Left/Right), NOT by element type (Links/Logos/QRCode).\n - Example: FooterTop contains logos + links + icons together (all elements in that visual area).\n </rule>\n\n <rule name=\"Naming & ID\">\n - \\`id\\` MUST match \\`data.name\\` (PascalCase, Unique).\n - **Containers**: \\`elementIds\\` = container background/frame IDs only.\n - **Leaves**: \\`elementIds\\` = component's direct IDs + ALL nested children IDs.\n - **Integrity**: Every input ID must be assigned to exactly one component.\n </rule>\n\n <rule name=\"Layout Data\">\n For EACH node, calculate:\n - \\`boundingBox\\`: {top, left, width, height} (Absolute)\n - \\`relativeBoundingBox\\`: {top, left, width, height} (Relative to parent)\n - \\`spacing\\`: {next: number} (Distance to next sibling)\n - \\`layoutDirection\\`: \"VERTICAL\" | \"HORIZONTAL\" | \"NONE\"\n **HOW TO DETERMINE layoutDirection (ESPECIALLY FOR ROOT NODE)**:\n 1. Look at ALL direct children's \\`boundingBox.left\\` values\n 2. **If children have 2+ DIFFERENT \\`left\\` values** (difference > 50px):\n → Set \\`layoutDirection = \"HORIZONTAL\"\\`\n → Example: Sidebar at left=0, Content at left=240 → HORIZONTAL\n 3. **If ALL children have SAME \\`left\\` value** (within ±50px tolerance):\n → Set \\`layoutDirection = \"VERTICAL\"\\`\n → Example: Header at left=0, Content at left=0, Footer at left=0 → VERTICAL\n 4. Common patterns:\n - **Sidebar Layout**: Sidebar (left=0) + Main Content (left=240+) → ROOT is HORIZONTAL\n - **Stacked Layout**: All sections at same left position → ROOT is VERTICAL\n </rule>\n </rules>\n\n <output_format>\n Return ONLY a single valid JSON object (no markdown code blocks).\n Output MUST be COMPACT (no pretty-printing): avoid indentation/newlines as much as possible.\n Follow this schema exactly:\n {\n \"id\": \"ComponentName\",\n \"data\": {\n \"name\": \"ComponentName\",\n \"componentName\": \"OptionalReusableComponentName\",\n \"purpose\": \"string\",\n \"elementIds\": [\"id1\", \"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": []\n }\n\n layoutDirection MUST be one of: \"VERTICAL\" | \"HORIZONTAL\" | \"NONE\"\n \n **Layout Groups**:\n - Do NOT generate \\`layoutGroups\\` field in your output\n - This field will be automatically added by post-processing if needed\n\n **CRITICAL: 2-Level Maximum Structure**\n\n Your output MUST follow a maximum 2-level structure. Level 2 nodes should have \\`children: []\\`.\n All primitive elements (single text, links, logos, icons, dividers) go into \\`elementIds\\`, NOT as child components.\n\n Example for ANY section:\n {\n \"id\": \"SectionName\",\n \"data\": {\n \"name\": \"SectionName\",\n \"purpose\": \"Main section description\",\n \"elementIds\": [\"container-id\"],\n \"layout\": {...}\n },\n \"children\": [\n {\n \"id\": \"SubSectionA\",\n \"data\": {\n \"name\": \"SubSectionA\",\n \"purpose\": \"Visual area with links, logos, text\",\n \"elementIds\": [\"all\", \"element\", \"ids\", \"including\", \"links\", \"logos\", \"text\"],\n \"layout\": {...}\n },\n \"children\": [] // MUST be empty - all elements via elementIds\n },\n {\n \"id\": \"SubSectionB\",\n \"data\": {\n \"name\": \"SubSectionB\",\n \"purpose\": \"Another visual area\",\n \"elementIds\": [\"all\", \"element\", \"ids\", \"in\", \"this\", \"area\"],\n \"layout\": {...}\n },\n \"children\": [] // MUST be empty\n }\n ]\n }\n\n For reusable component instances that share the same \\`data.componentName\\` but have different \\`id\\` and \\`data.name\\`, an additional expected example is:\n {\n \"id\": \"TaskCardGrid\",\n \"data\": {\n \"name\": \"TaskCardGrid\",\n \"purpose\": \"Grid container for task cards\",\n \"elementIds\": [\"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": [\n {\n \"id\": \"TaskCard1\",\n \"data\": {\n \"name\": \"TaskCard1\",\n \"componentName\": \"TaskCard\",\n \"purpose\": \"Single task card\",\n \"elementIds\": [\"id1\", \"...\"],\n \"layout\": {\n \"boundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"relativeBoundingBox\": { \"top\": 0, \"left\": 0, \"width\": 0, \"height\": 0 },\n \"spacing\": { \"next\": 0 },\n \"layoutDirection\": \"VERTICAL\"\n }\n },\n \"children\": []\n }\n ]\n }\n </output_format>\n</system_instructions>\n`.trim();\n};\n\n/**\n * Prompt template for extracting data list and props schema.\n */\nexport const extractDataListPrompt = (options: { containerName: string; childComponentName: string; figmaData: string }) => {\n const { containerName, childComponentName, figmaData } = options;\n return `\nYou are an expert Frontend Developer.\nYou are analyzing a container component \"${containerName}\" which contains a list of \"${childComponentName}\" components.\n\nYour task is to:\n1. Generate a **props schema** (formal parameter definitions) based on the first component instance\n2. Extract the **data array** (actual parameter values) for all component instances from the provided Figma structure data\n\nContext:\n- Container: ${containerName}\n- Child Component: ${childComponentName}\n- Figma Data:\n${figmaData}\n\nInstructions:\n1. Analyze the \"children\" in the Figma Data. Identify those that are instances of \"${childComponentName}\".\n2. For each instance, extract ALL differing content, including:\n - **Text**: Extract the **EXACT** text content found in the \"characters\" fields. Do NOT summarize or generate placeholders.\n - **Images/Icons**: Identify nodes where \\`type\\` is \"IMAGE\". These nodes will have a \\`url\\` field.\n - If a node has \\`isIcon: true\\` field, or the node name contains \"icon\", or the url ends with \".svg\", use \\`iconSrc\\` as the key.\n - Otherwise, use \\`imageSrc\\` or \\`avatarSrc\\` based on context (e.g., avatar for profile pictures).\n - **Layout/Variants**: \n - If the component has \\`layoutDirection: \"HORIZONTAL\"\\` in the layoutInfo, and contains both text and image content, determine the image position:\n - Check the \\`absoluteBoundingBox\\` positions: if image's x position is less than text's x position, image is on the **left**; otherwise on the **right**.\n - Extract as \\`imagePosition: \"left\"\\` or \\`imagePosition: \"right\"\\` prop.\n - Any other component properties or visual variants (e.g. \"variant\": \"filled\", \"active\": true).\n3. **Normalize the data keys (CRITICAL)**:\n - For text content: use \\`title\\`, \\`description\\`, \\`label\\`, etc.\n - For images/icons: ALWAYS use \\`iconSrc\\`, \\`imageSrc\\`, \\`avatarSrc\\` (with \"Src\" suffix)\n - **DO NOT** use ambiguous keys like \\`icon\\`, \\`image\\`, \\`avatar\\` alone\n - This ensures compatibility with standard React component prop naming conventions.\n - Example:\n \\`\\`\\`json\n {\n \"title\": \"Example Title\",\n \"description\": \"Example description\",\n \"imageSrc\": \"@/assets/actual-filename-from-figma.png\"\n }\n \\`\\`\\`\n4. **Generate Props Schema (CRITICAL)**:\n - Based on the first component instance, infer the prop definitions\n - For each extracted field (e.g., \"title\", \"description\", \"imageSrc\"), determine:\n * **key**: The property name (e.g., \"title\")\n * **type**: The TypeScript type - must be one of: \"string\", \"number\", \"boolean\", \"string[]\", \"number[]\"\n * **description**: A clear description of what this prop represents\n - Return as \"props\" array with objects containing { key, type, description }\n - Example:\n \\`\\`\\`json\n {\n \"key\": \"title\",\n \"type\": \"string\",\n \"description\": \"Card title text\"\n }\n \\`\\`\\`\n5. **CRITICAL Rules**:\n - **USE ACTUAL DATA ONLY**: The example above uses placeholder names. You MUST use the actual \"characters\" and \"url\" values from the Figma Data provided.\n - **NO FIGMA IDs**: Do NOT include Figma node IDs (like \"1:2859\") in the output. Only extract actual content data.\n - **NO PLACEHOLDERS**: Do NOT generate fake text or copy paths from examples. If a node does not have a \"url\" field, do not include an \"imageSrc\" property.\n - **Deep Search**: Text nodes might be nested deeply inside Frames/Groups. Look recursively for \"characters\" fields.\n - **Layout Information**: The Figma Data includes \\`layoutInfo\\` array with \\`layoutDirection\\` and \\`absoluteBoundingBox\\` for each component instance. Use this to determine layout-related props like image position (left/right) in horizontal layouts.\n - **Asset Paths - CRITICAL**: \n - Images are represented by nodes with \\`type: \"IMAGE\"\\`. These nodes MUST have a \\`url\\` field.\n - You MUST use the EXACT value from the \\`url\\` field as the value for \"imageSrc\", \"iconSrc\", or \"avatarSrc\" without any modifications.\n - The \\`url\\` field value MUST be a file path (e.g., \"@/assets/icon-name.svg\").\n - **DO NOT hallucinate or copy paths from the example above.** Every image MUST correspond to a node in the provided Figma Data.\n - CORRECT: If a node has \\`type: \"IMAGE\"\\` and \\`url: \"@/assets/real-image-123.png\"\\`, use exactly that.\n - WRONG: Using \"@/assets/start-2.svg\" if it's not in the input data.\n - If the \\`url\\` field does not exist or does not contain a valid path starting with \"@/assets/\", omit the iconSrc/imageSrc/avatarSrc field entirely.\n6. Return a JSON object with two keys:\n - \"props\": Array of prop definitions with { key, type, description }\n - \"state\": Array of data objects for each component instance\n7. Return ONLY the JSON object. Do not explain.\n\nExample Output JSON Structure (for reference only):\n{\n \"props\": [\n {\n \"key\": \"title\",\n \"type\": \"string\",\n \"description\": \"Card title text\"\n },\n {\n \"key\": \"description\",\n \"type\": \"string\",\n \"description\": \"Card description text\"\n },\n {\n \"key\": \"imageSrc\",\n \"type\": \"string\",\n \"description\": \"Path to card image\"\n }\n ],\n \"state\": [\n {\n \"title\": \"Actual Title 1\",\n \"description\": \"Actual Description 1\",\n \"imageSrc\": \"@/assets/actual-file-1.png\"\n },\n {\n \"title\": \"Actual Title 2\",\n \"description\": \"Actual Description 2\",\n \"imageSrc\": \"@/assets/actual-file-2.png\"\n }\n ]\n}\n`.trim();\n};\n","/* Enum representing the nodes in the LangGraph. */\nexport enum GraphNode {\n INITIAL = 'initial',\n PROCESS = 'process',\n VALIDATION = 'validation',\n DATA = 'data',\n CODE = 'code',\n}\n\nexport enum ValidationMode {\n CodeOnly = 'codeOnly',\n ReportOnly = 'reportOnly',\n Full = 'full',\n}\n\nexport interface GlobalFigmaInfo {\n thumbnail: string;\n}\n\n/**\n * Validation-specific configuration.\n * Controls validation behavior in the workflow.\n */\nexport interface ValidationConfig {\n /**\n * Validation execution mode.\n * - codeOnly: generate component code without validation (only commit)\n * - reportOnly: run a single validation pass and generate a report (no code edits)\n * - full: run iterative actor-critic refinement loop (may edit code + commit markers)\n */\n validationMode?: ValidationMode;\n}\n","/**\n * Figma API Types\n *\n * Type definitions for Figma design data structures.\n * These types represent the data fetched from Figma REST API and used throughout the protocol generation process.\n */\n\n/* URL and File Information */\n\n/**\n * Parsed Figma URL information\n */\nexport interface FigmaUrlInfo {\n /** The ID of the Figma file */\n fileId: string | null;\n /** The name of the Figma file */\n name: string;\n /** The ID of the Figma node */\n nodeId: string | null;\n /** The name of the project */\n projectName: string | null;\n}\n\n/* Color and Visual Properties */\n\n/**\n * RGBA color representation\n */\nexport interface FigmaColor {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/**\n * Gradient stop definition\n */\nexport interface FigmaGradientStop {\n color: FigmaColor;\n position: number;\n}\n\n/**\n * Figma paint/fill object\n * Supports solid colors, gradients, and images\n */\nexport interface FigmaColorObject {\n blendMode?: string;\n visible?: boolean;\n type: string;\n color?: FigmaColor;\n opacity?: number;\n gradientStops?: FigmaGradientStop[];\n gradientHandlePositions?: { x: number; y: number }[];\n imageRef?: string;\n}\n\n/* Position and Layout */\n\n/**\n * Position and size in Figma coordinate space\n */\nexport interface FigmaPositionAndSize {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/* Typography */\n\n/**\n * Text styling properties from Figma\n */\nexport interface FigmaTextStyle {\n fontFamily: string;\n fontPostScriptName: string;\n fontStyle: string;\n fontWeight: number;\n textAutoResize: string;\n fontSize: number;\n textAlignHorizontal: string;\n textAlignVertical: string;\n letterSpacing: number;\n lineHeightPx: number;\n lineHeightPercent: number;\n lineHeightPercentFontSize: number;\n lineHeightUnit: string;\n}\n\n/* CSS Conversion */\n\n/**\n * Computed CSS styles converted from Figma properties\n * Used for direct style application in generated components\n */\nexport interface CSSStyles {\n position?: string;\n top?: string;\n left?: string;\n width?: string;\n height?: string;\n display?: string;\n flexDirection?: string;\n justifyContent?: string;\n alignItems?: string;\n gap?: string;\n padding?: string;\n paddingLeft?: string;\n paddingRight?: string;\n paddingTop?: string;\n paddingBottom?: string;\n backgroundColor?: string;\n backgroundImage?: string;\n background?: string;\n border?: string;\n borderRadius?: string;\n boxShadow?: string;\n opacity?: string | number;\n backdropFilter?: string;\n color?: string;\n fontSize?: string;\n fontFamily?: string;\n fontWeight?: string;\n lineHeight?: string;\n letterSpacing?: string;\n textAlign?: string;\n overflow?: string;\n [key: string]: string | number | undefined;\n}\n\n/* Frame/Node Structure */\n\n/**\n * Figma frame/node data structure\n * Represents a complete Figma design node with all its properties and children\n */\nexport interface FigmaFrameInfo {\n id: string;\n name: string;\n type: string;\n url?: string;\n thumbnailUrl?: string;\n visible?: boolean;\n scrollBehavior?: string;\n children?: FigmaFrameInfo[];\n characters?: string;\n frames?: FigmaFrameInfo[];\n blendMode?: string;\n clipsContent?: boolean;\n background?: FigmaColorObject[];\n fills?: FigmaColorObject[];\n strokes?: FigmaColorObject[];\n strokeWeight?: number;\n cornerRadius?: number;\n rectangleCornerRadii?: number[];\n strokeAlign?: string;\n backgroundColor?: FigmaColor;\n absoluteBoundingBox: FigmaPositionAndSize;\n absoluteRenderBounds?: FigmaPositionAndSize;\n constraints?: {\n vertical: string;\n horizontal: string;\n };\n exportSettings?: [\n {\n suffix: string;\n format: FigmaImageFormat;\n constraint: {\n type: string;\n value: number;\n };\n },\n ];\n style?: FigmaTextStyle;\n inlineStyles?: CSSStyles;\n effects?: {\n type: string; // 'DROP_SHADOW' | 'INNER_SHADOW' | 'LAYER_BLUR' | 'BACKGROUND_BLUR'\n visible?: boolean;\n radius: number;\n color?: FigmaColor;\n offset?: {\n x: number;\n y: number;\n };\n spread?: number;\n }[];\n}\n\n/**\n * Supported Figma image export formats\n */\nexport enum FigmaImageFormat {\n PNG = 'png',\n JPG = 'jpg',\n SVG = 'svg',\n PDF = 'pdf',\n EPS = 'eps',\n WEBP = 'webp',\n}\n","/**\n * Generates the system prompt for the InitialAgent.\n * Defines project requirements, tech stack (React + Tailwind V4), and safety constraints.\n *\n * @param options - Configuration options for the prompt generation.\n * @param options.appPath - The target directory path where the project will be scaffolded.\n */\nexport const INITIAL_AGENT_SYSTEM_PROMPT = `\n<system_instructions>\n <task>Scaffold a clean React V18 + TS + Vite + TailwindCSS V4 + Less project.</task>\n\n <input_context>\n - appPath: /absolute/path/to/app\n - appName: document title name\n </input_context>\n\n <requirements>\n <tech_stack>React V18, TypeScript, Vite, TailwindCSS V4, Less</tech_stack>\n\n <directory_constraint>\n CRITICAL: All file operations MUST be performed within the directory specified by appPath.\n When using 'FileEditor.write' or other file tools, you MUST use the full path format: {appPath}/filename.\n </directory_constraint>\n\n <file_specs>\n - \\`.gitignore\\`: Standard gitignore file. MUST include:\n * node_modules/\n * dist/ and build/ directories\n * .env file\n * Editor files (.vscode/*, .DS_Store, etc.)\n * Log files and cache directories\n * Lock files (package-lock.json, yarn.lock, pnpm-lock.yaml)\n - \\`package.json\\`: Basic scripts and dependencies. MUST include \\`tailwindcss\\` (v4) and \\`@tailwindcss/vite\\`.\n * Scripts MUST use \"pnpm exec\" prefix to ensure project dependencies are prioritized:\n - \"dev\": \"pnpm exec vite\"\n - \"build\": \"pnpm exec tsc && pnpm exec vite build\"\n - \"preview\": \"pnpm exec vite preview\"\n * IMPORTANT: Do NOT add \"coderio\" as a dependency - it's only a build tool, not a runtime dependency.\n - \\`vite.config.ts\\`: Configure React and TailwindCSS V4 plugins. MUST include path alias configuration:\n * Add \\`resolve.alias\\` with \\`@\\` pointing to \\`path.resolve(__dirname, './src')\\`\n * Import \\`path\\` from 'node:path'\n - \\`tsconfig.json\\`: Standard React-Vite TS config. MUST include path alias configuration:\n * Add \\`compilerOptions.baseUrl\\` set to \".\"\n * Add \\`compilerOptions.paths\\` with \\`\"@/*\": [\"src/*\"]\\`\n - \\`index.html\\`: Basic template with #root and entry script. set document title to appName.\n - \\`src/main.tsx\\`: Entry point rendering App.\n - \\`src/vite-env.d.ts\\`: MUST include \\`/// <reference types=\"vite/client\" />\\` to support CSS/Less module imports.\n - \\`src/App.tsx\\`: MUST be an empty component (returns null or empty div), NO React import if unused.\n - \\`src/App.less\\`: MUST be an empty file.\n - \\`src/globals.css\\`: ONLY include \\`@import \"tailwindcss\";\\`.\n </file_specs>\n </requirements>\n\n <workflow>\n 1. Use 'FileEditor.write' to create each file listed in <file_specs>, using the format {appPath}/filename for all paths.\n 2. Ensure the directory structure is correct and all files are contained within the appPath directory.\n </workflow>\n\n <final_instruction>\n Create a fully working but MINIMAL project skeleton using Tailwind CSS V4. Use 'FileEditor.write' for all file creations. Use the full path format {appPath}/filename for every file. Do not provide code blocks in chat.\n </final_instruction>\n</system_instructions>\n`.trim();\n","export function initialAgentInstruction(params: { appPath: string; appName: string }): string {\n return `\nappPath: ${params.appPath}\nappName: ${params.appName}\n\nTASK:\nScaffold a clean React V18 + TS + Vite + TailwindCSS V4 + Less project.\n`.trim();\n}\n","import { FigmaUrlInfo } from '../types/figma-types';\n\n/**\n * Parse Figma URL and extract fileId, name, and nodeId in one pass\n * @param url - Figma URL to parse\n * @returns Object containing fileId, name, and nodeId\n * @example\n * parseFigmaUrl('https://www.figma.com/design/aONcu8L82l1PdcT304Q8Za/Intern?node-id=0-495')\n * // Returns: { fileId: 'aONcu8L82l1PdcT304Q8Za', name: 'intern', nodeId: '0-495' }\n */\nexport const parseFigmaUrl = (url: string): FigmaUrlInfo => {\n let fileId: string | null = null;\n let name = 'untitled';\n let nodeId: string | null = null;\n\n try {\n const urlObj = new URL(decodeURIComponent(url));\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n if (pathParts.length >= 3) {\n fileId = pathParts[pathParts.length - 2] || null;\n const fileName = pathParts[pathParts.length - 1];\n name = fileName ? encodeURI(fileName).toLowerCase() : 'untitled';\n name = name.length > 20 ? name.substring(0, 20) : name;\n }\n\n nodeId = urlObj.searchParams.get('node-id') || null;\n nodeId = nodeId ? nodeId.replace(/-/g, ':') : null;\n } catch {}\n\n if (!fileId || !nodeId) {\n throw new Error('Invalid Figma URL');\n }\n\n return { fileId, name, nodeId, projectName: `${name}_${nodeId.replace(/:/g, '_')}` };\n};\n","import { tools } from 'evoltagent';\nimport { checkBorder } from './figma';\nimport { FigmaFrameInfo } from '../../types/figma-types';\nimport { ImageNode } from './types';\nimport { executeDownloadImages, fetchImages, findImageNodes } from './images';\nimport { cleanFigma, fetchFigmaNode, fetchFigmaImages } from './figma';\nimport { styleTool } from '../style-tool';\n\n@tools({\n fetchAndClean: {\n description: 'Fetch and clean Figma document from URL',\n params: [\n { name: 'fileId', type: 'string', description: 'Figma file ID' },\n { name: 'nodeId', type: 'string', description: 'Figma node ID' },\n { name: 'token', type: 'string', description: 'Figma API token' },\n ],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Original Figma document fetched from the URL via official Figma API and cleaned by removing invisible nodes',\n },\n },\n downloadImages: {\n description: 'Detect and download image nodes from figma document',\n params: [\n { name: 'fileId', type: 'string', description: 'Figma file ID' },\n { name: 'token', type: 'string', description: 'Figma API token' },\n { name: 'imageDir', type: 'string', description: 'Output directory path' },\n {\n name: 'document',\n optional: true,\n type: 'FigmaFrameInfo',\n description: 'Figma document which is fetched and cleaned from the URL',\n },\n ],\n returns: {\n type: '{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }',\n description: 'Download results of images from the Figma document with Map structure',\n },\n },\n simplifyImageNodes: {\n description: 'Simplify image nodes in figma document by replacing redundant properties with url',\n params: [\n { name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' },\n { name: 'imageNodes', type: 'Map<string, ImageNode>', description: 'Image nodes map with id as key' },\n ],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Figma node with image nodes simplified and replaced with url',\n },\n },\n processedStyle: {\n description: 'Process styles in Figma document',\n params: [{ name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' }],\n returns: {\n type: 'FigmaFrameInfo',\n description: 'Figma node with styles processed',\n },\n },\n})\nclass FigmaTool {\n async fetchAndClean(fileId: string, nodeId: string, token: string): Promise<FigmaFrameInfo | undefined> {\n if (!fileId || !nodeId || !token) {\n return undefined;\n }\n\n const document = await fetchFigmaNode(fileId, nodeId, token);\n if (!document || !document?.children?.length) {\n throw new Error('Failed to fetch Figma document');\n }\n\n const images = await fetchFigmaImages(fileId, nodeId, token);\n const thumbnail = images?.[nodeId] || '';\n if (!thumbnail) {\n throw new Error('Failed to fetch Figma document thumbnail');\n }\n document.thumbnailUrl = thumbnail;\n\n const cleanedDocument = cleanFigma(document);\n\n return cleanedDocument;\n }\n\n async downloadImages(\n fileId: string,\n token: string,\n imageDir: string,\n document?: FigmaFrameInfo\n ): Promise<{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }> {\n if (!fileId) {\n return { successCount: 0, failCount: 0, imageNodesMap: new Map() };\n }\n\n /* Detect images from the document */\n const imageNodes = findImageNodes(document?.children || [], document?.absoluteBoundingBox);\n const fetchedImages = await fetchImages(imageNodes, fileId, token);\n if (!fetchedImages.length) {\n return { successCount: 0, failCount: 0, imageNodesMap: new Map() };\n }\n\n return await executeDownloadImages(fetchedImages, imageDir);\n }\n\n simplifyImageNodes(node: FigmaFrameInfo, imageNodes: Map<string, ImageNode>): FigmaFrameInfo {\n const imageTarget = imageNodes.get(node.id);\n\n if (imageTarget) {\n const basicInfo: FigmaFrameInfo = {\n id: node.id,\n name: node.name,\n type: 'IMAGE',\n url: imageTarget.url,\n absoluteBoundingBox: node.absoluteBoundingBox,\n absoluteRenderBounds: node.absoluteRenderBounds,\n };\n\n if (node.cornerRadius) {\n basicInfo.cornerRadius = node.cornerRadius;\n }\n\n if (checkBorder(node)) {\n basicInfo.strokes = node.strokes;\n basicInfo.strokeWeight = node.strokeWeight;\n basicInfo.strokeAlign = node.strokeAlign;\n }\n return basicInfo;\n }\n\n const result: FigmaFrameInfo = { ...node };\n if (node.children && Array.isArray(node.children)) {\n result.children = node.children.map(child => this.simplifyImageNodes(child, imageNodes));\n }\n\n return result;\n }\n\n processedStyle(node: FigmaFrameInfo): FigmaFrameInfo {\n // Convert current node's styles using style-tool\n const processedNode = styleTool.convert(node);\n\n // Recursively process children\n if (processedNode.children && Array.isArray(processedNode.children)) {\n processedNode.children = processedNode.children.map(child => this.processedStyle(child));\n }\n\n return processedNode;\n }\n}\n\nexport const figmaTool = new FigmaTool();\n","import axios, { AxiosRequestConfig } from 'axios';\nimport { getDebugConfig } from './config';\nimport { writeFile } from './file';\nimport { workspaceManager } from './workspace';\n\n/**\n * Save debug log\n */\nfunction saveDebugLog(\n requestInfo: { url: string; config: AxiosRequestConfig },\n responseInfo: { status: number; statusText: string; data: unknown }\n): void {\n const debugConfig = getDebugConfig();\n if (!debugConfig.enabled) {\n return;\n }\n const debugContent = [\n '------------request------------',\n JSON.stringify(requestInfo, null, 2),\n '------------response------------',\n JSON.stringify(responseInfo, null, 2),\n ].join('\\n');\n writeFile(workspaceManager.path?.debug ?? '', `fetch_${new Date().toISOString().replace(/:/g, '-')}.md`, debugContent);\n}\n\n/**\n * Axios get request with debug logging\n */\nexport async function get<T>(url: string, config?: AxiosRequestConfig<T>): Promise<T> {\n const response = await axios.get<T>(url, config);\n\n saveDebugLog(\n {\n url,\n config: config ?? {},\n },\n {\n status: response.status,\n statusText: response.statusText,\n data: response.data,\n }\n );\n\n return response.data;\n}\n","import { readFileSync, existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { homedir } from 'os';\nimport yaml from 'js-yaml';\n\n// Configuration directory in user's home\nexport const CONFIG_DIR = resolve(homedir(), '.coderio');\nconst CONFIG_FILE = resolve(CONFIG_DIR, 'config.yaml');\n\n/**\n * Model configuration interface\n */\nexport interface ModelConfig {\n provider: string;\n model: string;\n baseUrl: string;\n apiKey: string;\n}\n\n/**\n * Figma configuration interface\n */\nexport interface FigmaConfig {\n token: string;\n}\n\n/**\n * Debug configuration interface\n */\nexport interface DebugConfig {\n enabled: boolean;\n outputDir?: string;\n}\n\n/**\n * Configuration file structure\n */\ninterface Config {\n model: ModelConfig;\n figma: FigmaConfig;\n debug?: DebugConfig;\n}\n\n// Cache for loaded configuration\nlet cachedConfig: Config | null = null;\n\n/**\n * Load configuration from user's config directory\n * The configuration is cached after first load\n * @returns Full configuration object\n */\nexport function loadConfig(): Config {\n if (cachedConfig) {\n return cachedConfig;\n }\n\n if (!existsSync(CONFIG_FILE)) {\n throw new Error(`Configuration file not found at: ${CONFIG_FILE}\\n` + `Please create the configuration file.`);\n }\n\n try {\n const fileContent = readFileSync(CONFIG_FILE, 'utf8');\n const rawConfig = yaml.load(fileContent) as Config;\n\n if (!rawConfig) {\n throw new Error('Invalid config.yaml structure: configuration is empty');\n }\n\n cachedConfig = rawConfig;\n return cachedConfig;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to load config.yaml: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Get model configuration from config.yaml\n * @returns Model configuration\n * @throws Error if configuration is invalid\n */\nexport function getModelConfig(): ModelConfig {\n const config = loadConfig();\n if (!config.model) {\n throw new Error('Model configuration not found in config.yaml');\n }\n return config.model;\n}\n\n/**\n * Get Figma configuration from config.yaml\n * @returns Figma configuration\n * @throws Error if configuration is invalid\n */\nexport function getFigmaConfig(): FigmaConfig {\n const config = loadConfig();\n if (!config.figma) {\n throw new Error('Figma configuration not found in config.yaml');\n }\n return config.figma;\n}\n\n/**\n * Get debug configuration from config.yaml\n * @returns Debug configuration (defaults to disabled if not specified)\n */\nexport function getDebugConfig(): DebugConfig {\n const config = loadConfig();\n return config.debug || { enabled: false };\n}\n\n/**\n * Get the path to the configuration file\n */\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n\n/**\n * Check if configuration file exists\n */\nexport function configExists(): boolean {\n return existsSync(CONFIG_FILE);\n}\n/**\n * Clear the configuration cache\n * Useful for testing or when configuration needs to be reloaded\n */\nexport function clearConfigCache(): void {\n cachedConfig = null;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from './logger';\nimport { FileInfo } from '../types/file-types';\n\n/**\n * Write file to the specified path\n * @param filePath - The path to the file\n * @param content - The content to write to the file\n */\nexport const writeFile = (folderPath: string, filePath: string, content: string) => {\n if (!folderPath || !filePath || !content) {\n return;\n }\n if (!fs.existsSync(folderPath)) {\n fs.mkdirSync(folderPath, { recursive: true });\n }\n fs.writeFileSync(path.join(folderPath, filePath), content);\n};\n\n/**\n * Create multiple files from parsed data\n */\nexport function createFiles({ files, filePath }: { files: FileInfo[]; filePath: string }): void {\n try {\n for (const file of files) {\n const dirPath = path.dirname(filePath);\n writeFile(dirPath, file.filename, file.content);\n }\n } catch (error) {\n logger.printErrorLog(`Failed to create files in ${filePath}: ${(error as Error).message}`);\n throw error;\n }\n}\n","import chalk from 'chalk';\n\n/**\n * Get current timestamp in YYYY-MM-DD HH:mm:ss format\n */\nfunction getTimestamp(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\n/**\n * Logger utility for consistent console output with colors\n */\nexport const logger = {\n /**\n * Print standard log (alias for info logs).\n *\n * Many subsystems (e.g. validation) use `printLog()` as the default logging method.\n */\n printLog(message: string): void {\n console.log(message);\n },\n\n /**\n * Print info log in blue\n */\n printInfoLog(message: string): void {\n console.log(chalk.blue(`[${getTimestamp()}] [INFO] ${message}`));\n },\n\n /**\n * Print warning log in yellow\n */\n printWarnLog(message: string): void {\n console.warn(chalk.yellow(`[${getTimestamp()}] [WARNING] ${message}`));\n },\n\n /**\n * Print error log in red\n */\n printErrorLog(message: string): void {\n console.error(chalk.red(`[${getTimestamp()}] [ERROR] ✖ ${message}`));\n },\n\n /**\n * Print test/debug log in gray\n * Only logs in development/test environments\n */\n printTestLog(message: string): void {\n if (process.env.NODE_ENV === 'development') {\n console.log(chalk.gray(`[${getTimestamp()}] [DEBUG] ${message}`));\n }\n },\n\n /**\n * Print success log in green\n */\n printSuccessLog(message: string): void {\n console.log(chalk.green(`[${getTimestamp()}] [SUCCESS] ✔ ${message}`));\n },\n};\n","import path from 'node:path';\nimport fs from 'node:fs';\nimport { WorkspaceStructure } from '../types/workspace-types';\nimport { logger } from './logger';\n\n/**\n * Defines the logical structure of the output workspace.\n * output directory structure\n coderio/\n └── figmaName/ # Project root directory generated from a Figma URL\n ├── my-app/ # Generated project source code\n ├── process/ # Intermediate data and cache during generation\n │ ├── validation/ # Validation reports, screenshots, and processed.json\n │ └── ... # Other process artifacts\n ├── reports.html # Validation reports summary\n └── checkpoint/ # Cache\n ├── coderio-cli.db \n └── checkpoint.json \n*/\nclass Workspace {\n path: WorkspaceStructure | null = null;\n\n initWorkspace(subPath: string, rootPath?: string, appName?: string): WorkspaceStructure {\n if (this.path) {\n return this.path;\n }\n\n const root = rootPath || (process.env.CODERIO_CLI_USER_CWD ?? process.cwd());\n const coderioRoot = path.join(root, 'coderio');\n const finalRoot = path.resolve(coderioRoot, subPath);\n const app = appName || 'my-app';\n\n const absoluteRoot = path.resolve(finalRoot);\n const processDir = path.join(absoluteRoot, 'process');\n const checkpointDir = path.join(absoluteRoot, 'checkpoint');\n const debugDir = path.join(absoluteRoot, 'debug');\n\n this.path = {\n root: absoluteRoot,\n app: path.join(absoluteRoot, app),\n process: processDir,\n debug: debugDir,\n reports: path.join(absoluteRoot, 'reports.html'),\n db: path.join(checkpointDir, 'coderio-cli.db'),\n checkpoint: path.join(checkpointDir, 'checkpoint.json'),\n };\n return this.path;\n }\n\n /**\n * Delete all files and directories inside the workspace\n * @param workspace - The workspace structure\n * @param exclude - Optional list of file/directory names to exclude from deletion\n */\n deleteWorkspace(workspace: WorkspaceStructure, exclude: string[] = []): void {\n try {\n if (fs.existsSync(workspace.root)) {\n // Read all entries in the workspace root\n const entries = fs.readdirSync(workspace.root);\n\n // Delete each entry\n for (const entry of entries) {\n if (exclude.includes(entry)) {\n continue;\n }\n const fullPath = path.join(workspace.root, entry);\n fs.rmSync(fullPath, { recursive: true, force: true });\n }\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to delete workspace: ${errorMessage}`);\n }\n }\n\n /**\n * Resolve the absolute path to the source code directory\n * @param paths - The workspace structure\n * @param srcSubPath - The subpath to the source code directory\n * @returns The absolute path to the source code directory\n */\n resolveAppSrc(paths: WorkspaceStructure, srcSubPath: string): string {\n return path.join(paths.app, 'src', srcSubPath);\n }\n\n /**\n * Resolve component alias path to absolute filesystem path.\n *\n * Handles various path formats:\n * - @/components/Button → /workspace/my-app/src/components/Button/index.tsx\n * - @/src/components/Button → /workspace/my-app/src/components/Button/index.tsx\n * - components/Button → /workspace/my-app/src/components/Button/index.tsx\n *\n */\n resolveComponentPath(aliasPath: string): string {\n // Normalize path separators for robustness across platforms.\n const normalizedAlias = aliasPath.replace(/\\\\/g, '/');\n\n // Step 1: Strip @/ prefix if present\n let relativePath = normalizedAlias.startsWith('@/')\n ? normalizedAlias.substring(2) // '@/components/Button' → 'components/Button'\n : normalizedAlias;\n\n // Step 2: Strip 'src/' prefix if present (resolveAppSrc adds it)\n // '@/src/components/Button' → 'components/Button'\n if (relativePath.startsWith('src/')) {\n relativePath = relativePath.substring(4);\n }\n\n // Step 3: Ensure path ends with /index.tsx (all components follow this convention)\n if (!relativePath.endsWith('.tsx') && !relativePath.endsWith('.ts')) {\n relativePath = `${relativePath}/index.tsx`;\n }\n\n return relativePath;\n }\n}\nexport const workspaceManager = new Workspace();\n","import { FigmaFrameInfo, FigmaImageFormat, FigmaColorObject } from '../../types/figma-types';\nimport { get } from '../../utils/axios';\nimport { logger } from '../../utils/logger';\n\n/**\n * Fetch Figma nodes by fileId and nodeId\n * @param fileId - Figma file ID\n * @param nodeId - Figma node ID\n * @param token - Figma API token\n * @returns Figma frame information\n */\nexport const fetchFigmaNode = async (fileId: string, nodeId: string, token: string): Promise<FigmaFrameInfo | undefined> => {\n const url = `https://api.figma.com/v1/files/${fileId}/nodes?ids=${nodeId}`;\n try {\n const data = await get<{ nodes: Record<string, { document: FigmaFrameInfo }> }>(url, {\n headers: {\n 'X-Figma-Token': token,\n },\n });\n // format node id to match the format in the response\n const resData = data.nodes?.[nodeId];\n return resData?.document;\n } catch (error) {\n logger.printErrorLog(`Failed to fetch Figma node: ${error instanceof Error ? error.message : 'Unknown error'}`);\n return undefined;\n }\n};\n\n/**\n * Fetch Figma image by fileId and nodeId\n * @param fileId - Figma file ID\n * @param nodeIds - Figma node ID, multiple node ids can be passed separated by commas\n * @param token - Figma API token\n * @param format - Figma image format\n * @returns Figma image\n */\nexport const fetchFigmaImages = async (\n fileId: string,\n nodeIds: string,\n token: string,\n format?: FigmaImageFormat\n): Promise<Record<string, string>> => {\n const url = `https://api.figma.com/v1/images/${fileId}`;\n try {\n const data = await get<{ images: Record<string, string> }>(url, {\n headers: {\n 'X-Figma-Token': token,\n },\n params: {\n ids: nodeIds,\n format: format || 'png',\n },\n });\n const images = data.images || {};\n // format node id to match the format from response to request\n return Object.fromEntries(Object.entries(images));\n } catch (error) {\n logger.printErrorLog(`Failed to fetch Figma images: ${error instanceof Error ? error.message : 'Unknown error'}`);\n return {};\n }\n};\n\n/**\n * Clean Figma node and children. Remove invisible nodes and children.\n * @param node - Figma node or children\n * @returns Cleaned Figma node or children. If the node is invisible, return null.\n */\nexport const cleanFigma = (node: FigmaFrameInfo): FigmaFrameInfo | undefined => {\n // if node is invisible, return undefined\n if (node.visible === false) {\n return undefined;\n }\n\n // if node has children, recursively clean each child\n if (node.children && Array.isArray(node.children)) {\n node.children = node.children\n .map(child => cleanFigma(child)) // recursively clean each child\n .filter(child => child !== undefined); // filter out invisible nodes\n }\n\n return node;\n};\n\n/**\n * Check if node has border\n * @param node - Figma node\n * @returns True if node has border, false otherwise\n */\nexport const checkBorder = (node: FigmaFrameInfo): boolean => {\n const strokes = node.strokes;\n const strokeWeight = node.strokeWeight;\n\n if (!strokes || !strokes.length || !strokeWeight) return false;\n\n const visibleStrokes = strokes.filter((s: FigmaColorObject) => s.visible !== false);\n if (visibleStrokes.length === 0) return false;\n\n return true;\n};\n","import { FigmaColorObject, FigmaFrameInfo, FigmaImageFormat, FigmaPositionAndSize } from '../../types/figma-types';\nimport { ImageNode } from './types';\nimport fs from 'fs';\nimport path from 'path';\nimport axios from 'axios';\nimport { promisePool } from '../../utils/promise-pool';\nimport { fetchFigmaImages } from './figma';\nimport { DEFAULT_DOWNLOAD_CONCURRENCY, DOWNLOAD_TIMEOUT_MS, MAX_DOWNLOAD_RETRIES, BASE_RETRY_DELAY_MS } from './constants';\nimport { logger } from '../../utils/logger';\n\n/**\n * Fetch images from figma document\n * @param nodes - Image nodes\n * @param fileId - Figma file ID\n * @param token - Figma API token\n * @returns Image nodes\n */\nexport const fetchImages = async (nodes: ImageNode[], fileId: string, token: string): Promise<ImageNode[]> => {\n if (!fileId || !nodes?.length) {\n return [];\n }\n\n const svgs = nodes.filter(node => node.format === FigmaImageFormat.SVG);\n const pngs = nodes.filter(node => node.format === FigmaImageFormat.PNG);\n const getImagePromises: Promise<{ [key: string]: string } | undefined>[] = [];\n\n if (svgs.length > 0) {\n getImagePromises.push(fetchFigmaImages(fileId, svgs.map(node => node.id).join(','), token, FigmaImageFormat.SVG));\n }\n if (pngs.length > 0) {\n getImagePromises.push(fetchFigmaImages(fileId, pngs.map(node => node.id).join(','), token, FigmaImageFormat.PNG));\n }\n\n const images: ImageNode[] = [];\n const results = await Promise.all(getImagePromises);\n results.forEach((res: { [key: string]: string } | undefined) => {\n if (res) {\n for (const [key, value] of Object.entries(res)) {\n images.push({\n id: key,\n name: '',\n format: FigmaImageFormat.PNG,\n ...(nodes.find(n => n.id === key) || {}),\n url: value || '',\n });\n }\n }\n });\n\n return images;\n};\n\n/**\n * Download images from figma document\n * @param images - Image nodes\n * @param imageDir - Output directory path\n * @param concurrency - Concurrency level\n * @returns Download results\n */\nexport const executeDownloadImages = async (\n images: ImageNode[],\n imageDir?: string,\n concurrency: number = DEFAULT_DOWNLOAD_CONCURRENCY\n): Promise<{ successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> }> => {\n if (!images || !images.length || !imageDir) {\n return {\n successCount: 0,\n failCount: 0,\n imageNodesMap: new Map(),\n };\n }\n\n // Process all images with dynamic concurrency control using promisePool\n const results = await promisePool(images, image => createDownloadTask(image, imageDir), concurrency);\n\n // Aggregate log after completion\n const successCount = results.filter(r => r.success).length;\n const failCount = results.length - successCount;\n\n // Convert results array to Map with id as key\n const imageNodesMap = new Map<string, ImageNode>(results.map(img => [img.id, img]));\n\n return {\n successCount,\n failCount,\n imageNodesMap,\n };\n};\n\n/**\n * Find image nodes in figma document\n * @param nodes - Figma nodes\n * @param absoluteBoundingBox - Absolute bounding box of the document\n * @returns Image nodes\n */\nexport const findImageNodes = (nodes: FigmaFrameInfo[], absoluteBoundingBox?: FigmaPositionAndSize): ImageNode[] => {\n const imageNodes: ImageNode[] = [];\n if (!nodes || !Array.isArray(nodes)) {\n return imageNodes;\n }\n\n for (const node of nodes) {\n if (node.visible === false) {\n continue;\n }\n // Rule 1: If node type is VECTOR, directly add to imageNodeIds\n else if (node.type === 'VECTOR') {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n // Rule 2: If node type is IMAGE or has imageRef, directly add to imageNodeIds\n else if (isImageNode(node) || isImageNodeViaName(node)) {\n if (isImageNode(node) || hasAnyImageNodeInDescendants(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n } else if (isMaskNode(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n }\n // Rule 3: For nodes with children, check if any leaf descendant is a TEXT node with characters\n else if (node.children && node.children.length > 0) {\n const hasAnyTextNode = hasAnyTextNodeWithCharacters(node);\n\n if (hasAnyTextNode) {\n const firstLevelChildrenHasImageNode = node.children.some((child: FigmaFrameInfo) => isImageNode(child));\n const firstLevelChildrenHasTextNode = node.children.some((child: FigmaFrameInfo) => isTextNode(child));\n if (firstLevelChildrenHasImageNode && !firstLevelChildrenHasTextNode) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n const childImageIds = findImageNodes(node.children, absoluteBoundingBox);\n imageNodes.push(...childImageIds);\n }\n } else if (hasAnyImageNodeInDescendants(node)) {\n imageNodes.push(assignImageObject(node, FigmaImageFormat.PNG));\n } else {\n imageNodes.push(assignImageObject(node, exportSvgIfNeeded(node, absoluteBoundingBox)));\n }\n }\n }\n\n return imageNodes;\n};\n\n/**\n * Determine whether a node should be exported as SVG or PNG\n * @param node - Figma node\n * @param absoluteBoundingBox - Absolute bounding box of the document\n * @returns Figma image format\n */\nexport const exportSvgIfNeeded = (node: FigmaFrameInfo, absoluteBoundingBox?: FigmaPositionAndSize) => {\n // Rule 1: Check if node is very large (likely a background) -> PNG\n if (node.absoluteBoundingBox && absoluteBoundingBox) {\n const { width, height } = node.absoluteBoundingBox;\n const { width: pageWidth, height: pageHeight } = absoluteBoundingBox;\n if (width >= pageWidth && height >= pageHeight) {\n return FigmaImageFormat.PNG;\n }\n }\n\n // Rule 2: Check exportSettings for explicit format specification\n if (node.exportSettings && node.exportSettings.length > 0) {\n const format = node.exportSettings[0].format;\n if (format === FigmaImageFormat.PNG) {\n return FigmaImageFormat.PNG;\n }\n if (format === FigmaImageFormat.SVG) {\n return FigmaImageFormat.SVG;\n }\n }\n\n // Rule 3: Check node name for background keywords -> PNG\n if (node.name.includes('背景') || node.name.toLowerCase().includes('background')) {\n return FigmaImageFormat.PNG;\n }\n\n // Default: Export as SVG\n return FigmaImageFormat.SVG;\n};\n\n/** Assign image object from figma node and format **/\nexport const assignImageObject = (node: { id: string; name: string }, format: FigmaImageFormat) => {\n return {\n id: node.id,\n name: node.name,\n format,\n };\n};\n\n/** Check if node has image ref in fills **/\nexport const hasImageRefInFills = (node: FigmaFrameInfo): boolean => {\n if (!node || !node.fills || node.fills.length === 0) {\n return false;\n }\n return node.fills.some((fill: FigmaColorObject) => {\n const fillWithImageRef = fill;\n return (fillWithImageRef.imageRef && fillWithImageRef.imageRef !== '') || fillWithImageRef.type === 'IMAGE';\n });\n};\n\n/** Check if node is image node **/\nexport const isImageNode = (node: FigmaFrameInfo): boolean => {\n if (!node) {\n return false;\n }\n\n if (node.type === 'IMAGE') {\n return true;\n }\n\n if (node.fills && node.fills.length > 0) {\n return hasImageRefInFills(node);\n }\n\n return false;\n};\n\n/** Check if node is image node via name **/\nexport const isImageNodeViaName = (node: FigmaFrameInfo): boolean => {\n return (node && node.name.toLowerCase().includes('img')) || node.name.toLowerCase().includes('image');\n};\n\n/** Check if node is mask node **/\nexport const isMaskNode = (node: FigmaFrameInfo): boolean => {\n return node && node.name.toLowerCase().includes('mask');\n};\n\n/** Check if node is text node **/\nexport const isTextNode = (node: FigmaFrameInfo): boolean => {\n return node && node.type === 'TEXT' && node.characters !== undefined && node.characters.trim() !== '';\n};\n\n/** Check if node has any image node in descendants **/\nexport const hasAnyImageNodeInDescendants = (node: FigmaFrameInfo): boolean => {\n if (!node) return false;\n\n if (!node.children || node.children.length === 0) {\n return isImageNode(node);\n }\n return node.children.some((child: FigmaFrameInfo) => hasAnyImageNodeInDescendants(child));\n};\n\n/** Check if node has any text node in descendants **/\nexport const hasAnyTextNodeWithCharacters = (node: FigmaFrameInfo): boolean => {\n if (!node) return false;\n\n if (!node.children || node.children.length === 0) {\n return isTextNode(node);\n }\n return node.children.some((child: FigmaFrameInfo) => hasAnyTextNodeWithCharacters(child));\n};\n\n/**\n * Download an image from URL and save to local directory\n * @param url - Image URL to download\n * @param filename - Local filename (with extension)\n * @param outputDir - Output directory path\n * @returns Local file path\n */\nexport async function downloadImage(url: string, filename?: string, imageDir?: string, base64?: boolean): Promise<string> {\n if (!url || (!base64 && (!filename || !imageDir))) {\n return '';\n }\n\n const maxRetries = MAX_DOWNLOAD_RETRIES;\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await axios.get(url, {\n responseType: 'arraybuffer',\n timeout: DOWNLOAD_TIMEOUT_MS,\n });\n\n if (base64) {\n return Buffer.from(response.data).toString('base64');\n } else {\n if (!imageDir || !filename) {\n return '';\n }\n // Create directory if it doesn't exist\n if (!fs.existsSync(imageDir)) {\n fs.mkdirSync(imageDir, { recursive: true });\n }\n const filepath = path.join(imageDir, filename);\n fs.writeFileSync(filepath, Buffer.from(response.data));\n return filepath;\n }\n } catch (error) {\n lastError = error;\n // Don't wait on the last attempt\n if (attempt < maxRetries) {\n // Wait 1s, 2s, 3s, 4s, 5s\n const delay = BASE_RETRY_DELAY_MS * (attempt + 1);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);\n throw new Error(`Failed to download image from ${url} after ${maxRetries} retries: ${errorMessage}`);\n}\n\n/**\n * Create download task for image\n * @param image - Image object\n * @param outputDir - Output directory path\n * @returns Download task object\n */\nexport const createDownloadTask = async (image: ImageNode, imageDir?: string): Promise<ImageNode> => {\n if (!image.url) {\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: image.url,\n remote: image.url,\n success: false,\n };\n }\n\n const ext = image.format || FigmaImageFormat.PNG;\n // Sanitize filename: remove special characters, replace spaces with dashes\n const sanitizedName = (image.name || image.id).replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();\n const filename = `${sanitizedName}-${image.id.replace(/:/g, '-')}.${ext}`;\n\n try {\n const localPath = await downloadImage(image.url, filename, imageDir);\n const normalizedImageDir = imageDir ? path.normalize(imageDir) : '';\n const dirParts = normalizedImageDir.split(path.sep).filter(Boolean);\n const srcIndex = dirParts.lastIndexOf('src');\n const srcDir =\n srcIndex >= 0 ? (normalizedImageDir.startsWith(path.sep) ? path.sep : '') + path.join(...dirParts.slice(0, srcIndex + 1)) : '';\n\n const relativeFromSrc = srcDir ? path.relative(srcDir, localPath) : '';\n const normalizedRelative = relativeFromSrc.split(path.sep).join('/');\n const aliasPath = `@/${normalizedRelative}`;\n\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: aliasPath,\n remote: image.url,\n success: true,\n };\n } catch {\n logger.printErrorLog(`Failed to download image: ${image.url}`);\n return {\n id: image.id,\n name: image.name,\n format: image.format,\n url: image.url,\n remote: image.url,\n success: false,\n };\n }\n};\n","/**\n * Run multiple asynchronous tasks with a concurrency limit.\n *\n * @param items - Array of items to process\n * @param taskGenerator - Function that creates a promise for a single item\n * @param concurrency - Maximum number of concurrent tasks\n * @returns Array of results\n */\nexport async function promisePool<T, R>(items: T[], taskGenerator: (item: T) => Promise<R>, concurrency: number = 5): Promise<R[]> {\n if (!items || !items.length) {\n return [];\n }\n const results: R[] = [];\n let nextIndex = 0;\n const executing = new Set<Promise<void>>();\n\n const processTask = async (index: number) => {\n const item = items[index];\n if (!item) {\n results[index] = undefined as unknown as R;\n return;\n }\n const result = await taskGenerator(item);\n results[index] = result;\n };\n\n while (nextIndex < items.length || executing.size > 0) {\n while (nextIndex < items.length && executing.size < concurrency) {\n const index = nextIndex++;\n const promise = processTask(index).finally(() => {\n executing.delete(promise);\n });\n executing.add(promise);\n }\n\n if (executing.size > 0) {\n await Promise.race(executing);\n }\n }\n\n return results;\n}\n","export const MAX_DOWNLOAD_RETRIES = 5; // Maximum number of download retries\nexport const DOWNLOAD_TIMEOUT_MS = 60000; // Download timeout in milliseconds\nexport const BASE_RETRY_DELAY_MS = 1000; // Base retry delay in milliseconds\nexport const DEFAULT_DOWNLOAD_CONCURRENCY = 5; // Default download concurrency\n","import { tools } from 'evoltagent';\nimport { CSSStyles, FigmaFrameInfo } from '../../types/figma-types';\nimport { convertBorderRadius, convertEffects, convertFills, convertStrokes, convertClipContent } from './utils';\n\n@tools({\n convert: {\n description: 'Convert Figma properties to CSS styles, remove redundant properties, and return the processed Figma node',\n params: [{ name: 'node', type: 'FigmaFrameInfo', description: 'Figma node' }],\n returns: {\n type: 'CSSStyles',\n description: 'CSS styles converted from Figma properties',\n },\n },\n})\nclass StyleTool {\n convert(node: FigmaFrameInfo): FigmaFrameInfo {\n const inlineStyles: CSSStyles = {};\n\n if (node.type !== 'TEXT') {\n convertBorderRadius(node, inlineStyles);\n convertEffects(node, inlineStyles);\n convertFills(node, inlineStyles);\n convertStrokes(node, inlineStyles);\n convertClipContent(node, inlineStyles);\n }\n\n // Create processed node with styles\n const processedNode: FigmaFrameInfo = {\n ...node,\n inlineStyles,\n };\n\n // Remove converted fields to reduce data size\n if (node.type !== 'TEXT') {\n const nodeWithOptionalStyles = processedNode as Partial<FigmaFrameInfo> & Record<string, unknown>;\n delete nodeWithOptionalStyles.fills;\n delete nodeWithOptionalStyles.background;\n delete nodeWithOptionalStyles.strokes;\n delete nodeWithOptionalStyles.strokeAlign;\n delete nodeWithOptionalStyles.backgroundColor;\n delete nodeWithOptionalStyles.cornerRadius;\n delete nodeWithOptionalStyles.rectangleCornerRadii;\n delete nodeWithOptionalStyles.effects;\n // delete nodeWithOptionalStyles.absoluteRenderBounds;\n delete nodeWithOptionalStyles.style;\n }\n return processedNode;\n }\n}\n\nexport const styleTool = new StyleTool();\n","/**\n * Color Converter\n * Convert Figma color objects (SOLID, GRADIENT_LINEAR, GRADIENT_RADIAL) to CSS color strings\n */\nimport { FigmaColorObject, FigmaColor, FigmaGradientStop } from '../../types/figma-types';\n\nexport class ColorConverter {\n // Main conversion entry point\n static convert(colorObject: FigmaColorObject): string {\n if (!colorObject) return 'transparent';\n\n const type = colorObject.type;\n\n switch (type) {\n case 'SOLID':\n return this.convertSolid(colorObject);\n case 'GRADIENT_LINEAR':\n return this.convertLinearGradient(colorObject);\n case 'GRADIENT_RADIAL':\n return this.convertRadialGradient(colorObject);\n default:\n return 'transparent';\n }\n }\n\n // Convert SOLID fill to rgba/rgb string\n private static convertSolid(fill: FigmaColorObject): string {\n if (!fill.color) return 'transparent';\n\n const opacity = this.getOpacity(fill.opacity);\n return this.rgbaToString(fill.color, opacity);\n }\n\n // Convert linear gradient to CSS linear-gradient\n private static convertLinearGradient(fill: FigmaColorObject): string {\n const stops = fill.gradientStops || [];\n const handles = fill.gradientHandlePositions || [];\n const fillOpacity = this.getOpacity(fill.opacity);\n\n if (stops.length === 0) return 'transparent';\n\n // Calculate gradient angle\n const angle = handles.length >= 2 ? this.calculateLinearGradientAngle(handles) : 180;\n\n // Calculate gradient stops with positions\n const stopsWithPositions = this.calculateLinearGradientStops(stops, handles, fillOpacity);\n\n return `linear-gradient(${angle}deg, ${stopsWithPositions})`;\n }\n\n // Convert radial gradient to CSS radial-gradient\n private static convertRadialGradient(fill: FigmaColorObject): string {\n const stops = fill.gradientStops || [];\n const handles = fill.gradientHandlePositions || [];\n const fillOpacity = this.getOpacity(fill.opacity);\n\n if (stops.length === 0) return 'transparent';\n\n // Calculate radial gradient parameters\n const { size, position } = this.calculateRadialGradientParams(handles);\n\n // Convert stops\n const stopsStr = stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const pos = Math.round(stop.position * 100);\n return `${color} ${pos}%`;\n })\n .join(', ');\n\n return `radial-gradient(${size} at ${position}, ${stopsStr})`;\n }\n\n private static getOpacity(opacity?: number): number {\n return opacity !== undefined ? opacity : 1;\n }\n\n // Helper: Convert Figma color to CSS rgba/rgb/hex string\n static rgbaToString(color: FigmaColor, opacity: number = 1): string {\n if (!color) return 'transparent';\n\n const r = Math.round((color.r || 0) * 255);\n const g = Math.round((color.g || 0) * 255);\n const b = Math.round((color.b || 0) * 255);\n const a = (color.a !== undefined ? color.a : 1) * (opacity !== undefined ? opacity : 1);\n\n // Use hex format when fully opaque, rgba when transparent\n if (Math.abs(a - 1) < 0.001) {\n // Fully opaque - use hex format\n if (r === 255 && g === 255 && b === 255) return '#FFF';\n if (r === 0 && g === 0 && b === 0) return '#000';\n // Convert to hex\n const toHex = (n: number) => n.toString(16).toUpperCase().padStart(2, '0');\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n }\n\n // Transparent - use rgba with proper formatting\n const alphaStr = a !== undefined ? a.toFixed(2) : '1';\n return `rgba(${r}, ${g}, ${b}, ${alphaStr})`;\n }\n\n /**\n * Calculate CSS gradient angle from Figma gradient handle positions\n * Figma uses a transform matrix system with 3 points:\n * - p0: gradient origin\n * - p1: gradient direction endpoint (color changes from p0 to p1)\n * - p2: perpendicular direction endpoint\n *\n * Formula: CSS angle = atan2(v1) + angleBetween(v1, v2)\n * where v1 = p1-p0, v2 = p2-p0\n */\n private static calculateLinearGradientAngle(positions: { x: number; y: number }[]): number {\n if (positions.length < 2) return 180;\n\n const [p0, p1, p2] = positions;\n\n if (!p0 || !p1) return 180;\n\n // Vector v1: gradient direction (p0 → p1)\n const v1x = p1.x - p0.x;\n const v1y = p1.y - p0.y;\n const len1 = Math.sqrt(v1x * v1x + v1y * v1y);\n\n // Calculate angle of v1\n const angle1Rad = Math.atan2(v1y, v1x);\n const angle1Deg = angle1Rad * (180 / Math.PI);\n\n // If we don't have p2, use simple formula\n if (!p2 || positions.length < 3) {\n let cssAngle = angle1Deg + 90;\n while (cssAngle < 0) cssAngle += 360;\n while (cssAngle >= 360) cssAngle -= 360;\n return Math.round(cssAngle);\n }\n\n // Vector v2: perpendicular reference (p0 → p2)\n const v2x = p2.x - p0.x;\n const v2y = p2.y - p0.y;\n const len2 = Math.sqrt(v2x * v2x + v2y * v2y);\n\n // Calculate angle between v1 and v2\n const dot = v1x * v2x + v1y * v2y;\n const cosAngle = Math.max(-1, Math.min(1, dot / (len1 * len2)));\n const angleBetweenRad = Math.acos(cosAngle);\n const angleBetweenDeg = angleBetweenRad * (180 / Math.PI);\n\n // CSS angle = angle1 + angleBetween\n let cssAngle = angle1Deg + angleBetweenDeg;\n\n // Normalize to 0-360 range\n while (cssAngle < 0) {\n cssAngle += 360;\n }\n while (cssAngle >= 360) {\n cssAngle -= 360;\n }\n\n return Math.round(cssAngle);\n }\n\n /**\n * Calculate gradient stops with correct positions\n * Based on Figma's gradient handle positions and transform matrix\n */\n private static calculateLinearGradientStops(\n stops: FigmaGradientStop[],\n handles: { x: number; y: number }[],\n fillOpacity: number\n ): string {\n if (handles.length < 2) {\n // Fallback: simple position mapping\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n // Format: remove .00 for whole numbers\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n const [p0, p1] = handles;\n\n if (!p0 || !p1) {\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Transform matrix vectors\n const m1x = p1.x - p0.x;\n const m1y = p1.y - p0.y;\n\n // Gradient length\n const gradientLength = Math.sqrt(m1x * m1x + m1y * m1y);\n\n if (gradientLength === 0) {\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n const position = Math.round(stop.position * 100);\n const posStr = position === 0 ? '0%' : `${position}%`;\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Get CSS angle\n const cssAngle = this.calculateLinearGradientAngle(handles);\n const cssAngleRad = (cssAngle * Math.PI) / 180;\n\n // CSS gradient direction vector\n const gradDirX = Math.sin(cssAngleRad);\n const gradDirY = -Math.cos(cssAngleRad);\n\n // Project box corners onto gradient direction\n const corners = [\n { x: 0, y: 0 },\n { x: 1, y: 0 },\n { x: 0, y: 1 },\n { x: 1, y: 1 },\n ];\n\n const projections = corners.map(c => c.x * gradDirX + c.y * gradDirY);\n const minProj = Math.min(...projections);\n const maxProj = Math.max(...projections);\n const projRange = maxProj - minProj;\n\n // Calculate stop positions\n return stops\n .map(stop => {\n const color = this.rgbaToString(stop.color, fillOpacity);\n\n // Point on gradient line at this stop\n const pointX = p0.x + stop.position * m1x;\n const pointY = p0.y + stop.position * m1y;\n\n // Project onto CSS gradient direction\n const projection = pointX * gradDirX + pointY * gradDirY;\n\n // Convert to percentage\n let cssPosition = ((projection - minProj) / projRange) * 100;\n\n // Round to 2 decimal places then format\n cssPosition = cssPosition !== undefined ? Math.round(cssPosition * 100) / 100 : 0;\n\n // Format position string\n let posStr: string;\n if (cssPosition === 0) {\n posStr = '0%';\n } else if (Number.isInteger(cssPosition)) {\n posStr = `${cssPosition}%`;\n } else {\n posStr = `${cssPosition.toFixed(2)}%`;\n }\n\n return `${color} ${posStr}`;\n })\n .join(', ');\n }\n\n // Calculate radial gradient parameters from handle positions\n private static calculateRadialGradientParams(handles: { x: number; y: number }[]): {\n size: string;\n position: string;\n } {\n if (handles.length < 2) {\n return { size: 'circle', position: '50% 50%' };\n }\n\n const [center, edge, perpendicular] = handles;\n\n if (!center || !edge || !perpendicular) {\n return { size: 'circle', position: '50% 50%' };\n }\n\n // Calculate radius as distance from center to edge\n const dx = edge.x - center.x;\n const dy = edge.y - center.y;\n const radiusX = Math.sqrt(dx * dx + dy * dy);\n\n // Calculate perpendicular radius if third handle exists\n let radiusY = radiusX;\n if (perpendicular) {\n const pdx = perpendicular.x - center.x;\n const pdy = perpendicular.y - center.y;\n radiusY = Math.sqrt(pdx * pdx + pdy * pdy);\n }\n\n // Convert center position to percentage\n const centerX = center.x !== undefined ? (center.x * 100).toFixed(2) : '0';\n const centerY = center.y !== undefined ? (center.y * 100).toFixed(2) : '0';\n\n // Calculate size\n const sizeX = radiusX !== undefined ? (radiusX * 100).toFixed(2) : '0';\n const sizeY = radiusY !== undefined ? (radiusY * 100).toFixed(2) : '0';\n\n return {\n size: `${sizeY}% ${sizeX}%`,\n position: `${centerX}% ${centerY}%`,\n };\n }\n\n // Convert SOLID fill to linear-gradient format (for layering multiple fills)\n static solidToGradient(fill: FigmaColorObject): string {\n const color = this.convertSolid(fill);\n return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`;\n }\n}\n","import { CSSStyles, FigmaColorObject, FigmaFrameInfo } from '../../types/figma-types';\nimport { ColorConverter } from './color';\n\n// Convert border radius\nexport const convertBorderRadius = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n if (node.cornerRadius !== undefined) {\n inlineStyles.borderRadius = `${node.cornerRadius}px`;\n }\n\n // Individual corner radius\n if (node.rectangleCornerRadii) {\n const [tl, tr, br, bl] = node.rectangleCornerRadii;\n inlineStyles.borderRadius = `${tl}px ${tr}px ${br}px ${bl}px`;\n }\n};\n\n// Convert effects (shadow, filter,backdrop-filter)\nexport const convertEffects = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const effects = node.effects;\n\n if (!effects || effects.length === 0) return;\n\n const shadows: string[] = [];\n const filters: string[] = [];\n const backdropFilters: string[] = [];\n\n for (const effect of effects) {\n if (effect.visible === false) continue;\n\n if (effect.type === 'DROP_SHADOW') {\n const x = effect.offset?.x || 0;\n const y = effect.offset?.y || 0;\n const blur = effect.radius || 0;\n const spread = effect.spread || 0;\n // Use ColorConverter for shadow color\n const color = effect.color ? ColorConverter.convert({ type: 'SOLID', color: effect.color }) : 'rgba(0, 0, 0, 0.25)';\n\n shadows.push(`${x.toFixed(0)}px ${y.toFixed(0)}px ${(blur / 2).toFixed(0)}px ${spread.toFixed(0)}px ${color}`);\n } else if (effect.type === 'INNER_SHADOW') {\n const x = effect.offset?.x || 0;\n const y = effect.offset?.y || 0;\n const blur = effect.radius || 0;\n const spread = effect.spread || 0;\n const color = effect.color ? ColorConverter.convert({ type: 'SOLID', color: effect.color }) : 'rgba(0, 0, 0, 0.25)';\n\n shadows.push(`inset ${x.toFixed(0)}px ${y.toFixed(0)}px ${(blur / 2).toFixed(0)}px ${spread.toFixed(0)}px ${color}`);\n } else if (effect.type === 'LAYER_BLUR') {\n const blur = effect.radius || 0;\n filters.push(`blur(${(blur / 2).toFixed(0)}px)`);\n } else if (effect.type === 'BACKGROUND_BLUR') {\n const blur = effect.radius || 0;\n backdropFilters.push(`blur(${(blur / 2).toFixed(0)}px)`);\n }\n }\n\n if (shadows.length > 0) {\n inlineStyles.boxShadow = shadows.join(', ');\n }\n\n if (filters.length > 0) {\n inlineStyles.filter = filters.join(' ');\n }\n\n if (backdropFilters.length > 0) {\n inlineStyles.backdropFilter = backdropFilters.join(', ');\n }\n};\n\n// Convert fills to background\n// Handles multiple fills and creates layered backgrounds\nexport const convertFills = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const fills = node.fills || node.background;\n\n if (!fills || fills.length === 0) return;\n\n // Filter visible fills\n const visibleFills = fills.filter((fill: FigmaColorObject) => fill.visible !== false);\n if (visibleFills.length === 0) return;\n\n // Convert all fills to CSS\n // For multiple fills, convert SOLID to gradient format for proper layering\n const backgrounds: string[] = visibleFills.map((fill: FigmaColorObject) => {\n if (fill.type === 'SOLID' && visibleFills.length > 1) {\n return ColorConverter.solidToGradient(fill);\n }\n return ColorConverter.convert(fill);\n });\n\n // IMPORTANT: Reverse the array!\n // Figma fills: index 0 = top layer\n // CSS background: first declared = top layer\n // But Figma's rendering order is bottom-to-top in the fills array\n // So we need to reverse to match CSS rendering order\n backgrounds.reverse();\n\n // Set background with all layers\n inlineStyles.background = backgrounds.join(', ');\n};\n\n// Convert strokes to border\nexport const convertStrokes = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n const strokes = node.strokes;\n\n if (!strokes || strokes.length === 0 || !node.strokeWeight) return;\n\n const visibleStrokes = strokes.filter((s: FigmaColorObject) => s.visible !== false);\n if (visibleStrokes.length === 0) return;\n\n const width = node.strokeWeight || 1;\n let strokeColor = '';\n const isGradientStroke = visibleStrokes.some((s: FigmaColorObject) => s.type.includes('GRADIENT'));\n if (isGradientStroke) {\n const gradient = visibleStrokes.find((s: FigmaColorObject) => s.type.includes('GRADIENT'));\n if (gradient) {\n strokeColor = ColorConverter.convert(gradient);\n }\n inlineStyles.strokeColor = strokeColor;\n inlineStyles.borderRadius = `${node.cornerRadius !== undefined ? node.cornerRadius.toFixed(0) : '0'}px`;\n inlineStyles.strokeWidth = `${width}px`;\n } else {\n const solid = visibleStrokes.find((s: FigmaColorObject) => s.type === 'SOLID');\n if (solid) {\n strokeColor = ColorConverter.convert(solid);\n }\n inlineStyles.border = `${width}px solid ${strokeColor}`;\n }\n};\n\n// Convert clip content\nexport const convertClipContent = (node: FigmaFrameInfo, inlineStyles: CSSStyles): void => {\n if (node.clipsContent) {\n inlineStyles.overflow = 'hidden';\n }\n};\n","import { HumanMessage } from '@langchain/core/messages';\nimport { ChatOpenAI } from '@langchain/openai';\nimport { logger } from './logger';\nimport { getDebugConfig, getModelConfig, type ModelConfig } from './config';\nimport { writeFile } from './file';\nimport { workspaceManager } from './workspace';\n\n/**\n * Content part types for multimodal messages\n */\ntype ContentPart = { type: 'text'; text: string } | { type: 'image_url'; image_url: { url: string } };\n\n/**\n * Options for calling the model\n */\nexport interface CallModelOptions {\n question: string;\n imageUrls?: string | string[];\n streaming?: boolean;\n responseFormat?: { type: 'json_object' | 'text' };\n maxTokens?: number;\n}\n\n/**\n * Validate model configuration\n * @param config - Model configuration to validate\n * @throws Error if required fields are missing\n */\nfunction validateModelConfig(config: ModelConfig | null | undefined): asserts config is ModelConfig {\n if (!config || !config.provider || !config.model || !config.baseUrl || !config.apiKey) {\n throw new Error(\n `Invalid model configuration. Required fields: provider, model, baseUrl, apiKey. Please check your config.yaml file.`\n );\n }\n}\n\n/**\n * Call an LLM model with the given options\n * @param options - Configuration options for the model call\n * @returns The model's text response\n */\nexport async function callModel(options: CallModelOptions): Promise<string> {\n const { question, imageUrls, streaming = false, responseFormat, maxTokens } = options;\n\n // Validate input\n if (!question || !question.trim()) {\n throw new Error('Question must be a non-empty string');\n }\n\n // Get and validate config separately\n let config: ModelConfig;\n try {\n config = getModelConfig();\n validateModelConfig(config);\n } catch (error) {\n if (error instanceof Error) {\n logger.printErrorLog(`Configuration error: ${error.message}`);\n }\n throw error;\n }\n\n try {\n const requestConfig = {\n modelName: config.model,\n apiKey: config.apiKey,\n configuration: {\n baseURL: config.baseUrl,\n },\n ...(maxTokens && { maxTokens }),\n temperature: 0.1,\n streaming,\n ...(streaming && {\n streamingOptions: {\n includeUsage: true,\n },\n }),\n ...(!streaming && { streamUsage: true }),\n ...(responseFormat && { modelKwargs: { response_format: responseFormat } }),\n };\n const agentModel = new ChatOpenAI(requestConfig);\n\n // Build multimodal content parts: text + image_url\n const contentParts: ContentPart[] = [];\n contentParts.push({ type: 'text', text: question });\n\n // Add images if provided\n if (imageUrls) {\n const urls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];\n for (const url of urls) {\n if (url && typeof url === 'string' && url.trim()) {\n contentParts.push({ type: 'image_url', image_url: { url: url.trim() } });\n }\n }\n }\n\n // Create user message - use array if multimodal, string if text-only\n const userMessage = new HumanMessage({\n content: contentParts.length > 1 ? contentParts : question,\n });\n\n const message = await agentModel.invoke([userMessage]);\n\n if (!message.text) {\n throw new Error('Model returned empty response');\n }\n\n const debugConfig = getDebugConfig();\n const isDebugEnabled = debugConfig.enabled;\n if (isDebugEnabled) {\n const debugContent = [\n '------------config------------',\n JSON.stringify(requestConfig, null, 2),\n '------------request------------',\n JSON.stringify(contentParts, null, 2),\n '------------response------------',\n JSON.stringify(message.text, null, 2),\n ].join('\\n');\n writeFile(workspaceManager.path?.debug ?? '', `model_${new Date().toISOString().replace(/:/g, '-')}.md`, debugContent);\n }\n\n return message.text;\n } catch (error) {\n if (error instanceof Error) {\n logger.printErrorLog(`[${config.model}] Error details: ${error.message}`);\n if (error.stack) {\n logger.printTestLog(`[${config.model}] Stack trace: ${error.stack}`);\n }\n }\n throw new Error(`${config.model} model request failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n","import type { FigmaFrameInfo } from '../../../types/figma-types';\nimport type { Protocol } from '../../../types';\nimport { callModel } from '../../../utils/call-model';\nimport { logger } from '../../../utils/logger';\nimport { generateStructurePrompt } from './prompt';\nimport { extractNodePositionsHierarchical, postProcessStructure, populateComponentProps } from './utils';\nimport { extractJSON } from '../../../utils/parser';\n\n/**\n * Structure node - generates component hierarchy from Figma design\n *\n * Responsibilities:\n * 1. Analyzes Figma frame structure using AI model\n * 2. Extracts component relationships and data\n * 3. Generates file paths and naming conventions\n * 4. Populates component props and states for code generation\n *\n * @param state - Current graph state\n * @returns Updated state with protocol\n */\nexport const generateStructure = async (figma: FigmaFrameInfo) => {\n const frames = figma.frames || figma.children;\n const imageWidth = figma.absoluteBoundingBox?.width;\n const thumbnailUrl = figma.thumbnailUrl;\n\n if (!frames || frames.length === 0) {\n logger.printErrorLog('No processed frames found in state');\n throw new Error('No processed frames found');\n }\n\n logger.printInfoLog('Starting structure analysis...');\n\n try {\n // Extract hierarchical position data from Figma frames\n const positions = extractNodePositionsHierarchical(frames);\n const positionsJson = JSON.stringify(positions);\n\n // Generate structure using AI\n const prompt = generateStructurePrompt({\n positions: positionsJson,\n width: imageWidth ? String(imageWidth) : '1440',\n });\n\n logger.printInfoLog('Calling AI model to generate component structure...');\n\n const structureResult = await callModel({\n question: prompt,\n imageUrls: thumbnailUrl,\n responseFormat: { type: 'json_object' },\n maxTokens: 20240,\n });\n\n // Parse AI response\n const jsonContent = extractJSON(structureResult);\n const parsedStructure = JSON.parse(jsonContent) as Protocol | Protocol[];\n\n // Post-process structure: normalize names, populate elements, annotate paths\n logger.printInfoLog('Processing structure tree...');\n postProcessStructure(parsedStructure, frames);\n\n const protocol = (Array.isArray(parsedStructure) ? parsedStructure[0] : parsedStructure) as Protocol;\n\n // Extract component props and states for reusable components\n if (frames && protocol) {\n logger.printInfoLog('Extracting component properties and states...');\n await populateComponentProps(protocol, frames, thumbnailUrl);\n }\n\n logger.printSuccessLog('Component structure generated successfully');\n\n return protocol;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printErrorLog(`Error generating component structure: ${errorMessage}`);\n throw new Error(`Failed to parse component structure: ${errorMessage}`);\n }\n};\n","import type { FigmaFrameInfo, Protocol, FrameData } from '../../../types';\nimport type { SimplifiedFigmaNode, ExtendedFrameStructNode, ParsedDataListResponse } from './types';\nimport path from 'node:path';\nimport { toKebabCase } from '../../../utils/naming';\nimport { extractJSON } from '../../../utils/parser';\nimport { callModel } from '../../../utils/call-model';\nimport { logger } from '../../../utils/logger';\n\n// ============= Figma Node Utilities =============\n/**\n * Simplifies Figma nodes for content extraction, retaining essential fields for AI processing\n * Removes heavy vector data while keeping text content, images, and layout information\n *\n * @param node - The Figma frame node to simplify\n * @returns Simplified node with only essential fields\n */\nexport function simplifyFigmaNodeForContent(node: FigmaFrameInfo): SimplifiedFigmaNode {\n const simple: SimplifiedFigmaNode = {\n id: node.id,\n name: node.name,\n type: node.type,\n };\n\n // Check both url (set by Asset node) and thumbnailUrl (original Figma field)\n const imageUrl = (node as FigmaFrameInfo & { url?: string }).url || node.thumbnailUrl;\n if (imageUrl) {\n simple.url = imageUrl;\n }\n\n if (node.cornerRadius !== undefined) {\n simple.cornerRadius = node.cornerRadius;\n }\n\n if (node.characters !== undefined && node.characters !== null) {\n simple.characters = node.characters;\n }\n\n if (node.visible !== undefined) simple.visible = node.visible;\n\n if (node.absoluteBoundingBox) simple.absoluteBoundingBox = node.absoluteBoundingBox;\n\n if (node.children && Array.isArray(node.children)) {\n simple.children = node.children.map(simplifyFigmaNodeForContent);\n }\n\n if (node.inlineStyles) {\n simple.inlineStyles = node.inlineStyles as Record<string, unknown>;\n }\n\n if (node.style) {\n simple.style = node.style as unknown as Record<string, unknown>;\n }\n\n if (node.strokes && Array.isArray(node.strokes) && node.strokes.length > 0) {\n simple.hasStrokes = true;\n }\n\n return simple;\n}\n\n/**\n * Extract node positions with hierarchical structure preserved\n * Returns nested position data maintaining parent-child relationships\n *\n * @param data - Figma frame data (single frame or array of frames)\n * @returns Hierarchical position data with node information\n */\nexport function extractNodePositionsHierarchical(data: FigmaFrameInfo[] | FigmaFrameInfo | undefined): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n if (!data) {\n return result;\n }\n\n const list = Array.isArray(data) ? data : [data];\n\n for (const item of list) {\n if (item && typeof item === 'object' && item.id) {\n const nodeData: Record<string, unknown> = {};\n\n // Extract position information\n const bounds = item.absoluteBoundingBox || item.absoluteRenderBounds;\n if (bounds) {\n nodeData.x = bounds.x;\n nodeData.y = bounds.y;\n nodeData.w = bounds.width;\n nodeData.h = bounds.height;\n }\n\n // Recursively process children\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n nodeData.children = extractNodePositionsHierarchical(item.children);\n }\n\n result[item.id] = nodeData;\n }\n }\n\n return result;\n}\n\n/**\n * Extract nodes by IDs, including their full subtrees (all children)\n * This allows inspecting the full content of specific component instances\n */\nfunction extractNodesWithSubtreeByIds(tree: FigmaFrameInfo | FigmaFrameInfo[], idList: string[]): FigmaFrameInfo[] {\n const idSet = new Set(idList);\n const result: FigmaFrameInfo[] = [];\n\n const findNodes = (nodes: FigmaFrameInfo[]) => {\n for (const node of nodes) {\n if (idSet.has(node.id)) {\n // Deep clone the node to ensure all fields (including url) are preserved\n const clonedNode = JSON.parse(JSON.stringify(node)) as FigmaFrameInfo;\n result.push(clonedNode);\n // Do not recurse into children of a match, because the match already contains them.\n } else if (node.children && Array.isArray(node.children)) {\n findNodes(node.children);\n }\n }\n };\n\n const nodeArray = Array.isArray(tree) ? tree : [tree];\n findNodes(nodeArray);\n return result;\n}\n\n/**\n * Extract nodes by IDs while preserving hierarchical structure\n * Nodes in idList are kept; if a deep child is in idList but parent isn't, the child is still extracted\n *\n * @param tree - The Figma frame tree to search\n * @param idList - Array of node IDs to extract\n * @param options - Optional settings\n * @param options.includeSubtree - If true, includes all descendants of matched nodes\n * @returns Array of extracted nodes with hierarchy preserved\n */\nexport function extractHierarchicalNodesByIds(\n tree: FigmaFrameInfo | FigmaFrameInfo[],\n idList: string[],\n options?: { includeSubtree?: boolean }\n): FigmaFrameInfo[] {\n if (options?.includeSubtree) {\n return extractNodesWithSubtreeByIds(tree, idList);\n }\n\n const idSet = new Set(idList);\n const result: FigmaFrameInfo[] = [];\n\n // Helper function to check if a node or any of its descendants are in idList\n const hasDescendantInList = (node: FigmaFrameInfo): boolean => {\n if (idSet.has(node.id)) return true;\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n if (hasDescendantInList(child)) {\n return true;\n }\n }\n }\n }\n\n return false;\n };\n\n // Helper function to recursively process a single node\n const processNode = (node: FigmaFrameInfo): FigmaFrameInfo[] => {\n // First check if this node or any descendant is in the list\n if (!hasDescendantInList(node)) {\n return [];\n }\n\n // If current node is in the list, keep it with filtered children\n if (idSet.has(node.id)) {\n const clonedNode: FigmaFrameInfo = { ...node };\n\n // Process children if they exist\n if (node.children && Array.isArray(node.children)) {\n const filteredChildren: FigmaFrameInfo[] = [];\n\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n const processedChildren = processNode(child);\n filteredChildren.push(...processedChildren);\n }\n }\n\n clonedNode.children = filteredChildren.length > 0 ? filteredChildren : [];\n } else {\n clonedNode.children = [];\n }\n\n return [clonedNode];\n } else {\n // Current node is not in the list, but some descendants are\n // Collect all matching descendants and return them (flattened)\n const matchingDescendants: FigmaFrameInfo[] = [];\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n if (typeof child === 'object' && child !== null && 'id' in child) {\n const processedChildren = processNode(child);\n matchingDescendants.push(...processedChildren);\n }\n }\n }\n\n return matchingDescendants;\n }\n };\n\n // Process tree (handle both single node and array)\n const nodeArray = Array.isArray(tree) ? tree : [tree];\n\n for (const node of nodeArray) {\n const processedNodes = processNode(node);\n result.push(...processedNodes);\n }\n\n return result;\n}\n\n// ============= Structure Processing Utilities =============\n\n/**\n * Post-processes the structure tree in a single traversal\n * Performs three operations simultaneously:\n * 1. Normalizes componentName (moves from top-level to data field)\n * 2. Populates elements data from elementIds\n * 3. Annotates with file system paths (path, componentPath, kebabName)\n *\n * @param structure - The parsed structure from AI model\n * @param frames - The Figma frames tree for element extraction\n */\nexport function postProcessStructure(structure?: Protocol | Protocol[] | null, frames?: FigmaFrameInfo[]): void {\n if (!structure) {\n return;\n }\n\n // Utility to join alias path segments (always POSIX '/')\n const joinSegments = (...segments: (string | undefined)[]): string =>\n path.posix.join(...segments.filter((segment): segment is string => Boolean(segment && segment.length)));\n\n const nodes = Array.isArray(structure) ? structure : [structure];\n let rootPath = '@/components';\n\n // Convert component name to kebab-case for file naming\n const toKebabName = (node: Protocol): string => {\n const source = node.data.kebabName || node.data.name || node.id || 'component';\n const kebabName = toKebabCase(source);\n if (!node.data.kebabName) {\n node.data.kebabName = kebabName;\n }\n return kebabName;\n };\n\n const traverse = (node?: Protocol | null, parentPath?: string, level = 0): void => {\n if (!node || !node.data) {\n return;\n }\n\n // 1. Normalize componentName (from top-level to data field)\n const extendedNode = node as ExtendedFrameStructNode;\n const topLevelComponentName = extendedNode.componentName;\n if (topLevelComponentName && !node.data.componentName) {\n node.data.componentName = topLevelComponentName;\n delete extendedNode.componentName;\n }\n\n // 2. Populate elements data from elementIds\n if (frames) {\n const nodeData = node.data as FrameData & { elementIds?: string[] };\n const elementIds = nodeData.elementIds;\n if (elementIds && Array.isArray(elementIds)) {\n if (elementIds.length > 0) {\n node.data.elements = extractHierarchicalNodesByIds(frames, elementIds, { includeSubtree: true });\n } else {\n node.data.elements = [];\n }\n delete nodeData.elementIds;\n } else {\n node.data.elements = node.data.elements || [];\n }\n }\n\n // 3. Annotate with file system paths\n const segment = toKebabName(node);\n let currentPath: string;\n\n if (level === 0) {\n // Root node always uses base path\n currentPath = rootPath;\n rootPath = currentPath;\n } else {\n const ancestorPath = parentPath || rootPath;\n currentPath = joinSegments(ancestorPath, segment);\n }\n\n // For reusable components, generate flat componentPath (non-hierarchical)\n if (node.data.componentName) {\n const componentKebabName = toKebabCase(node.data.componentName);\n node.data.componentPath = joinSegments(rootPath, componentKebabName);\n node.data.path = node.data.componentPath;\n }\n\n node.data.path = currentPath;\n\n // Recursively process children\n if (Array.isArray(node.children) && node.children.length > 0) {\n node.children.forEach(child => traverse(child, node.data.path, level + 1));\n }\n };\n\n nodes.forEach(node => {\n if (!node || !node.data) {\n return;\n }\n traverse(node, undefined, 0);\n });\n}\n\n/**\n * Extract component groups from protocol children\n * Groups components by their componentName for batch processing\n *\n * @param protocol - The protocol node to analyze\n * @returns Map of componentName to array of instances\n */\nexport function extractComponentGroups(node: Protocol): Map<string, Protocol[]> {\n if (!node || !node.children || node.children.length === 0) return new Map();\n const componentGroups = new Map<string, Protocol[]>();\n const validChildren = node.children.filter(c => c && c.data);\n\n validChildren.forEach(child => {\n const name = child.data.componentName;\n if (name) {\n if (!componentGroups.has(name)) {\n componentGroups.set(name, []);\n }\n componentGroups.get(name)!.push(child);\n }\n });\n\n return componentGroups;\n}\n\n/**\n * Applies props and state to the protocol node\n * @param parsed - The parsed data list response\n * @param node - The protocol node to apply the props and state to\n * @param compName - The name of the component\n * @param group - The group of components\n * @param isList - Whether the component is a list\n */\nexport function applyPropsAndStateToProtocol(\n parsed: ParsedDataListResponse,\n node: Protocol,\n compName: string,\n group: Protocol[],\n isList: boolean\n): void {\n if (parsed && parsed.state && Array.isArray(parsed.state)) {\n if (isList) {\n if (!node.data.states) {\n node.data.states = [];\n }\n\n node.data.states.push({\n state: parsed.state,\n componentName: compName,\n componentPath: group[0]?.data.componentPath || '',\n });\n\n const originalChildren: Protocol[] = node.children || [];\n const newChildren: Protocol[] = [];\n const processedComponentNames = new Set<string>();\n\n for (const child of originalChildren) {\n const childName = child.data.componentName;\n if (childName === compName) {\n if (!processedComponentNames.has(childName)) {\n child.data.name = childName;\n child.id = childName;\n const cleanKebabName = toKebabCase(childName);\n child.data.kebabName = cleanKebabName;\n delete child.data.path;\n\n if (parsed.props && Array.isArray(parsed.props)) {\n child.data.props = parsed.props;\n }\n\n newChildren.push(child);\n processedComponentNames.add(childName);\n }\n } else {\n newChildren.push(child);\n }\n }\n\n node.children = newChildren;\n }\n }\n}\n\n/**\n * Extracts component properties and states from repeated component instances\n * For components that appear multiple times (e.g., cards in a grid), this function:\n * 1. Groups instances by componentName\n * 2. Uses AI to extract data variations (text, images, etc.)\n * 3. Generates props schema for the component\n * 4. Collapses duplicate instances into a single template + state array\n *\n * @param node - Current structure node to process\n * @param frames - Figma frames for reference\n * @param thumbnailUrl - Design thumbnail URL for AI visual context\n */\nexport async function populateComponentProps(node: Protocol, frames: FigmaFrameInfo[], thumbnailUrl?: string): Promise<void> {\n if (!node || !node.children || node.children.length === 0) return;\n\n const componentGroups = extractComponentGroups(node);\n\n // Process each component group to extract props and data\n for (const [compName, group] of componentGroups) {\n if (group.length === 0) continue;\n\n const isList = group.length > 1;\n const allElements = group.flatMap(g => g.data.elements || []);\n const simplifiedNodes = allElements\n .filter((n): n is FigmaFrameInfo => typeof n === 'object' && n !== null)\n .map(n => simplifyFigmaNodeForContent(n));\n const figmaDataJson = JSON.stringify(simplifiedNodes);\n const containerName = node.data.name || 'Container';\n\n try {\n const { extractDataListPrompt } = await import('./prompt');\n const prompt = extractDataListPrompt({\n containerName,\n childComponentName: compName,\n figmaData: figmaDataJson,\n });\n\n const result = await callModel({\n question: prompt,\n imageUrls: thumbnailUrl,\n responseFormat: { type: 'json_object' },\n });\n\n const json = extractJSON(result);\n const parsed = JSON.parse(json) as ParsedDataListResponse;\n applyPropsAndStateToProtocol(parsed, node, compName, group, isList);\n } catch (e) {\n logger.printErrorLog(\n `Failed to extract data list for ${compName} in ${containerName}: ${e instanceof Error ? e.message : String(e)}`\n );\n }\n }\n\n // Recursively process children\n for (const child of node.children) {\n await populateComponentProps(child, frames, thumbnailUrl);\n }\n}\n","/**\n * Standard naming utilities for the project.\n * Ensures consistency between kebab-case file paths and PascalCase component names.\n */\n\n/**\n * Converts a string to PascalCase (e.g. \"my-component\" -> \"MyComponent\")\n * Used for Component Names and imports.\n */\nexport function toPascalCase(str: string): string {\n // 1. Replace special chars with space\n // 2. Split by space or capital letters\n // 3. Capitalize first letter of each part\n return str\n .replace(/[^a-zA-Z0-9]+/g, ' ')\n .trim()\n .split(/\\s+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Converts a string to kebab-case (e.g. \"MyComponent\" -> \"my-component\")\n * Used for file paths and directories.\n */\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2') // Split camelCase\n .replace(/[^a-zA-Z0-9]+/g, '-') // Replace non-alphanumeric with hyphen\n .toLowerCase()\n .replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens\n}\n","/**\n * Response parsing utilities for AI model outputs.\n * Handles extraction of structured content from markdown-formatted responses.\n */\n\nimport { FileInfo } from '../types/file-types';\n\n/**\n * Extract JSON content from markdown code blocks\n * Handles cases where AI models wrap JSON in ```json ... ``` or ``` ... ```\n * If no code block markers are found, returns the original content\n *\n * @param response - Model response that may contain markdown code blocks\n * @returns Extracted JSON string without markdown formatting\n *\n * @example\n * // Input: \"```json\\n{\\\"key\\\": \\\"value\\\"}\\n```\"\n * // Output: \"{\\\"key\\\": \\\"value\\\"}\"\n */\nexport function extractJSON(response: string): string {\n // Try to match ```json ... ``` format first\n const jsonBlockMatch = response.match(/```json\\s*\\n([\\s\\S]*?)\\n```/);\n if (jsonBlockMatch && jsonBlockMatch[1]) {\n return jsonBlockMatch[1].trim();\n }\n\n // Try to match generic ``` ... ``` format (with newlines around content)\n const codeBlockMatch = response.match(/```\\s*\\n([\\s\\S]*?)\\n```/);\n if (codeBlockMatch && codeBlockMatch[1]) {\n return codeBlockMatch[1].trim();\n }\n\n // Fallback: no proper code block; use full content but strip loose markdown fences.\n // Some models return raw JSON with trailing \"```\" (e.g. ...]```) or leading ```\\n.\n let cleaned = response.trim();\n cleaned = cleaned.replace(/^\\s*```(?:json)?\\s*\\n?/g, '').replace(/\\s*```+\\s*$/g, '');\n return cleaned;\n}\n\n/**\n * Extract code content from markdown code blocks\n * Handles cases where AI models wrap code in ```tsx ... ``` or ``` ... ```\n * If no code block markers are found, returns the original content\n */\nexport function extractCode(response: string): string {\n // 1. Try to extract content strictly within ``` fences\n // Regex captures content between ```language and ```\n const codeBlockMatch = response.match(/```(?:tsx|typescript|react|js|javascript|json|css|less|scss)?\\s*\\n([\\s\\S]*?)```/);\n\n if (codeBlockMatch && codeBlockMatch[1]) {\n return codeBlockMatch[1].trim();\n }\n\n // 2. Fallback: If no clear block structure is found (or fences are missing/malformed),\n // try to strip loose fences just in case, but usually method 1 catches the block.\n // If the model returned JUST code without fences, this preserves it.\n // If the model returned \"Here is code: code\", this returns the whole string (which might be bad, but safest fallback).\n\n // Removing loose fences if any remain (unlikely if method 1 failed but good for cleanup)\n const cleaned = response\n .replace(/```(tsx|typescript|react|js|javascript|json|css|less|scss)?/g, '')\n .replace(/```/g, '')\n .trim();\n\n return cleaned;\n}\n\n/**\n * Extract multiple files from content with file headers\n * Format:\n * ## filename.tsx\n * ```tsx\n * code...\n * ```\n *\n * ## filename.css\n * ```css\n * styles...\n * ```\n */\nexport function extractFiles(content: string): FileInfo[] {\n const files: FileInfo[] = [];\n\n // Match file sections: ## filename\\n```language\\ncode\\n```\n // Allow optional whitespace around newlines for flexibility\n const fileRegex = /##\\s+([^\\n]+)\\s*\\n\\s*```(?:\\w+)?\\s*\\n([\\s\\S]*?)\\n\\s*```/g;\n let match;\n\n while ((match = fileRegex.exec(content)) !== null) {\n if (match[1] && match[2]) {\n const filename = match[1].trim();\n const code = match[2].trim();\n files.push({ filename, content: code });\n }\n }\n\n return files;\n}\n","import { GraphState } from '../../state';\nimport { figmaTool } from '../../tools/figma-tool';\nimport { FigmaFrameInfo, FigmaUrlInfo } from '../../types/figma-types';\nimport { getFigmaConfig } from '../../utils/config';\nimport { writeFile } from '../../utils/file';\nimport { logger } from '../../utils/logger';\nimport { generateStructure } from './structure';\nimport { ImageNode } from '../../tools/figma-tool/types';\nimport { workspaceManager } from '../../utils/workspace';\n\n/**\n * 'process' node, responsible for generating the protocol for frontend code generation.\n *\n * This function serves as a unified workflow that can be consumed by:\n * 1. LangGraph Node: As part of the design2code graph workflow\n * 2. CLI Command: `f2p` (figma2protocol) - full protocol generation\n * 3. CLI Command: `images` (get-images) - partial workflow (fetch + download only)\n * 4. Script Execution: Direct execution via tsx/node\n *\n * Workflow Steps:\n * - Step 1: Fetch and clean Figma document from API\n * - Step 2: Detect and download images from Figma document\n * - Step 3: Simplify image nodes by replacing properties with URLs\n * - Step 4: Process styles (convert Figma styles to CSS)\n * - Step 5: Write protocol.json and images.json to workspace\n * @param state - The state of the graph.\n * @returns The state of the graph.\n */\nexport const generateProtocol = async (state: GraphState) => {\n const assetsDir = workspaceManager.resolveAppSrc(state.workspace, 'assets');\n const processDir = state.workspace.process;\n const { document, imageNodesMap } = await executeFigmaAndImagesActions(state.urlInfo, assetsDir, processDir);\n\n /* Simplify image nodes in Figma document by replacing redundant properties with url */\n const simplifiedDocument = figmaTool.simplifyImageNodes(document, imageNodesMap);\n /* Process styles (convert Figma styles to CSS) */\n const processedStyleDocument = figmaTool.processedStyle(simplifiedDocument);\n /* Generate structure */\n const protocol = await generateStructure(processedStyleDocument);\n\n // Write protocol.json (contains all Figma data in data.elements)\n writeFile(state.workspace.process, 'protocol.json', JSON.stringify(protocol, null, 2));\n logger.printInfoLog(`Please check the output in the workspace: ${state.workspace.process}`);\n\n return {\n protocol,\n figmaInfo: {\n thumbnail: processedStyleDocument?.thumbnailUrl || document?.thumbnailUrl || '',\n },\n };\n};\n\n/**\n * Executes the Figma and images actions.\n * @param state - The state of the graph.\n * @returns The state of the graph with imageNodesMap as Map<string, ImageNode>.\n */\nexport const executeFigmaAndImagesActions = async (\n urlInfo: FigmaUrlInfo,\n assetsDir: string,\n processDir: string\n): Promise<{ document: FigmaFrameInfo; imageNodesMap: Map<string, ImageNode> }> => {\n const { fileId, nodeId } = urlInfo;\n if (!fileId || !nodeId) {\n throw new Error('Invalid Figma URL');\n }\n\n const token = getFigmaConfig().token;\n if (!token) {\n throw new Error('Figma API token is required');\n }\n\n /* Fetch and clean Figma document */\n const document = await figmaTool.fetchAndClean(fileId, nodeId, token);\n if (!document) {\n throw new Error('Failed to fetch and clean Figma document');\n }\n writeFile(processDir, 'figma.json', JSON.stringify(document, null, 2));\n logger.printSuccessLog(`Figma document fetched and cleaned successfully`);\n\n /* Detect and download images from Figma document */\n const downloadResult: { successCount: number; failCount: number; imageNodesMap: Map<string, ImageNode> } =\n await figmaTool.downloadImages(fileId, token, assetsDir, document);\n const { successCount, failCount, imageNodesMap } = downloadResult;\n\n if (successCount) {\n logger.printSuccessLog(`Downloaded ${successCount} images`);\n }\n if (failCount) {\n logger.printWarnLog(`Failed to download ${failCount} images`);\n }\n\n /* Write images.json with array format for JSON compatibility */\n const resultsArray = Array.from(imageNodesMap.values());\n writeFile(processDir, 'images.json', JSON.stringify(resultsArray, null, 2));\n\n return {\n document,\n imageNodesMap,\n };\n};\n","/**\n * Skill API Module\n *\n * This module exports all utilities needed for SKILL execution in IDEs like Cursor or Claude Code.\n * It intentionally does NOT include model calling logic - the IDE's AI agent handles that.\n *\n * Design Philosophy:\n * - Export data processing utilities (Figma, Protocol, etc.)\n * - Export prompt generation functions (same as CLI for consistency)\n * - Let the IDE's AI agent execute the prompts using its own model\n * - Provide helper functions for protocol manipulation\n */\n\n// ============= Type Exports =============\nexport type { Protocol, FigmaFrameInfo, ParsedDataListResponse } from './types';\n\n// ============= Initial Agent Exports =============\nexport { INITIAL_AGENT_SYSTEM_PROMPT } from './agents/initial-agent/prompt';\nexport { initialAgentInstruction } from './agents/initial-agent/instruction';\n\n// ============= Protocol Agent Exports =============\nexport { parseFigmaUrl } from './utils/url-parser';\nexport { executeFigmaAndImagesActions } from './nodes/process';\nexport { figmaTool } from './tools/figma-tool';\nexport {\n extractNodePositionsHierarchical,\n postProcessStructure,\n extractComponentGroups,\n simplifyFigmaNodeForContent,\n extractHierarchicalNodesByIds,\n applyPropsAndStateToProtocol,\n} from './nodes/process/structure/utils';\nexport { generateStructurePrompt, extractDataListPrompt } from './nodes/process/structure/prompt';\nexport { extractJSON } from './utils/parser';\n\n// ============= Code Generation Constants (Same as CLI) =============\nexport { flattenPostOrder, detectRenderingModes, saveGeneratedCode } from './nodes/code/utils';\nexport { DEFAULT_STYLING } from './nodes/code/constants';\nexport { generateFramePrompt, generateComponentPrompt } from './nodes/code/prompt';\nexport { workspaceManager } from './utils/workspace';\n\n// ============= Image Utilities (reuse existing figma-tool) =============\nexport { downloadImage } from './tools/figma-tool/images';\n\n// ============= Naming Utilities (ensure path consistency) =============\nexport { toKebabCase, toPascalCase } from './utils/naming';\n","// ============================================\n// Common Prompt Sections\n// ============================================\n\nconst STYLING_GUIDELINES = `\n - **Style Consistency**: Implement styles using the technical stack and libraries listed in <styling>.\n - **Strict Restriction**: Absolutely ONLY use the technical stack and libraries listed in <styling>. Do NOT use any other styling methods, libraries, or frameworks (e.g., if clsx is not listed, do not use clsx).\n - **Default Styling**: If <styling> is empty or does not contain specific libraries, DEFAULT to standard vanilla CSS.\n \n - **Tailwind CSS + CSS Modules (CRITICAL)**:\n - If the stack includes BOTH Tailwind and CSS Modules (Less/SCSS), use them correctly:\n 1. **Tailwind utilities**: Use DIRECTLY in JSX className (e.g., \\`className=\"flex items-center gap-4\"\\`)\n 2. **CSS Modules**: Use ONLY for complex styles that can't be expressed with Tailwind utilities (e.g., gradients, animations, pseudo-elements)\n 3. **NEVER use \\`@apply\\` in CSS Module files** - it's a Tailwind-specific directive that doesn't work in Less/SCSS\n 4. Example correct usage:\n TSX: \\`<div className={\\`flex \\${styles.customGradient}\\`}>\\`\n Less: \\`.customGradient { background: linear-gradient(...); }\\`\n \n - **CSS Modules Only**: If the tech stack specifies CSS Modules without Tailwind:\n 1. Create a corresponding style file (e.g., \\`index.module.less\\`, \\`index.module.scss\\`, or \\`index.module.css\\`)\n 2. Import it as \\`import styles from './index.module.[ext]';\\` in the TSX\n 3. Define all styles in the style file using standard CSS/Less/SCSS syntax\n 4. Use \\`styles.className\\` in JSX`;\n\nconst ASSETS_HANDLING = `\n - **CRITICAL**: For any image URL starting with \\`@/assets\\`, you MUST import it at the top of the file.\n - **Asset Name Matching**: \n - Check the \\`<available_assets>\\` list for actual filenames in the project.\n - Asset filenames follow the pattern: \\`kebab-case-name-id1-id2.ext\\` (e.g., \"Star 2.svg\" → \"star-2-1-2861.svg\")\n - Match the base name (ignoring spaces, case, and ID suffix): \"@/assets/arXiv.svg\" → look for \"arxiv-*.svg\" in the list\n - Use the EXACT filename from the available assets list in your import.\n - Example: If available_assets contains \"arxiv-1-2956.svg\", use:\n \\`import ArXivIcon from '@/assets/arxiv-1-2956.svg';\\`\n - **Usage**: \\`<img src={ArXivIcon} />\\`, do not use backgroundImage property.\n - **NEVER** use the string path directly in JSX or styles.`;\n\nconst DOM_IDS_REQUIREMENT = `\n - Assign \\`id\\` attributes to the main container and any internal elements, matching \\`frame_details\\`.`;\n\nconst REACT_IMPORT_RULE = `\n - Do **NOT** include \\`import React from 'react';\\` at the top of the file.`;\n\nconst FILE_NAMING_CONVENTION = `\n - ALWAYS name the main component file \\`index.tsx\\`.\n - ALWAYS name the style file (if applicable) \\`index.module.[css|less|scss]\\`.\n - NEVER use PascalCase or other names for filenames (e.g., DO NOT use \\`MainFrame.tsx\\` or \\`Button.tsx\\`).`;\n\nconst OUTPUT_FORMAT = `\n <output_format>\n If only one file (TSX) is needed:\n \\`\\`\\`tsx\n // code...\n \\`\\`\\`\n\n If multiple files are needed (e.g., TSX + Styles):\n ## index.tsx\n \\`\\`\\`tsx\n // code...\n \\`\\`\\`\n\n ## index.module.[css|less|scss]\n \\`\\`\\`[css|less|scss]\n // styles...\n \\`\\`\\`\n </output_format>`;\n\n// ============================================\n// Prompt Functions\n// ============================================\n\n/**\n * Generate children rendering instructions based on detected modes\n */\nfunction generateChildrenPropsInstructions(modes: { hasStates: boolean; hasIndependentChildren: boolean }): string {\n const instructions: string[] = [];\n\n if (modes.hasStates) {\n instructions.push(`\n - **List Rendering (States-based)**:\n - Check if \\`<frame_details>\\` contains a \\`states\\` property (array).\n - Each state entry has: \\`state\\` (data array), \\`componentName\\`, \\`componentPath\\`.\n - Implementation:\n \\`\\`\\`tsx\n import ComponentName from 'path';\n \n {states[0].state.map((item, index) => (\n <ComponentName key={index} {...item} />\n ))}\n \\`\\`\\`\n - **CRITICAL - Only Use State Data**:\n - **ONLY** pass props that exist in the state data objects.\n - **DO NOT** add extra props like \\`content\\`, \\`className\\`, or any other fields not present in state.\n - **DO NOT** create or invent additional data - use exactly what's in the state array.\n - Example: If state has \\`{iconSrc, title, description}\\`, only pass those three props.\n - **Asset Imports**: If state data contains image paths (e.g., \\`imageSrc\\`, \\`iconSrc\\`), \n import them at the top and pass as values.`);\n }\n\n if (modes.hasIndependentChildren) {\n instructions.push(`\n - **Independent Components (No Props) - CRITICAL**:\n - If a child has NO \\`componentName\\` and NO \\`properties\\`, render as \\`<ComponentName />\\` without any props.\n - These components use default values or hardcoded content internally.`);\n }\n\n return instructions.join('\\n');\n}\n\nexport const generateFramePrompt = ({\n childrenImports,\n frameDetails,\n assetFiles,\n styling,\n renderingModes,\n}: {\n childrenImports: string;\n frameDetails: string;\n assetFiles?: string;\n styling: string;\n renderingModes: {\n hasStates: boolean;\n hasIndependentChildren: boolean;\n };\n}) => {\n return `\n<system_instructions>\n <role>\n You are a React Architect.\n Your task is to assemble a \"Frame\" component that composes multiple child components based on Figma layout data.\n </role>\n\n <input_context>\n <frame_details>${frameDetails}</frame_details>\n <children>${childrenImports}</children>\n <styling>${styling}</styling>\n ${assetFiles ? `<available_assets>Available asset files: ${assetFiles}</available_assets>` : ''}\n\n <frame_structure>\n The \\`frame_details\\` parameter contains:\n - \\`layout\\`: Layout information for the frame (flex/grid/absolute)\n - \\`elements\\`: Array of CSS styles and asset URLs for all elements in this component (colors, spacing, fonts, backgrounds, etc.)\n - \\`states\\` (optional): If present, this frame contains reusable components. Each state entry includes:\n * \\`state\\`: Array of data objects to be used as props for component instances\n * \\`componentName\\`: Name of the reusable component\n * \\`componentPath\\`: Import path for the component\n Use \\`states\\` data to render multiple instances of reusable components via \\`.map()\\`.\n </frame_structure>\n </input_context>\n\n <requirements>\n <req_1>\n **Children Components & Props (CRITICAL)**:\n - The \\`<children>\\` field describes child components with their import paths and prop types.\n${generateChildrenPropsInstructions(renderingModes)}\n \n - **Component Imports (CRITICAL)**:\n - You MUST use the exact import path provided in the \\`<children>\\` list for each component.\n - **Example**:\n - Provided: \\`{\"name\": \"TaskGrid\", \"path\": \"@/components/tasks-section/task-grid\"}\\`\n - CORRECT: \\`import TaskGrid from \"@/components/tasks-section/task-grid\";\\`\n - WRONG: \\`import TaskGrid from \"@/components/task-grid\";\\` (Do NOT do this!)\n </req_1>\n \n <req_2>\n **Layout & Styling**:\n - Use \\`layout_data\\` for dimensions, spacing, and flow (flex/grid).\n${STYLING_GUIDELINES}\n - Use responsive utilities provided by the chosen libraries to ensure the component is adaptive.\n - Use \\`css_context\\` for exact background styles, gradients, and shadows.\n - Use \\`relative\\` positioning for the container.\n - Use \\`spacing\\` field in <frame_details> to set the spacing between elements\n </req_2>\n \n <req_3>\n **Images & Assets**:\n${ASSETS_HANDLING}\n </req_3>\n \n <req_4>\n **DOM IDs**:\n${DOM_IDS_REQUIREMENT}\n </req_4>\n \n <req_5>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_5>\n \n <req_6>\n **File Naming**:\n${FILE_NAMING_CONVENTION}\n </req_6>\n </requirements>\n\n${OUTPUT_FORMAT}\n</system_instructions>\n`.trim();\n};\n\nexport const generateComponentPrompt = ({\n componentName,\n componentDetails,\n styling,\n assetFiles,\n}: {\n componentName: string;\n componentDetails: string;\n styling: string;\n assetFiles?: string;\n}) => {\n return `\n<system_instructions>\n <role>\n You are a Pixel-Perfect React Frontend Engineer.\n Your goal is to implement a specific UI component from Figma design data with 100% visual fidelity while ensuring header scroll to sections on click.\n </role>\n\n <input_context>\n <component_details>${componentDetails}</component_details>\n ${assetFiles ? `<available_assets>Available asset files: ${assetFiles}</available_assets>` : ''}\n <styling>${styling}</styling>\n\n <component_structure>\n The \\`component_details\\` parameter contains:\n - \\`elements\\`: Array of CSS styles and asset URLs for all elements in this component (colors, spacing, fonts, backgrounds, etc.)\n - \\`componentName\\` (optional): If present, indicates this is a **reusable component**; if absent, it's a **regular component**\n - \\`props\\` (optional): Props interface definition, only present when \\`componentName\\` exists\n </component_structure>\n\n <global_styles>\n .gradientBorder {\n position: relative;\n z-index: 0;\n &::before {\n content: '';\n position: absolute;\n inset: 0;\n padding: 1px; // From strokeWidth\n border-radius: inherit;\n background: linear-gradient(278deg, rgba(170, 255, 248, 0.60) 1.63%, rgba(71, 169, 255, 0.60) 104.34%); // From strokeColor\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: destination-out;\n mask-composite: exclude;\n z-index: -1;\n }\n } \n </global_styles>\n\n <visual_reference>\n Refer to the conversation history for the page thumbnail and context.\n Use it to verify visual details like shadows, gradients, and spacing.\n </visual_reference>\n </input_context>\n\n <requirements>\n <req_1>\n **High Fidelity & Responsive**:\n${STYLING_GUIDELINES}\n - **CRITICAL**: Use exact design values from \\`component_details.elements\\` (colors, spacing, font sizes, gradients, shadows, etc.)\n - Use responsive utilities provided by the chosen libraries\n - For complex styles (gradients, shadows), use values from \\`elements\\` directly in CSS Modules or inline styles\n - For gradient rounded borders, CHECK \\`global_styles\\` for \\`.gradientBorder\\` mixin\n </req_1>\n\n <req_2>\n **DOM Identification**:\n - EVERY DOM element corresponding to a Figma node MUST have an \\`id\\` attribute.\n - The \\`id\\` value must match the \\`id\\` in the Figma Data exactly.\n - This is crucial for automated position validation.\n </req_2>\n\n <req_3>\n **Images & Assets**:\n${ASSETS_HANDLING}\n </req_3>\n\n <req_4>\n **Semantic HTML**:\n - Use semantic tags: \\`<header>\\`, \\`<footer>\\`, \\`<nav>\\`, \\`<article>\\`, \\`<section>\\`, \\`<button>\\`.\n </req_4>\n\n <req_5>\n **Layout Strategy**:\n - PREFER relative/flex/grid layout.\n - Use absolute positioning ONLY for decoration/overlays or if Figma structure explicitly implies overlay.\n </req_5>\n\n <req_6>\n **Naming Conventions**:\n - Component Name: **${componentName}** (PascalCase).\n - Export default.\n </req_6>\n\n <req_7>\n **Component Type & Props (CRITICAL)**:\n \n **IF \\`component_details.componentName\\` exists:**\n - This is a **reusable component**\n - Generate props interface from \\`component_details.props\\`: \\`interface ${componentName}Props { ... }\\`\n - Reference props in JSX: \\`{title}\\`, \\`<img src={iconSrc} />\\` (NOT hardcoded values)\n \n **IF \\`component_details.componentName\\` does NOT exist:**\n - This is a **regular component**\n - Do NOT generate props interface\n - Directly hardcode content from \\`component_details.elements\\` into JSX\n \n **Both types**: Do NOT repeat JSX to simulate grids; parent components handle iteration.\n </req_7>\n\n <req_9>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_9>\n \n <req_10>\n **File Naming**:\n${FILE_NAMING_CONVENTION}\n </req_10>\n </requirements>\n\n${OUTPUT_FORMAT}\n</system_instructions>\n`.trim();\n};\n\nexport const injectRootComponentPrompt = ({\n appContent,\n componentName,\n componentPath,\n}: {\n appContent: string;\n componentName: string;\n componentPath: string;\n}) => {\n return `\n<system_instructions>\n <role>\n You are a React code refactoring expert.\n Your task is to inject a root component into an existing App.tsx file with minimal changes.\n </role>\n\n <input_context>\n <current_app_content>\n${appContent}\n </current_app_content>\n <component_to_inject>\n - Component Name: ${componentName}\n - Import Path: ${componentPath}\n </component_to_inject>\n </input_context>\n\n <requirements>\n <req_1>\n **Import Statement**:\n - Add the import statement at the top of the file: \\`import ${componentName} from '${componentPath}';\\`\n - Place it after existing imports, before the component definition.\n - Do NOT remove or modify existing imports.\n </req_1>\n\n <req_2>\n **Component Rendering**:\n - Inside the App component's return statement, replace the existing content with \\`<${componentName} />\\`.\n - If the return contains a wrapper div or other container, keep it and place the component inside.\n - Preserve any existing className, styles, or other attributes on wrapper elements.\n </req_2>\n\n <req_3>\n **Preserve Existing Code**:\n - Keep all existing imports (CSS, Less, etc.)\n - Preserve any hooks, state, or logic inside the App component\n - Maintain the component structure and export statement\n - Do NOT add comments or explanatory text\n </req_3>\n\n <req_4>\n **Minimal Changes**:\n - Only add the necessary import and render the component\n - Do NOT refactor or optimize existing code\n - Do NOT change formatting or styling unless necessary\n - Do NOT add TypeScript types unless they already exist\n </req_4>\n\n <req_5>\n **React Import**:\n${REACT_IMPORT_RULE}\n </req_5>\n </requirements>\n\n <output_format>\n Return ONLY the complete updated App.tsx code without markdown code blocks or explanation.\n The output should be valid TypeScript/React code that can be directly written to the file.\n </output_format>\n</system_instructions>\n`.trim();\n};\n","export const DEFAULT_STYLING = {\n approach: 'Tailwind V4 and Less',\n libraries: [\n {\n name: 'Tailwind V4',\n role: 'utility_first',\n },\n {\n name: 'Less',\n role: 'css_preprocessor',\n },\n ],\n};\n\nexport const DEFAULT_APP_CONTENT = `function App() {\n return (\n <div>\n {/* Component will be injected here */}\n </div>\n );\n}\n\nexport default App;`;\n","import fs from 'fs';\nimport { GraphState } from '../../state';\nimport { logger } from '../../utils/logger';\nimport { callModel } from '../../utils/call-model';\nimport { promisePool } from '../../utils/promise-pool';\nimport { generateFramePrompt, generateComponentPrompt, injectRootComponentPrompt } from './prompt';\nimport { Protocol } from '../../types';\nimport { createFiles, writeFile } from '../../utils/file';\nimport { DEFAULT_APP_CONTENT, DEFAULT_STYLING } from './constants';\nimport path from 'path';\nimport { extractCode, extractFiles } from '../../utils/parser';\nimport { workspaceManager } from '../../utils/workspace';\nimport { CodeCache, isComponentGenerated, saveComponentGenerated, isAppInjected, saveAppInjected } from '../../utils/code-cache';\n\n/**\n * Process a node tree and generate code for all nodes\n * Uses post-order traversal (children first, then parent)\n */\nexport async function processNode(state: GraphState, cache: CodeCache): Promise<number> {\n // Read asset files list once for the entire generation run\n const assetFilesList = getAssetFilesList(state);\n\n // Flatten tree using post-order traversal (children first, then parent)\n const flatNodes = flattenPostOrder(state.protocol!);\n const total = flatNodes.length;\n\n if (total === 0) {\n logger.printWarnLog('No components found in structure to generate.');\n return 0;\n }\n\n logger.printInfoLog(`Processing ${total} nodes...`);\n\n let processedCount = 0;\n let skippedCount = 0;\n\n const processSingleNode = async (currentNode: Protocol) => {\n const componentName = currentNode.data.name || currentNode.data.componentName || 'UnknownComponent';\n const nodeId = currentNode.id;\n\n // Check if component is already generated\n if (isComponentGenerated(cache, nodeId)) {\n skippedCount++;\n logger.printInfoLog(`[${processedCount + skippedCount}/${total}] ⏭️ Skipping (cached): ${componentName}`);\n return;\n }\n\n const progressInfo = `[${++processedCount + skippedCount}/${total}]`;\n\n const isLeaf = !currentNode.children?.length;\n if (isLeaf) {\n await generateComponent(currentNode, state, assetFilesList, progressInfo);\n } else {\n await generateFrame(currentNode, state, assetFilesList, progressInfo);\n }\n\n // Mark component as generated and save immediately to prevent cache loss on interruption\n saveComponentGenerated(cache, nodeId, state.workspace);\n };\n\n // Process nodes with concurrency control\n await promisePool(flatNodes, processSingleNode);\n\n if (skippedCount > 0) {\n logger.printInfoLog(`⏭️ Skipped ${skippedCount} cached components`);\n }\n logger.printSuccessLog(`Generated ${processedCount} components`);\n return processedCount;\n}\n\n/**\n * Flatten tree into array using post-order traversal\n */\nexport function flattenPostOrder(node: Protocol): Protocol[] {\n const result: Protocol[] = [];\n\n function traverse(n: Protocol) {\n n.children?.forEach(child => traverse(child));\n result.push(n);\n }\n\n traverse(node);\n return result;\n}\n\n/**\n * Detect which rendering modes are used in this frame\n */\nexport function detectRenderingModes(node: Protocol): {\n hasStates: boolean;\n hasIndependentChildren: boolean;\n} {\n const hasStates = !!node.data.states?.length;\n\n let hasIndependentChildren = false;\n\n (node.children || []).forEach(child => {\n if (!child.data.componentName) {\n hasIndependentChildren = true;\n }\n });\n\n return { hasStates, hasIndependentChildren };\n}\n\n/**\n * Generate a frame/container component\n * Frames compose multiple child components based on layout\n */\nexport async function generateFrame(node: Protocol, state: GraphState, assetFilesList: string, progressInfo: string): Promise<void> {\n const frameName = node.data.name;\n logger.printInfoLog(`${progressInfo} 🖼️ Generating Frame: ${frameName}`);\n\n // Build children imports information\n const childrenImports = (node.children || []).map(child => ({\n name: child.data.name || '',\n path: child.data.path,\n }));\n\n // Detect rendering modes\n const renderingModes = detectRenderingModes(node);\n\n // Generate prompt\n const prompt = generateFramePrompt({\n frameDetails: JSON.stringify(node.data),\n childrenImports: JSON.stringify(childrenImports),\n styling: JSON.stringify(DEFAULT_STYLING),\n assetFiles: assetFilesList,\n renderingModes,\n });\n\n // Call AI model\n const code = await callModel({\n question: prompt,\n imageUrls: state.figmaInfo.thumbnail,\n });\n\n // Save generated files\n const componentPath = node.data.path || '';\n const filePath = workspaceManager.resolveAppSrc(state.workspace, workspaceManager.resolveComponentPath(componentPath));\n saveGeneratedCode(code, filePath);\n logger.printSuccessLog(`Successfully generated frame: ${frameName}`);\n}\n\n/**\n * Generate a component (leaf or reusable)\n * Components are self-contained UI elements driven by props\n */\nexport async function generateComponent(node: Protocol, state: GraphState, assetFilesList: string, progressInfo: string): Promise<void> {\n const componentName = node.data.componentName || node.data.name || 'UnknownComponent';\n const componentPath = node.data.componentPath || node.data.path || '';\n\n logger.printInfoLog(`${progressInfo} 📦 Generating Component: ${componentName}`);\n\n // Generate prompt\n const prompt = generateComponentPrompt({\n componentName,\n componentDetails: JSON.stringify(node.data),\n styling: JSON.stringify(DEFAULT_STYLING),\n assetFiles: assetFilesList,\n });\n\n // Call AI model\n const code = await callModel({\n question: prompt,\n imageUrls: state.figmaInfo.thumbnail,\n });\n\n // Save generated files\n const filePath = workspaceManager.resolveAppSrc(state.workspace, workspaceManager.resolveComponentPath(componentPath));\n saveGeneratedCode(code, filePath);\n logger.printSuccessLog(`Successfully generated component: ${componentName}`);\n}\n\n/**\n * Helper function to save generated code (handles both single and multi-file output)\n */\nexport function saveGeneratedCode(code: string, filePath: string): void {\n const files = extractFiles(code);\n\n if (files.length > 0) {\n // Multi-file output (e.g., index.tsx + index.module.less)\n createFiles({ files, filePath });\n } else {\n const extractedCode = extractCode(code);\n const folderPath = path.dirname(filePath);\n const fileName = path.basename(filePath);\n writeFile(folderPath, fileName, extractedCode);\n }\n}\n\n/**\n * Get list of available asset files for AI to match against\n */\nfunction getAssetFilesList(state: GraphState) {\n try {\n const assetsDir = workspaceManager.resolveAppSrc(state.workspace, 'assets');\n\n if (!fs.existsSync(assetsDir)) {\n return '';\n }\n\n const files = fs.readdirSync(assetsDir);\n return files.join(', ');\n } catch {\n return '';\n }\n}\n\n/**\n * Inject root component into App.tsx\n * Reads existing App.tsx, adds import and renders the root component\n */\nexport async function injectRootComponentToApp(state: GraphState, cache: CodeCache): Promise<void> {\n try {\n // Check if already injected\n if (isAppInjected(cache)) {\n logger.printInfoLog('⏭️ Skipping App.tsx injection (already injected)');\n return;\n }\n\n logger.printInfoLog('💉 Injecting root component into App.tsx...');\n\n // Construct App.tsx path\n const appTsxPath = workspaceManager.resolveAppSrc(state.workspace, 'App.tsx');\n\n // Read existing App.tsx or use default template\n let appContent: string;\n try {\n appContent = fs.readFileSync(appTsxPath, 'utf8');\n } catch {\n // Use default template if App.tsx doesn't exist\n logger.printWarnLog('App.tsx not found, using default template');\n appContent = DEFAULT_APP_CONTENT;\n }\n\n // Get root component information\n const rootNode = state.protocol!;\n const componentName = rootNode.data.name || 'RootComponent';\n const componentPath = rootNode.data.path || '';\n\n // Generate prompt\n const prompt = injectRootComponentPrompt({\n appContent,\n componentName,\n componentPath,\n });\n\n // Call AI model\n const updatedCode = await callModel({\n question: prompt,\n });\n\n // Extract code (no markdown blocks expected based on prompt requirements)\n const finalCode = updatedCode.includes('```') ? extractCode(updatedCode) : updatedCode.trim();\n\n // Write updated App.tsx\n const appFolderPath = path.dirname(appTsxPath);\n writeFile(appFolderPath, 'App.tsx', finalCode);\n\n // Mark as injected and save immediately\n saveAppInjected(cache, state.workspace);\n\n logger.printSuccessLog(`Successfully injected ${componentName} into App.tsx`);\n } catch (error) {\n logger.printErrorLog(`Failed to inject root component: ${(error as Error).message}`);\n // Don't throw - allow the process to continue even if injection fails\n }\n}\n","import fs from 'node:fs';\nimport { WorkspaceStructure } from '../types/workspace-types';\nimport { logger } from './logger';\n\n/**\n * Code generation cache structure\n * Tracks which components have been generated and whether App.tsx has been injected\n */\nexport interface CodeCache {\n generatedComponents: string[]; // Array of node IDs that have been generated\n appInjected: boolean;\n}\n\n/**\n * Get the path to the cache file\n */\nfunction getCachePath(workspace: WorkspaceStructure): string {\n return workspace.checkpoint;\n}\n\n/**\n * Load code generation cache from file\n * Returns empty cache if file doesn't exist or on error\n */\nexport function loadCodeCache(workspace: WorkspaceStructure): CodeCache {\n const cachePath = getCachePath(workspace);\n\n try {\n if (!fs.existsSync(cachePath)) {\n logger.printInfoLog('No code cache found, starting fresh');\n return createEmptyCache();\n }\n\n const content = fs.readFileSync(cachePath, 'utf-8');\n const cache = JSON.parse(content) as CodeCache;\n\n // Validate cache structure\n if (!Array.isArray(cache.generatedComponents) || typeof cache.appInjected !== 'boolean') {\n logger.printWarnLog('Invalid cache format, starting fresh');\n return createEmptyCache();\n }\n\n logger.printInfoLog(`Loaded code cache: ${cache.generatedComponents.length} components cached`);\n return cache;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to load code cache: ${errorMessage}. Starting fresh.`);\n return createEmptyCache();\n }\n}\n\n/**\n * Save code generation cache to file\n */\nexport function saveCodeCache(workspace: WorkspaceStructure, cache: CodeCache): void {\n const cachePath = getCachePath(workspace);\n\n try {\n // Ensure process directory exists\n if (!fs.existsSync(workspace.process)) {\n fs.mkdirSync(workspace.process, { recursive: true });\n }\n\n const content = JSON.stringify(cache, null, 2);\n fs.writeFileSync(cachePath, content, 'utf-8');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.printWarnLog(`Failed to save code cache: ${errorMessage}`);\n }\n}\n\n/**\n * Check if a component has already been generated\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to check\n */\nexport function isComponentGenerated(cache: CodeCache, nodeId: string): boolean {\n return cache.generatedComponents.includes(nodeId);\n}\n\n/**\n * Mark a component as generated\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to mark as generated\n */\nexport function markComponentGenerated(cache: CodeCache, nodeId: string): void {\n if (!isComponentGenerated(cache, nodeId)) {\n cache.generatedComponents.push(nodeId);\n }\n}\n\n/**\n * Mark a component as generated and save cache immediately\n * Combines markComponentGenerated and saveCodeCache for convenience\n * @param cache - The code cache object\n * @param nodeId - The unique node ID to mark as generated\n * @param workspace - The workspace structure containing cache file path\n */\nexport function saveComponentGenerated(cache: CodeCache, nodeId: string, workspace: WorkspaceStructure): void {\n markComponentGenerated(cache, nodeId);\n saveCodeCache(workspace, cache);\n}\n\n/**\n * Check if root component has been injected into App.tsx\n */\nexport function isAppInjected(cache: CodeCache): boolean {\n return cache.appInjected;\n}\n\n/**\n * Mark App.tsx as injected\n */\nexport function markAppInjected(cache: CodeCache): void {\n cache.appInjected = true;\n}\n\n/**\n * Mark App.tsx as injected and save cache immediately\n * Combines markAppInjected and saveCodeCache for convenience\n */\nexport function saveAppInjected(cache: CodeCache, workspace: WorkspaceStructure): void {\n markAppInjected(cache);\n saveCodeCache(workspace, cache);\n}\n\n/**\n * Create an empty cache\n */\nfunction createEmptyCache(): CodeCache {\n return {\n generatedComponents: [],\n appInjected: false,\n };\n}\n"],"mappings":"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","names":["GraphNode","ValidationMode","FigmaImageFormat","tools","path","fs","path","fs","fs","path","axios","axios","fs","path","resolve","cssAngle","_init","tools","path","path","path","fs","path"]}
|