feat: Add component library build functionality to support atomic design workflow and update related documentation.
This commit is contained in:
@@ -308,6 +308,7 @@ open tPOS.pen
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `python3 scripts/build.py --library` | Build Component Lib |
|
||||
| `python3 scripts/build.py -m` | Monolithic build |
|
||||
| `python3 scripts/build.py` | Standard build |
|
||||
| `python3 scripts/build.py -m --output name.pen` | Custom output |
|
||||
|
||||
@@ -299,110 +299,22 @@ mkdir -p src/foundations src/atoms src/molecules src/organisms src/pages
|
||||
|
||||
### Step 3: Extract to Atomic Files
|
||||
|
||||
```python
|
||||
import json
|
||||
import os
|
||||
The `pencil-design` skill now includes an automated extraction tool.
|
||||
|
||||
# Read monolithic UI Kit
|
||||
with open('tPOS-ui-kit.pen', 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
variables = data['variables']
|
||||
main_page = data['children'][0] # Component Library page
|
||||
sections = main_page['children'] # All sections
|
||||
|
||||
# 1. Extract design tokens
|
||||
tokens_file = {
|
||||
'version': '2.6',
|
||||
'children': [{
|
||||
'type': 'frame',
|
||||
'name': 'Design Tokens Reference',
|
||||
'layout': 'vertical',
|
||||
'gap': 20,
|
||||
'children': [
|
||||
{
|
||||
'type': 'text',
|
||||
'name': 'Title',
|
||||
'content': 'Design System Tokens',
|
||||
'fontSize': 32,
|
||||
'fill': '$text-primary'
|
||||
}
|
||||
]
|
||||
}],
|
||||
'variables': variables
|
||||
}
|
||||
|
||||
with open('src/foundations/design-tokens.pen', 'w') as f:
|
||||
json.dump(tokens_file, f, indent=2)
|
||||
|
||||
# 2. Categorize components by atomic level
|
||||
atomic_mapping = {
|
||||
'atoms': {
|
||||
'patterns': ['button', 'badge', 'input', 'icon', 'typography', 'chip'],
|
||||
'components': []
|
||||
},
|
||||
'molecules': {
|
||||
'patterns': ['formfield', 'searchbar', 'cardheader'],
|
||||
'components': []
|
||||
},
|
||||
'organisms': {
|
||||
'patterns': ['card', 'navigation', 'header', 'footer', 'form'],
|
||||
'components': []
|
||||
}
|
||||
}
|
||||
|
||||
# Categorize each section
|
||||
for section in sections:
|
||||
section_name = section.get('name', '').lower()
|
||||
|
||||
# Determine atomic level
|
||||
matched = False
|
||||
for level, config in atomic_mapping.items():
|
||||
for pattern in config['patterns']:
|
||||
if pattern in section_name:
|
||||
config['components'].append(section)
|
||||
matched = True
|
||||
break
|
||||
if matched:
|
||||
break
|
||||
|
||||
# 3. Generate atomic files
|
||||
for level, config in atomic_mapping.items():
|
||||
if not config['components']:
|
||||
continue
|
||||
|
||||
# Group by component type
|
||||
grouped = {}
|
||||
for component in config['components']:
|
||||
name = component.get('name', '')
|
||||
# Extract type (e.g., "Buttons & Actions Section" -> "buttons")
|
||||
comp_type = name.lower().split()[0].replace('&', '').strip()
|
||||
|
||||
if comp_type not in grouped:
|
||||
grouped[comp_type] = []
|
||||
grouped[comp_type].append(component)
|
||||
|
||||
# Create file for each type
|
||||
for comp_type, components in grouped.items():
|
||||
output_file = {
|
||||
'version': '2.6',
|
||||
'children': components,
|
||||
'variables': variables
|
||||
}
|
||||
|
||||
filepath = f'src/{level}/{comp_type}.pen'
|
||||
with open(filepath, 'w') as f:
|
||||
json.dump(output_file, f, indent=2)
|
||||
|
||||
print(f'✅ Created {filepath} with {len(components)} sections')
|
||||
|
||||
print(f'\n🎉 Atomic Design structure created!')
|
||||
print(f'📁 Foundations: src/foundations/design-tokens.pen')
|
||||
print(f'⚛️ Atoms: {len(atomic_mapping["atoms"]["components"])} files')
|
||||
print(f'🧬 Molecules: {len(atomic_mapping["molecules"]["components"])} files')
|
||||
print(f'🦠 Organisms: {len(atomic_mapping["organisms"]["components"])} files')
|
||||
```bash
|
||||
# Run the extraction script
|
||||
python3 scripts/extract-atomic.py
|
||||
```
|
||||
|
||||
This script will:
|
||||
1. Read `src/components/tPOS-ui-kit.pen` (or configured library)
|
||||
2. Extract design tokens to `src/foundations/design-tokens.pen`
|
||||
3. Categorize components into `src/atoms`, `src/molecules`, `src/organisms`
|
||||
4. Generate showcase wrappers for each file
|
||||
|
||||
**Customization**:
|
||||
Edit `scripts/extract-atomic.py` to adjust categorization logic (regex patterns) if your component names differ.
|
||||
|
||||
### Step 4: Create Showcase Files
|
||||
|
||||
```python
|
||||
@@ -528,11 +440,11 @@ src/
|
||||
## Workflow
|
||||
|
||||
1. **Update Foundation**: Edit `design-tokens.pen` for colors/typography changes
|
||||
2. **Sync Tokens**: Copy `variables` object to all modified component files
|
||||
3. **Build Components**: Edit atomic files with proper naming (Atom/Button/Primary/Default)
|
||||
2. **Build Components**: Edit atomic files with proper naming (Atom/Button/Primary/Default)
|
||||
3. **Update Library**: Run `python3 scripts/build.py --library` to compile atoms
|
||||
4. **Compose Up**: Molecules use atoms, organisms use molecules
|
||||
5. **Test in Pages**: Reference components in page files
|
||||
6. **Build**: Run `python3 scripts/build.py -m` for monolithic output
|
||||
5. **Test in Pages**: Reference components in page files (from compiled library)
|
||||
6. **Build Monolith**: Run `python3 scripts/build.py -m` for production output
|
||||
|
||||
## Component Naming
|
||||
|
||||
|
||||
@@ -86,6 +86,21 @@ python3 scripts/build.py -m --output myDesign.pen
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
@@ -262,7 +277,23 @@ jq '[.children[0].children[] | select(.reusable == true)] | length' tPOS.pen
|
||||
|
||||
## Workflows
|
||||
|
||||
### Development Cycle
|
||||
### 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:
|
||||
|
||||
@@ -226,7 +226,18 @@ Edit `pencil.config.json`:
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Edit Components**: Open individual files in `src/atoms/...`, `src/molecules/...`, etc.
|
||||
2. **Build**: Run `python3 scripts/build.py -m`. The build system automatically scans these directories.
|
||||
3. **Migrate**: Use `python3 scripts/extract-atomic.py` to convert legacy libraries.
|
||||
1. **Edit Atomic Components**: Update files in `src/atoms/...`, `src/molecules/...`, etc.
|
||||
2. **Build Library**: Run `python3 scripts/build.py --library`. This compiles all atomic components into `src/components/tPOS-ui-kit.pen`.
|
||||
3. **Design Pages**: Open your page files (e.g., `src/pages/desktop.pen`) in Pencil. They reference components from `tPOS-ui-kit.pen`.
|
||||
4. **Build Monolith**: Run `python3 scripts/build.py --monolithic`. This merges pages and the library into the final `tPOS.pen` for sharing/verification.
|
||||
|
||||
### Build Commands
|
||||
|
||||
```bash
|
||||
# Step 2: Build Component Library
|
||||
python3 scripts/build.py --library
|
||||
|
||||
# Step 4: Build Monolithic File
|
||||
python3 scripts/build.py --monolithic
|
||||
```
|
||||
```
|
||||
|
||||
@@ -306,6 +306,94 @@ class PencilBuilder:
|
||||
print(f"📊 Total frames: {len(monolithic['children'])}")
|
||||
print(f"🎨 Design tokens: {len(variables)}")
|
||||
|
||||
def build_library(self, output_path: str = None) -> None:
|
||||
"""Build merged component library from Atomic Design files"""
|
||||
print("🔨 Building Component Library")
|
||||
print("=" * 50)
|
||||
|
||||
components = []
|
||||
variables = {}
|
||||
|
||||
# 1. Load Variables from Foundations
|
||||
src_dir = self.project_root / self.config['sourceDir']
|
||||
tokens_path = src_dir / 'foundations/design-tokens.pen'
|
||||
|
||||
if tokens_path.exists():
|
||||
with open(tokens_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
variables = data.get('variables', {})
|
||||
print(f"✓ Loaded design tokens from {tokens_path.name}")
|
||||
else:
|
||||
print(f"⚠️ Design tokens not found at {tokens_path}")
|
||||
|
||||
# 2. Collect Components from Atomic Dirs
|
||||
atomic_dirs = ['atoms', 'molecules', 'organisms']
|
||||
|
||||
total_found = 0
|
||||
for atom_dir in atomic_dirs:
|
||||
dir_path = src_dir / atom_dir
|
||||
if not dir_path.exists():
|
||||
continue
|
||||
|
||||
print(f"Scanning {atom_dir}...")
|
||||
# Sorting ensures deterministic order
|
||||
for pen_file in sorted(dir_path.glob('*.pen')):
|
||||
with open(pen_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Extract children (components/showcases)
|
||||
file_children = data.get('children', [])
|
||||
|
||||
# If the file contains a "Showcase" frame wrapping everything,
|
||||
# we ideally want to extract the inner reusable components if we want a clean library.
|
||||
# BUT, users might want the showcase organization in the library too.
|
||||
# Let's flatten showcases for the library so components are cleaner?
|
||||
# Actually, keeping showcases/groups is better for Pencil's sidebar organization.
|
||||
|
||||
# Update: Ensure all top-level children have unique IDs if possible?
|
||||
# Pencil IDs are UUIDs mostly, should be fine.
|
||||
|
||||
components.extend(file_children)
|
||||
total_found += len(file_children)
|
||||
print(f" ✓ Added contents of {pen_file.name}")
|
||||
|
||||
# 3. Create Library File
|
||||
library_file = {
|
||||
"version": "2.6",
|
||||
"children": [
|
||||
{
|
||||
"type": "frame",
|
||||
"name": "aPOS Component Library", # Main page name
|
||||
"width": 1440,
|
||||
"fill": "$bg-page",
|
||||
"layout": "vertical",
|
||||
"gap": 40,
|
||||
"padding": 40,
|
||||
"children": components
|
||||
}
|
||||
],
|
||||
"variables": variables
|
||||
}
|
||||
|
||||
# Determine output path
|
||||
if output_path:
|
||||
out_file = self.project_root / output_path
|
||||
else:
|
||||
out_file = self.project_root / self.config['componentLibrary']
|
||||
|
||||
# Ensure directory exists
|
||||
out_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(out_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(library_file, f, indent=2)
|
||||
|
||||
print()
|
||||
print("=" * 50)
|
||||
print(f"✅ Library build complete!")
|
||||
print(f"📄 Output: {out_file.absolute()}")
|
||||
print(f"🧩 Components: {total_found}")
|
||||
print(f"🎨 Tokens: {len(variables)}")
|
||||
|
||||
def build_all(self) -> None:
|
||||
"""Build all pages"""
|
||||
print("🔨 Pencil Design Build System")
|
||||
@@ -361,15 +449,11 @@ def main():
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Build individual pages with component linking
|
||||
python build.py
|
||||
# 1. Build Component Library (from Atomic Source)
|
||||
python build.py --library
|
||||
|
||||
# Build monolithic file (merge all pages)
|
||||
# 2. Build Monolithic File (Pages + Library)
|
||||
python build.py --monolithic
|
||||
python build.py --mode monolithic
|
||||
|
||||
# Build monolithic with custom output name
|
||||
python build.py --monolithic --output myDesign.pen
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -379,17 +463,22 @@ Examples:
|
||||
help='Build monolithic file (merge all pages and component library)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--library', '--lib', '-l',
|
||||
action='store_true',
|
||||
help='Build component library file from Atomic Design sources'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--mode',
|
||||
choices=['standard', 'monolithic'],
|
||||
choices=['standard', 'monolithic', 'library'],
|
||||
default='standard',
|
||||
help='Build mode: standard (separate pages) or monolithic (merged file)'
|
||||
help='Build mode'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--output', '-o',
|
||||
default='tPOS.pen',
|
||||
help='Output filename for monolithic build (default: tPOS.pen)'
|
||||
help='Output filename'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -397,8 +486,12 @@ Examples:
|
||||
builder = PencilBuilder()
|
||||
|
||||
# Determine build mode
|
||||
if args.monolithic or args.mode == 'monolithic':
|
||||
builder.build_monolithic(output_filename=args.output)
|
||||
if args.library or args.mode == 'library':
|
||||
builder.build_library(output_path=args.output)
|
||||
elif args.monolithic or args.mode == 'monolithic':
|
||||
# Default output for monolithic is tPOS.pen, but allow override
|
||||
output = args.output if args.output else "tPOS.pen"
|
||||
builder.build_monolithic(output_filename=output)
|
||||
else:
|
||||
builder.build_all()
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user