# Build System Reference Complete guide to the Pencil build system for component linking and monolithic builds. ## Overview The build system enables: - **Component Linking**: Reference components across files - **Monolithic Builds**: Merge all pages + components into one file - **Standard Builds**: Inject components into individual pages - **Validation**: Ensure output structure is valid ## Configuration ### pencil.config.json Create in project root: ```json { "version": "1.0", "sourceDir": "src", "buildDir": "build", "componentLibrary": "src/components/ui-kit.pen", "buildOptions": { "minify": false, "validateAfterBuild": true, "preserveComments": true } } ``` | Field | Description | |-------|-------------| | `sourceDir` | Source files directory | | `buildDir` | Build output directory | | `componentLibrary` | Path to component library .pen file | | `minify` | Minify JSON output | | `validateAfterBuild` | Validate JSON after build | | `preserveComments` | Keep JSON comments | ## Build Modes ### Monolithic Build ⭐ (Recommended) Merges all pages + component library into one file. **Commands:** ```bash python3 scripts/build.py --monolithic python3 scripts/build.py -m python3 scripts/build.py --mode monolithic # Custom output name python3 scripts/build.py -m --output myDesign.pen ``` **Output:** - **File**: `tPOS.pen` (project root) - **Contains**: All pages + component library as separate frames - **Size**: ~326KB (example) - **Use case**: Opening complete design, sharing, archiving **Example structure:** ```json { "version": "2.6", "children": [ { "type": "frame", "name": "Component Library", "children": [ /* All reusable components */ ] }, { "type": "frame", "name": "Desktop Home Page", "children": [ /* Page content */ ] }, { "type": "frame", "name": "Mobile Home Page", "children": [ /* Page content */ ] } ], "variables": { /* Design tokens */ } } ``` ### Component Library Build (Atomic Design) Merges distributed atomic components into a single library file. **Command:** ```bash python3 scripts/build.py --library python3 scripts/build.py -l ``` **Output:** - **File**: `src/components/ui-kit.pen` (or configured path) - **Contains**: All atoms, molecules, organisms merged into one file - **Use case**: Generating the library to use during page design ### Standard Build Builds individual pages with component injection. **Command:** ```bash python3 scripts/build.py python3 scripts/build.py --mode standard ``` **Output:** - **Directory**: `build/` - **Files**: Separate `.pen` files with injected components - **Use case**: Development workflow, testing component refs **Example:** ``` build/ ├── desktop-home.pen # With injected components ├── mobile-home.pen # With injected components └── tablet-home.pen # With injected components ``` ## Build Process ### What the Build Script Does 1. **Load Component Library**: Read `ui-kit.pen` 2. **Extract Components**: Find all `reusable: true` components 3. **Extract Variables**: Get design tokens 4. **Find Component Refs**: Search for `component_ref` types in pages 5. **Resolve Paths**: Match `ui-kit#Button/Primary` to actual component 6. **Inject Components**: Add component definitions to pages 7. **Convert Refs**: Transform `component_ref` → `ref` with proper IDs 8. **Validate**: Check output structure ### Transformation Example **Before build (source file):** ```json { "type": "component_ref", "component": "ui-kit#Button/Primary", "name": "submitButton", "overrides": { "label_id": { "content": "Submit" } } } ``` **After build (output file):** Step 1: Component injected at top of children array ```json { "type": "frame", "id": "abc123", "name": "Button/Primary", "reusable": true, "children": [ { "type": "text", "id": "label_id", "content": "Button" } ] } ``` Step 2: Reference converted ```json { "type": "ref", "ref": "abc123", "name": "submitButton", "descendants": { "label_id": { "content": "Submit" } } } ``` ## Project Structure ### Recommended Structure ``` project-name/ ├── src/ │ ├── components/ │ │ └── ui-kit.pen # Component library + design tokens │ └── pages/ │ ├── desktop-home.pen # Desktop (1440px) │ ├── mobile-home.pen # Mobile (390px) │ └── tablet-home.pen # Tablet (768px) ├── build/ # Standard build output (gitignore) │ ├── desktop-home.pen # With injected components │ └── ... ├── scripts/ │ └── build.py # Build script ├── pencil.config.json # Build configuration ├── tPOS.pen # Monolithic build output └── USAGE_GUIDE.md # Documentation ``` ### Atomic Design Structure For enterprise-scale UI Kits: ``` project-name/ ├── src/ │ ├── foundations/ │ │ └── design-tokens.pen # Design system variables │ ├── atoms/ # Smallest building blocks │ │ ├── buttons.pen │ │ ├── inputs.pen │ │ └── badges.pen │ ├── molecules/ # Combinations of atoms │ │ ├── form-fields.pen │ │ └── search-bar.pen │ ├── organisms/ # Complex UI sections │ │ ├── cards.pen │ │ └── navigation.pen │ └── pages/ # Complete pages │ ├── desktop-home.pen │ └── mobile-home.pen ├── build/ ├── scripts/ │ └── build.py ├── pencil.config.json └── tPOS.pen ``` ## Build Commands Reference ### Basic Commands ```bash # Help python3 scripts/build.py --help # Monolithic build python3 scripts/build.py -m # Standard build python3 scripts/build.py # Custom output python3 scripts/build.py -m --output custom.pen ``` ### Validation Commands ```bash # Validate JSON jq empty tPOS.pen && echo "✅ Valid JSON" # Check structure jq '{version, childrenCount: (.children | length)}' tPOS.pen # List frame names jq '[.children[].name]' tPOS.pen # Extract design tokens jq '.variables' tPOS.pen # Count components jq '[.children[0].children[] | select(.reusable == true)] | length' tPOS.pen ``` ## Workflows ### Atomic Design Workflow (Recommended) ```markdown 1. Layout & Components: - Update atoms/molecules in `src/atoms/` etc. - Run `python3 scripts/build.py --library` to update component library 2. Page Design: - Open `src/pages/desktop-home.pen` - Use components from the generated `src/components/ui-kit.pen` 3. Production Build: - Run `python3 scripts/build.py -m` - Output `tPOS.pen` contains everything ``` ### Legacy/Mockup Workflow ```markdown 1. Design in modular files: - Edit components in `src/components/ui-kit.pen` - Edit pages in `src/pages/*.pen` - Use `component_ref` to reference components 2. Build for testing: python3 scripts/build.py # Standard build # Open files in build/ to test 3. Build for production: python3 scripts/build.py -m # Monolithic # Share tPOS.pen with team/clients 4. Convert to code: # Use agent to convert tPOS.pen to HTML/CSS/React/Blazor ``` ### Component Library Updates ```markdown 1. Update component in ui-kit.pen 2. Save file 3. Rebuild all pages: python3 scripts/build.py -m 4. Test in Pencil app 5. If OK, commit changes ``` ### Adding New Components ```markdown 1. Open ui-kit.pen in Pencil 2. Create component with: - Unique ID - Descriptive name (e.g., "Button/Secondary") - `reusable: true` 3. Save file 4. Use in pages with component_ref: { "type": "component_ref", "component": "ui-kit#Button/Secondary" } 5. Build and test ``` ## Best Practices ### 1. Component References ```markdown ✅ DO: Use component_ref in source files ❌ DON'T: Hardcode components into pages ✅ DO: Keep components in ui-kit.pen ❌ DON'T: Duplicate components across files ``` ### 2. Build Before Testing ```markdown ✅ DO: Build before opening in Pencil ❌ DON'T: Open source files directly # Correct workflow: python3 scripts/build.py -m open tPOS.pen # Component refs will be resolved ✅ ``` ### 3. Version Control ```markdown ✅ DO: Commit source files (src/) ✅ DO: Gitignore build output (build/, tPOS.pen) # .gitignore build/ tPOS.pen *.pen.backup ``` ### 4. Validation ```markdown ✅ DO: Validate after build ❌ DON'T: Skip validation # Always validate: jq empty tPOS.pen && echo "✅ Valid" ``` ### 5. Configuration Consistency ```markdown ✅ DO: Keep pencil.config.json in sync ❌ DON'T: Hardcode paths in scripts # Update config when structure changes ``` ## Troubleshooting ### Build Script Errors **Error: Component library not found** ```bash # Check config cat pencil.config.json # Verify path exists ls -la src/components/ui-kit.pen ``` **Error: Invalid JSON in source file** ```bash # Validate source files jq empty src/components/ui-kit.pen jq empty src/pages/desktop-home.pen ``` **Error: Component ref not resolved** ```bash # Check component exists in library jq '[.children[0].children[] | select(.name == "Button/Primary")]' src/components/ui-kit.pen # Check component path syntax # Correct: "ui-kit#Button/Primary" # Wrong: "Button/Primary" (missing library prefix) ``` ### Output Validation ```bash # Check file was created ls -lh tPOS.pen # Validate JSON structure jq . tPOS.pen > /dev/null && echo "✅ Valid" # Check version jq '.version' tPOS.pen # Count frames jq '.children | length' tPOS.pen # List frame names jq '[.children[].name]' tPOS.pen ``` ## Resources - Back to [Pencil Design Skill](../SKILL.md) - [File Format Reference](./FILE_FORMAT.md) - [Atomic Design Reference](./ATOMIC_DESIGN.md) - [Code Conversion Reference](./CODE_CONVERSION.md) - [Workflow: /pencil-build](../../../.agent/workflows/pencil-build.md)