Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
93
examples/01_basic_ingestion.py
Executable file
93
examples/01_basic_ingestion.py
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example 1: Basic Token Ingestion
|
||||
|
||||
Shows how to ingest design tokens from different sources.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("EXAMPLE 1: Basic Token Ingestion")
|
||||
print("=" * 60)
|
||||
|
||||
# 1. CSS Token Ingestion
|
||||
print("\n1. CSS Custom Properties")
|
||||
print("-" * 40)
|
||||
from tools.ingest.css import CSSTokenSource
|
||||
|
||||
css_content = """
|
||||
:root {
|
||||
--color-primary: #3B82F6;
|
||||
--color-secondary: #10B981;
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
}
|
||||
"""
|
||||
|
||||
css_parser = CSSTokenSource()
|
||||
css_result = await css_parser.extract(css_content)
|
||||
|
||||
print(f"✅ Extracted {len(css_result.tokens)} CSS tokens:")
|
||||
for token in css_result.tokens[:3]:
|
||||
print(f" {token.name} = {token.value} ({token.type.value})")
|
||||
|
||||
# 2. SCSS Token Ingestion
|
||||
print("\n2. SCSS Variables")
|
||||
print("-" * 40)
|
||||
from tools.ingest.scss import SCSSTokenSource
|
||||
|
||||
scss_content = """
|
||||
$primary-color: #3B82F6;
|
||||
$secondary-color: #10B981;
|
||||
$font-family-sans: 'Inter', sans-serif;
|
||||
$font-size-base: 16px;
|
||||
"""
|
||||
|
||||
scss_parser = SCSSTokenSource()
|
||||
scss_result = await scss_parser.extract(scss_content)
|
||||
|
||||
print(f"✅ Extracted {len(scss_result.tokens)} SCSS tokens:")
|
||||
for token in scss_result.tokens[:3]:
|
||||
print(f" {token.name} = {token.value}")
|
||||
|
||||
# 3. JSON Tokens (W3C Format)
|
||||
print("\n3. JSON Design Tokens (W3C)")
|
||||
print("-" * 40)
|
||||
from tools.ingest.json_tokens import JSONTokenSource
|
||||
import json
|
||||
|
||||
json_content = {
|
||||
"color": {
|
||||
"primary": {
|
||||
"500": {"value": "#3B82F6", "type": "color"},
|
||||
"600": {"value": "#2563EB", "type": "color"}
|
||||
}
|
||||
},
|
||||
"spacing": {
|
||||
"md": {"value": "16px", "type": "dimension"}
|
||||
}
|
||||
}
|
||||
|
||||
json_parser = JSONTokenSource()
|
||||
json_result = await json_parser.extract(json.dumps(json_content))
|
||||
|
||||
print(f"✅ Extracted {len(json_result.tokens)} JSON tokens:")
|
||||
for token in json_result.tokens:
|
||||
print(f" {token.name} = {token.value} ({token.type.value})")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Total tokens extracted: {len(css_result.tokens) + len(scss_result.tokens) + len(json_result.tokens)}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
87
examples/02_token_merge.py
Executable file
87
examples/02_token_merge.py
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example 2: Token Merging with Conflict Resolution
|
||||
|
||||
Shows how to merge tokens from multiple sources using different strategies.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("EXAMPLE 2: Token Merging & Conflict Resolution")
|
||||
print("=" * 60)
|
||||
|
||||
from tools.ingest.merge import TokenMerger, MergeStrategy
|
||||
from tools.ingest.base import TokenCollection, DesignToken, TokenType
|
||||
|
||||
# Create tokens from different sources
|
||||
print("\n1. Creating token collections from different sources...")
|
||||
print("-" * 60)
|
||||
|
||||
css_tokens = TokenCollection([
|
||||
DesignToken(name="color.primary", value="#FF0000", type=TokenType.COLOR, source="css"),
|
||||
DesignToken(name="color.secondary", value="#00FF00", type=TokenType.COLOR, source="css"),
|
||||
DesignToken(name="spacing.md", value="16px", type=TokenType.SPACING, source="css"),
|
||||
])
|
||||
|
||||
figma_tokens = TokenCollection([
|
||||
DesignToken(name="color.primary", value="#3B82F6", type=TokenType.COLOR, source="figma"),
|
||||
DesignToken(name="color.accent", value="#F59E0B", type=TokenType.COLOR, source="figma"),
|
||||
])
|
||||
|
||||
tailwind_tokens = TokenCollection([
|
||||
DesignToken(name="color.primary", value="#2563EB", type=TokenType.COLOR, source="tailwind"),
|
||||
DesignToken(name="spacing.lg", value="24px", type=TokenType.SPACING, source="tailwind"),
|
||||
])
|
||||
|
||||
print(f"CSS: {len(css_tokens.tokens)} tokens")
|
||||
print(f"Figma: {len(figma_tokens.tokens)} tokens")
|
||||
print(f"Tailwind: {len(tailwind_tokens.tokens)} tokens")
|
||||
|
||||
# Test different merge strategies
|
||||
strategies = [
|
||||
MergeStrategy.FIRST,
|
||||
MergeStrategy.LAST,
|
||||
MergeStrategy.PREFER_FIGMA,
|
||||
]
|
||||
|
||||
for strategy in strategies:
|
||||
print(f"\n2. Merge Strategy: {strategy.value.upper()}")
|
||||
print("-" * 60)
|
||||
|
||||
merger = TokenMerger(strategy=strategy)
|
||||
result = merger.merge([css_tokens, figma_tokens, tailwind_tokens])
|
||||
|
||||
print(f"✅ Merged: {len(result.collection.tokens)} tokens")
|
||||
print(f"⚠️ Conflicts: {len(result.conflicts)}")
|
||||
|
||||
if result.conflicts:
|
||||
print("\nConflict Resolutions:")
|
||||
for conflict in result.conflicts:
|
||||
print(f" • {conflict.token_name}:")
|
||||
print(f" Existing: {conflict.existing.value} (from {conflict.existing.source})")
|
||||
print(f" Incoming: {conflict.incoming.value} (from {conflict.incoming.source})")
|
||||
print(f" ✓ Chose: {conflict.resolved_token.value} (from {conflict.resolved_token.source})")
|
||||
|
||||
# Show final token values
|
||||
print("\nFinal Tokens:")
|
||||
for token in result.collection.tokens:
|
||||
print(f" {token.name}: {token.value} (source: {token.source})")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💡 Key Takeaways:")
|
||||
print(" • FIRST: Keeps first occurrence, ignores later sources")
|
||||
print(" • LAST: Override with latest source")
|
||||
print(" • PREFER_FIGMA: Prioritizes Figma as source of truth")
|
||||
print(" • PREFER_CODE: Prioritizes code sources (CSS, SCSS)")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
97
examples/03_project_analysis.py
Executable file
97
examples/03_project_analysis.py
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example 3: Project Analysis & Quick Wins
|
||||
|
||||
Shows how to analyze a React project and identify improvement opportunities.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("EXAMPLE 3: Project Analysis & Quick Wins")
|
||||
print("=" * 60)
|
||||
|
||||
# Use current directory as example project
|
||||
project_path = "."
|
||||
|
||||
# 1. Scan Project
|
||||
print("\n1. Scanning Project...")
|
||||
print("-" * 60)
|
||||
from tools.analyze.scanner import ProjectScanner
|
||||
|
||||
scanner = ProjectScanner(project_path)
|
||||
analysis = await scanner.scan()
|
||||
|
||||
print(f"✅ Project scanned successfully")
|
||||
print(f"\nProject Details:")
|
||||
print(f" Framework: {analysis.framework}")
|
||||
print(f" Styling: {analysis.styling_approach}")
|
||||
print(f" Package Manager: {analysis.package_manager}")
|
||||
print(f" Has TypeScript: {analysis.has_typescript}")
|
||||
print(f" Components Found: {len(analysis.components)}")
|
||||
|
||||
# 2. Find Quick Wins
|
||||
print("\n2. Finding Quick Wins...")
|
||||
print("-" * 60)
|
||||
from tools.analyze.quick_wins import QuickWinFinder
|
||||
|
||||
finder = QuickWinFinder(project_path)
|
||||
wins = await finder.find_all()
|
||||
|
||||
print(f"✅ Found {len(wins.opportunities)} improvement opportunities\n")
|
||||
|
||||
# Group by type
|
||||
by_type = {}
|
||||
for win in wins.opportunities:
|
||||
win_type = win.type.value
|
||||
if win_type not in by_type:
|
||||
by_type[win_type] = []
|
||||
by_type[win_type].append(win)
|
||||
|
||||
print("Breakdown by Type:")
|
||||
for win_type, items in sorted(by_type.items()):
|
||||
print(f" {win_type}: {len(items)} opportunities")
|
||||
|
||||
# 3. Show Top Priorities (High Impact, Low Effort)
|
||||
print("\n3. Top Priorities (High ROI)")
|
||||
print("-" * 60)
|
||||
|
||||
high_roi = [
|
||||
w for w in wins.opportunities
|
||||
if w.impact.value == "high" and w.effort.value in ["low", "medium"]
|
||||
]
|
||||
|
||||
if high_roi:
|
||||
for i, win in enumerate(high_roi[:5], 1):
|
||||
print(f"\n{i}. {win.title}")
|
||||
print(f" Type: {win.type.value}")
|
||||
print(f" Impact: {win.impact.value} | Effort: {win.effort.value}")
|
||||
print(f" Description: {win.description}")
|
||||
if win.suggested_fix:
|
||||
print(f" Fix: {win.suggested_fix}")
|
||||
else:
|
||||
print(" No high-ROI opportunities found (project is well-optimized!)")
|
||||
|
||||
# 4. Generate Report
|
||||
print("\n4. Generating Report...")
|
||||
print("-" * 60)
|
||||
|
||||
report = await finder.generate_report()
|
||||
print(report[:500] + "..." if len(report) > 500 else report)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("💡 Next Steps:")
|
||||
print(" 1. Review high-impact, low-effort wins")
|
||||
print(" 2. Create backlog tickets for improvements")
|
||||
print(" 3. Use MCP tools for automated fixes")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
113
examples/README.md
Normal file
113
examples/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# DSS Code Examples
|
||||
|
||||
Practical examples showing how to use DSS core functionality.
|
||||
|
||||
## Running the Examples
|
||||
|
||||
```bash
|
||||
# Activate virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# Run any example
|
||||
python examples/01_basic_ingestion.py
|
||||
python examples/02_token_merge.py
|
||||
python examples/03_project_analysis.py
|
||||
```
|
||||
|
||||
## Example List
|
||||
|
||||
### 01_basic_ingestion.py
|
||||
**Topic**: Token Ingestion from Multiple Sources
|
||||
|
||||
Shows how to extract design tokens from:
|
||||
- CSS custom properties (`:root { --var: value; }`)
|
||||
- SCSS variables (`$var: value;`)
|
||||
- JSON tokens (W3C Design Tokens format)
|
||||
|
||||
**Output**: Demonstrates token extraction with counts and preview
|
||||
|
||||
---
|
||||
|
||||
### 02_token_merge.py
|
||||
**Topic**: Merging Tokens with Conflict Resolution
|
||||
|
||||
Shows how to:
|
||||
- Create token collections from different sources
|
||||
- Merge tokens using different strategies (FIRST, LAST, PREFER_FIGMA)
|
||||
- Resolve conflicts between sources
|
||||
- Understand which strategy to use when
|
||||
|
||||
**Output**: Side-by-side comparison of merge strategies and conflict resolution
|
||||
|
||||
---
|
||||
|
||||
### 03_project_analysis.py
|
||||
**Topic**: Project Analysis & Quick Wins
|
||||
|
||||
Shows how to:
|
||||
- Scan a project for framework and styling detection
|
||||
- Find improvement opportunities (quick wins)
|
||||
- Prioritize changes by impact vs effort
|
||||
- Generate improvement reports
|
||||
|
||||
**Output**: Project stats, quick win recommendations, prioritized task list
|
||||
|
||||
---
|
||||
|
||||
## More Examples
|
||||
|
||||
Check the [QUICKSTART.md](../docs/QUICKSTART.md) for:
|
||||
- Full workflow examples (Figma → DSS → Storybook)
|
||||
- REST API usage
|
||||
- MCP tool integration
|
||||
- Production deployment patterns
|
||||
|
||||
## Adding Your Own Examples
|
||||
|
||||
1. Create a new file: `examples/XX_your_example.py`
|
||||
2. Add docstring explaining the example
|
||||
3. Use `async def main()` pattern
|
||||
4. Add to this README
|
||||
5. Submit PR!
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Basic Example Structure
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Brief description of what this example demonstrates"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add project to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("EXAMPLE TITLE")
|
||||
print("=" * 60)
|
||||
|
||||
# Your code here
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```python
|
||||
try:
|
||||
result = await some_operation()
|
||||
print(f"✅ Success: {result}")
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
```
|
||||
|
||||
## Need Help?
|
||||
|
||||
- Read [QUICKSTART.md](../docs/QUICKSTART.md) for detailed guides
|
||||
- Check [ARCHITECTURE.md](../docs/ARCHITECTURE.md) for system design
|
||||
- Review [PROJECT_MEMORY.md](../PROJECT_MEMORY.md) for module inventory
|
||||
613
examples/merge_cases.py
Executable file
613
examples/merge_cases.py
Executable file
@@ -0,0 +1,613 @@
|
||||
"""
|
||||
DSS Merge Case Examples
|
||||
|
||||
Demonstrates how DSS handles multi-source token ingestion and translation
|
||||
to the canonical DSS structure. Uses HeroUI and shadcn as examples.
|
||||
|
||||
Key Principle: DSS is MONOLITHIC. External systems translate TO us.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent to path for imports
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
|
||||
|
||||
from ingest import (
|
||||
DesignToken, TokenCollection, TokenMerger, MergeStrategy,
|
||||
CSSTokenSource, JSONTokenSource
|
||||
)
|
||||
from ingest.base import TokenType, TokenCategory
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CASE 1: HeroUI Token Structure
|
||||
# =============================================================================
|
||||
# HeroUI uses: --heroui-{category}-{scale}
|
||||
# Example: --heroui-primary-500, --heroui-content1, --heroui-radius-medium
|
||||
|
||||
HEROUI_TOKENS = """
|
||||
:root {
|
||||
/* HeroUI Color Scale System */
|
||||
--heroui-primary-50: #e6f1fe;
|
||||
--heroui-primary-100: #cce3fd;
|
||||
--heroui-primary-200: #99c7fb;
|
||||
--heroui-primary-300: #66aaf9;
|
||||
--heroui-primary-400: #338ef7;
|
||||
--heroui-primary-500: #006FEE;
|
||||
--heroui-primary-600: #005bc4;
|
||||
--heroui-primary-700: #004493;
|
||||
--heroui-primary-800: #002e62;
|
||||
--heroui-primary-900: #001731;
|
||||
|
||||
--heroui-secondary-500: #7828c8;
|
||||
--heroui-success-500: #17c964;
|
||||
--heroui-warning-500: #f5a524;
|
||||
--heroui-danger-500: #f31260;
|
||||
|
||||
/* HeroUI Content Layers */
|
||||
--heroui-content1: #ffffff;
|
||||
--heroui-content2: #f4f4f5;
|
||||
--heroui-content3: #e4e4e7;
|
||||
--heroui-content4: #d4d4d8;
|
||||
|
||||
/* HeroUI Layout Tokens */
|
||||
--heroui-radius-small: 8px;
|
||||
--heroui-radius-medium: 12px;
|
||||
--heroui-radius-large: 14px;
|
||||
|
||||
--heroui-shadow-small: 0px 0px 5px 0px rgba(0,0,0,0.02);
|
||||
--heroui-shadow-medium: 0px 0px 15px 0px rgba(0,0,0,0.03);
|
||||
--heroui-shadow-large: 0px 0px 30px 0px rgba(0,0,0,0.04);
|
||||
|
||||
--heroui-font-size-tiny: 0.75rem;
|
||||
--heroui-font-size-small: 0.875rem;
|
||||
--heroui-font-size-medium: 1rem;
|
||||
--heroui-font-size-large: 1.125rem;
|
||||
|
||||
--heroui-spacing-unit: 4px;
|
||||
}
|
||||
"""
|
||||
|
||||
# =============================================================================
|
||||
# CASE 2: shadcn Token Structure
|
||||
# =============================================================================
|
||||
# shadcn uses: --{semantic-name} with background/foreground pattern
|
||||
# Example: --primary, --primary-foreground, --card, --card-foreground
|
||||
|
||||
SHADCN_TOKENS = """
|
||||
:root {
|
||||
/* shadcn Semantic Colors (OKLCH format) */
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* shadcn Chart Colors */
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
}
|
||||
"""
|
||||
|
||||
# =============================================================================
|
||||
# CASE 3: Legacy Corporate CSS (typical client scenario)
|
||||
# =============================================================================
|
||||
# Legacy uses: various naming conventions, inconsistent structure
|
||||
|
||||
LEGACY_CORPORATE_TOKENS = """
|
||||
:root {
|
||||
/* Old brand colors - inconsistent naming */
|
||||
--brand-blue: #1E40AF;
|
||||
--brand-dark-blue: #1E3A8A;
|
||||
--brand-light: #DBEAFE;
|
||||
--brandAccent: #F59E0B; /* camelCase mixed in */
|
||||
|
||||
/* Old button colors */
|
||||
--btn-primary-bg: #1E40AF;
|
||||
--btn-primary-text: #FFFFFF;
|
||||
--btn-secondary-bg: #F3F4F6;
|
||||
|
||||
/* Inconsistent spacing */
|
||||
--space-xs: 4px;
|
||||
--space-sm: 8px;
|
||||
--spacing-md: 16px; /* Different prefix! */
|
||||
--SPACING_LG: 24px; /* SCREAMING_CASE! */
|
||||
|
||||
/* Typography chaos */
|
||||
--font-base: 16px;
|
||||
--fontSize-lg: 18px;
|
||||
--text-xl: 24px;
|
||||
|
||||
/* Random radius values */
|
||||
--rounded: 4px;
|
||||
--border-radius-md: 8px;
|
||||
--corners-lg: 12px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# DSS CANONICAL STRUCTURE (The Target)
|
||||
# =============================================================================
|
||||
# This is what ALL external systems translate TO. Never changes.
|
||||
|
||||
DSS_CANONICAL = {
|
||||
"colors": {
|
||||
"primary": {"50": None, "100": None, "200": None, "300": None, "400": None,
|
||||
"500": None, "600": None, "700": None, "800": None, "900": None},
|
||||
"secondary": {"500": None},
|
||||
"success": {"500": None},
|
||||
"warning": {"500": None},
|
||||
"danger": {"500": None},
|
||||
"neutral": {"50": None, "100": None, "200": None, "300": None, "400": None,
|
||||
"500": None, "600": None, "700": None, "800": None, "900": None},
|
||||
},
|
||||
"spacing": {
|
||||
"xs": None, "sm": None, "md": None, "lg": None, "xl": None, "2xl": None
|
||||
},
|
||||
"radius": {
|
||||
"sm": None, "md": None, "lg": None, "xl": None, "full": None
|
||||
},
|
||||
"shadows": {
|
||||
"sm": None, "md": None, "lg": None, "xl": None
|
||||
},
|
||||
"typography": {
|
||||
"fontSize": {"xs": None, "sm": None, "base": None, "lg": None, "xl": None}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TRANSLATION DICTIONARIES
|
||||
# =============================================================================
|
||||
|
||||
def create_heroui_translation():
|
||||
"""
|
||||
Translation dictionary: HeroUI → DSS Canonical
|
||||
|
||||
HeroUI uses numeric color scales (like DSS) but different naming.
|
||||
Mapping is mostly 1:1 with prefix removal.
|
||||
"""
|
||||
return {
|
||||
"$schema": "dss-translation-v1",
|
||||
"project": "heroui-integration",
|
||||
"source": "heroui",
|
||||
"mappings": {
|
||||
"tokens": {
|
||||
# Colors - direct scale mapping
|
||||
"--heroui-primary-50": "color.primary.50",
|
||||
"--heroui-primary-100": "color.primary.100",
|
||||
"--heroui-primary-200": "color.primary.200",
|
||||
"--heroui-primary-300": "color.primary.300",
|
||||
"--heroui-primary-400": "color.primary.400",
|
||||
"--heroui-primary-500": "color.primary.500",
|
||||
"--heroui-primary-600": "color.primary.600",
|
||||
"--heroui-primary-700": "color.primary.700",
|
||||
"--heroui-primary-800": "color.primary.800",
|
||||
"--heroui-primary-900": "color.primary.900",
|
||||
|
||||
"--heroui-secondary-500": "color.secondary.500",
|
||||
"--heroui-success-500": "color.success.500",
|
||||
"--heroui-warning-500": "color.warning.500",
|
||||
"--heroui-danger-500": "color.danger.500",
|
||||
|
||||
# Content layers → Neutral scale
|
||||
"--heroui-content1": "color.neutral.50",
|
||||
"--heroui-content2": "color.neutral.100",
|
||||
"--heroui-content3": "color.neutral.200",
|
||||
"--heroui-content4": "color.neutral.300",
|
||||
|
||||
# Layout tokens
|
||||
"--heroui-radius-small": "radius.sm",
|
||||
"--heroui-radius-medium": "radius.md",
|
||||
"--heroui-radius-large": "radius.lg",
|
||||
|
||||
"--heroui-shadow-small": "shadow.sm",
|
||||
"--heroui-shadow-medium": "shadow.md",
|
||||
"--heroui-shadow-large": "shadow.lg",
|
||||
|
||||
# Typography
|
||||
"--heroui-font-size-tiny": "typography.fontSize.xs",
|
||||
"--heroui-font-size-small": "typography.fontSize.sm",
|
||||
"--heroui-font-size-medium": "typography.fontSize.base",
|
||||
"--heroui-font-size-large": "typography.fontSize.lg",
|
||||
|
||||
# Spacing
|
||||
"--heroui-spacing-unit": "spacing.unit",
|
||||
},
|
||||
"components": {
|
||||
"Button": "Button", # HeroUI Button → DSS Button
|
||||
"Card": "Card",
|
||||
"Input": "Input",
|
||||
}
|
||||
},
|
||||
"custom_props": {
|
||||
# HeroUI-specific that don't map to DSS core
|
||||
# These go in isolated namespace
|
||||
},
|
||||
"notes": [
|
||||
"HeroUI uses numeric scales similar to DSS - easy mapping",
|
||||
"Content layers (1-4) map to neutral scale",
|
||||
"Component mapping is mostly 1:1"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def create_shadcn_translation():
|
||||
"""
|
||||
Translation dictionary: shadcn → DSS Canonical
|
||||
|
||||
shadcn uses semantic naming (primary, secondary, muted) without scales.
|
||||
We map to the 500 (default) value in DSS scales.
|
||||
|
||||
KEY DIFFERENCE: shadcn is HEADLESS - no numeric scales!
|
||||
"""
|
||||
return {
|
||||
"$schema": "dss-translation-v1",
|
||||
"project": "shadcn-integration",
|
||||
"source": "shadcn",
|
||||
"mappings": {
|
||||
"tokens": {
|
||||
# Semantic colors → DSS scale defaults
|
||||
"--background": "color.neutral.50",
|
||||
"--foreground": "color.neutral.900",
|
||||
|
||||
"--primary": "color.primary.500",
|
||||
"--primary-foreground": "color.primary.50",
|
||||
|
||||
"--secondary": "color.secondary.500",
|
||||
"--secondary-foreground": "color.secondary.50",
|
||||
|
||||
"--muted": "color.neutral.200",
|
||||
"--muted-foreground": "color.neutral.600",
|
||||
|
||||
"--accent": "color.accent.500",
|
||||
"--accent-foreground": "color.accent.50",
|
||||
|
||||
"--destructive": "color.danger.500",
|
||||
|
||||
"--card": "color.neutral.50",
|
||||
"--card-foreground": "color.neutral.900",
|
||||
|
||||
"--popover": "color.neutral.50",
|
||||
"--popover-foreground": "color.neutral.900",
|
||||
|
||||
"--border": "color.neutral.200",
|
||||
"--input": "color.neutral.200",
|
||||
"--ring": "color.primary.500",
|
||||
|
||||
# Layout
|
||||
"--radius": "radius.md",
|
||||
},
|
||||
"components": {
|
||||
# shadcn components → DSS components
|
||||
"Button": "Button",
|
||||
"Card": "Card",
|
||||
"Input": "Input",
|
||||
"Dialog": "Modal",
|
||||
"Popover": "Popover",
|
||||
}
|
||||
},
|
||||
"custom_props": {
|
||||
# shadcn-specific that don't exist in DSS
|
||||
"color.chart.1": "var(--chart-1)",
|
||||
"color.chart.2": "var(--chart-2)",
|
||||
"color.chart.3": "var(--chart-3)",
|
||||
"color.chart.4": "var(--chart-4)",
|
||||
"color.chart.5": "var(--chart-5)",
|
||||
"color.sidebar.primary": "var(--sidebar-primary)",
|
||||
},
|
||||
"notes": [
|
||||
"shadcn is HEADLESS - no numeric color scales",
|
||||
"Semantic names map to 500 (default) DSS values",
|
||||
"foreground variants map to contrast colors (50)",
|
||||
"Chart colors are shadcn-specific custom props"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def create_legacy_translation():
|
||||
"""
|
||||
Translation dictionary: Legacy Corporate → DSS Canonical
|
||||
|
||||
Messy legacy code with inconsistent naming needs careful mapping.
|
||||
"""
|
||||
return {
|
||||
"$schema": "dss-translation-v1",
|
||||
"project": "acme-corp-legacy",
|
||||
"source": "legacy-css",
|
||||
"mappings": {
|
||||
"tokens": {
|
||||
# Brand colors (inconsistent naming)
|
||||
"--brand-blue": "color.primary.500",
|
||||
"--brand-dark-blue": "color.primary.700",
|
||||
"--brand-light": "color.primary.100",
|
||||
"--brandAccent": "color.warning.500", # camelCase → DSS
|
||||
|
||||
# Button colors → Component tokens
|
||||
"--btn-primary-bg": "color.primary.500",
|
||||
"--btn-primary-text": "color.neutral.50",
|
||||
"--btn-secondary-bg": "color.neutral.100",
|
||||
|
||||
# Spacing chaos → DSS order
|
||||
"--space-xs": "spacing.xs",
|
||||
"--space-sm": "spacing.sm",
|
||||
"--spacing-md": "spacing.md",
|
||||
"--SPACING_LG": "spacing.lg",
|
||||
|
||||
# Typography normalization
|
||||
"--font-base": "typography.fontSize.base",
|
||||
"--fontSize-lg": "typography.fontSize.lg",
|
||||
"--text-xl": "typography.fontSize.xl",
|
||||
|
||||
# Radius normalization
|
||||
"--rounded": "radius.sm",
|
||||
"--border-radius-md": "radius.md",
|
||||
"--corners-lg": "radius.lg",
|
||||
},
|
||||
"components": {
|
||||
".btn-primary": "Button[variant=primary]",
|
||||
".btn-secondary": "Button[variant=secondary]",
|
||||
".card-wrapper": "Card",
|
||||
".input-field": "Input",
|
||||
}
|
||||
},
|
||||
"custom_props": {
|
||||
# ACME-specific branding that extends DSS
|
||||
"color.brand.acme.primary": "#1E40AF",
|
||||
"color.brand.acme.accent": "#F59E0B",
|
||||
},
|
||||
"validation_warnings": [
|
||||
"Inconsistent spacing prefixes detected",
|
||||
"Mixed case conventions found",
|
||||
"Some values may need manual review"
|
||||
],
|
||||
"notes": [
|
||||
"Legacy system had 4 different naming conventions",
|
||||
"All mapped to DSS canonical structure",
|
||||
"Brand-specific colors isolated in custom_props"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# MERGE DEMONSTRATIONS
|
||||
# =============================================================================
|
||||
|
||||
async def demonstrate_merge_heroui_shadcn():
|
||||
"""
|
||||
CASE: Merge HeroUI and shadcn tokens into DSS canonical structure.
|
||||
|
||||
This demonstrates how two different atomic structures become one.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("MERGE CASE: HeroUI + shadcn → DSS Canonical")
|
||||
print("="*70)
|
||||
|
||||
css_source = CSSTokenSource()
|
||||
|
||||
# Ingest both sources
|
||||
heroui_collection = await css_source.extract(HEROUI_TOKENS)
|
||||
shadcn_collection = await css_source.extract(SHADCN_TOKENS)
|
||||
|
||||
print(f"\n📥 HeroUI tokens extracted: {len(heroui_collection.tokens)}")
|
||||
print(f"📥 shadcn tokens extracted: {len(shadcn_collection.tokens)}")
|
||||
|
||||
# Merge with PREFER_SPECIFIC strategy
|
||||
# This prefers concrete values (hex) over references
|
||||
merger = TokenMerger(strategy=MergeStrategy.PREFER_SPECIFIC)
|
||||
result = merger.merge([heroui_collection, shadcn_collection])
|
||||
|
||||
print(f"\n🔀 Merged result: {result.stats['total_tokens']} tokens")
|
||||
print(f" New tokens: {result.stats['new_tokens']}")
|
||||
print(f" Conflicts resolved: {result.stats['conflicts_resolved']}")
|
||||
|
||||
# Show conflicts
|
||||
if result.conflicts:
|
||||
print(f"\n⚠️ Conflicts detected ({len(result.conflicts)}):")
|
||||
for conflict in result.conflicts[:5]:
|
||||
print(f" • {conflict.token_name}")
|
||||
print(f" HeroUI: {conflict.existing.value}")
|
||||
print(f" shadcn: {conflict.incoming.value}")
|
||||
print(f" Resolution: {conflict.resolution}")
|
||||
|
||||
# Demonstrate atomic structure difference
|
||||
print("\n" + "-"*70)
|
||||
print("ATOMIC STRUCTURE COMPARISON:")
|
||||
print("-"*70)
|
||||
|
||||
print("\n🎨 HeroUI (Numeric Scale System):")
|
||||
print(" Uses: --heroui-primary-{50-900}")
|
||||
print(" Pattern: Full color scales with 10 values each")
|
||||
print(" Granularity: HIGH - precise control over each shade")
|
||||
|
||||
print("\n🔲 shadcn (Semantic System):")
|
||||
print(" Uses: --primary, --primary-foreground")
|
||||
print(" Pattern: Semantic pairs (color + contrast)")
|
||||
print(" Granularity: LOW - headless, no scales")
|
||||
|
||||
print("\n📐 DSS Canonical (Target):")
|
||||
print(" Uses: color.primary.{50-900}")
|
||||
print(" Result: Unified scale with both sources merged")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def demonstrate_merge_with_legacy():
|
||||
"""
|
||||
CASE: Merge legacy corporate tokens with modern DSS structure.
|
||||
|
||||
Shows how messy legacy code gets normalized.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("MERGE CASE: Legacy Corporate → DSS Canonical")
|
||||
print("="*70)
|
||||
|
||||
css_source = CSSTokenSource()
|
||||
|
||||
# Ingest legacy tokens
|
||||
legacy_collection = await css_source.extract(LEGACY_CORPORATE_TOKENS)
|
||||
|
||||
print(f"\n📥 Legacy tokens extracted: {len(legacy_collection.tokens)}")
|
||||
|
||||
# Show the chaos
|
||||
print("\n🔴 LEGACY TOKEN NAMING CHAOS:")
|
||||
for token in legacy_collection.tokens[:8]:
|
||||
print(f" {token.original_name} → {token.normalize_name()}")
|
||||
|
||||
# Get translation dictionary
|
||||
translation = create_legacy_translation()
|
||||
|
||||
print("\n🔄 TRANSLATION DICTIONARY MAPPINGS:")
|
||||
for legacy, dss in list(translation["mappings"]["tokens"].items())[:6]:
|
||||
print(f" {legacy} → {dss}")
|
||||
|
||||
print("\n✅ RESULT: Unified DSS canonical tokens")
|
||||
print(" All naming conventions normalized")
|
||||
print(" Custom brand colors isolated in namespace")
|
||||
|
||||
return legacy_collection
|
||||
|
||||
|
||||
async def demonstrate_conflict_strategies():
|
||||
"""
|
||||
CASE: Show different merge strategies and their outcomes.
|
||||
"""
|
||||
print("\n" + "="*70)
|
||||
print("MERGE STRATEGIES COMPARISON")
|
||||
print("="*70)
|
||||
|
||||
css_source = CSSTokenSource()
|
||||
|
||||
# Create conflicting tokens
|
||||
source_a = """
|
||||
:root {
|
||||
--primary: #3B82F6;
|
||||
--spacing-md: 16px;
|
||||
}
|
||||
"""
|
||||
|
||||
source_b = """
|
||||
:root {
|
||||
--primary: #2563EB;
|
||||
--spacing-md: 20px;
|
||||
}
|
||||
"""
|
||||
|
||||
collection_a = await css_source.extract(source_a)
|
||||
collection_b = await css_source.extract(source_b)
|
||||
|
||||
strategies = [
|
||||
(MergeStrategy.FIRST, "Keep first occurrence"),
|
||||
(MergeStrategy.LAST, "Use last (override)"),
|
||||
(MergeStrategy.PREFER_SPECIFIC, "Prefer concrete values"),
|
||||
(MergeStrategy.MERGE_METADATA, "Merge metadata, use latest value"),
|
||||
]
|
||||
|
||||
for strategy, description in strategies:
|
||||
merger = TokenMerger(strategy=strategy)
|
||||
result = merger.merge([collection_a, collection_b])
|
||||
|
||||
primary_token = next(
|
||||
(t for t in result.collection.tokens if 'primary' in t.name.lower()),
|
||||
None
|
||||
)
|
||||
|
||||
print(f"\n📋 {strategy.value}: {description}")
|
||||
if primary_token:
|
||||
print(f" --primary result: {primary_token.value}")
|
||||
if result.conflicts:
|
||||
print(f" Resolution: {result.conflicts[0].resolution}")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run all merge demonstrations."""
|
||||
print("\n" + "="*70)
|
||||
print("DSS MERGE CASE EXAMPLES")
|
||||
print("Demonstrating Multi-Source Token Ingestion")
|
||||
print("="*70)
|
||||
|
||||
# Run demonstrations
|
||||
await demonstrate_merge_heroui_shadcn()
|
||||
await demonstrate_merge_with_legacy()
|
||||
await demonstrate_conflict_strategies()
|
||||
|
||||
# Summary
|
||||
print("\n" + "="*70)
|
||||
print("SUMMARY: ATOMIC STRUCTURE DIFFERENCES")
|
||||
print("="*70)
|
||||
|
||||
print("""
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ATOMIC STRUCTURE COMPARISON │
|
||||
├─────────────────┬──────────────────────┬────────────────────────────┤
|
||||
│ Feature │ HeroUI │ shadcn │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ Color System │ Numeric scales │ Semantic pairs │
|
||||
│ │ (50-900) │ (primary/foreground) │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ Granularity │ HIGH (10 shades) │ LOW (2 values) │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ Naming │ --heroui-{cat}-{n} │ --{semantic} │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ CSS Variables │ Prefixed │ Unprefixed │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ Theme Layers │ content1-4 │ card, popover, muted │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ Design Tokens │ Full W3C-like │ Minimal semantic │
|
||||
├─────────────────┼──────────────────────┼────────────────────────────┤
|
||||
│ DSS Mapping │ Direct 1:1 │ Expand to scales │
|
||||
│ │ (strip prefix) │ (500 = default) │
|
||||
└─────────────────┴──────────────────────┴────────────────────────────┘
|
||||
""")
|
||||
|
||||
print("""
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ DSS TRANSLATION APPROACH │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ HeroUI │ │ shadcn │
|
||||
│ --heroui- │ TRANSLATION │ --primary ──────┐ │
|
||||
│ primary-500 ────┼────── LAYER ────────┼→ color.primary.500 ←───────┤
|
||||
│ │ │ --primary-foreground ─────┤
|
||||
│ --heroui- │ │ ↓ │
|
||||
│ content1 ───────┼─────────────────────┼→ color.neutral.50 │
|
||||
│ │ │ │
|
||||
│ │ DSS CANONICAL │ │
|
||||
│ │ (IMMUTABLE) │ │
|
||||
│ │ │ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user