refactor: streamline UI design workflow and add extraction scripts

- Simplified UI design command workflows (batch-generate, explore-auto, generate, update)
- Enhanced style-extract and layout-extract with improved documentation
- Refactored imitate-auto for better usability
- Removed obsolete consolidate workflow
- Added extract-computed-styles.js and extract-layout-structure.js utilities
- Updated UI generation and instantiation scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-10-16 13:47:58 +08:00
parent d2c66135fb
commit e95be40c2b
12 changed files with 794 additions and 669 deletions

View File

@@ -0,0 +1,118 @@
/**
* Extract Computed Styles from DOM
*
* This script extracts real CSS computed styles from a webpage's DOM
* to provide accurate design tokens for UI replication.
*
* Usage: Execute this function via Chrome DevTools evaluate_script
*/
(() => {
/**
* Extract unique values from a set and sort them
*/
const uniqueSorted = (set) => {
return Array.from(set)
.filter(v => v && v !== 'none' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)')
.sort();
};
/**
* Parse rgb/rgba to OKLCH format (placeholder - returns original for now)
*/
const toOKLCH = (color) => {
// TODO: Implement actual RGB to OKLCH conversion
// For now, return the original color with a note
return `${color} /* TODO: Convert to OKLCH */`;
};
/**
* Extract only key styles from an element
*/
const extractKeyStyles = (element) => {
const s = window.getComputedStyle(element);
return {
color: s.color,
bg: s.backgroundColor,
borderRadius: s.borderRadius,
boxShadow: s.boxShadow,
fontSize: s.fontSize,
fontWeight: s.fontWeight,
padding: s.padding,
margin: s.margin
};
};
/**
* Main extraction function - extract all critical design tokens
*/
const extractDesignTokens = () => {
// Include all key UI elements
const selectors = [
'button', '.btn', '[role="button"]',
'input', 'textarea', 'select',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'.card', 'article', 'section',
'a', 'p', 'nav', 'header', 'footer'
];
// Collect all design tokens
const tokens = {
colors: new Set(),
borderRadii: new Set(),
shadows: new Set(),
fontSizes: new Set(),
fontWeights: new Set(),
spacing: new Set()
};
// Extract from all elements
selectors.forEach(selector => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
const s = extractKeyStyles(element);
// Collect all tokens (no limits)
if (s.color && s.color !== 'rgba(0, 0, 0, 0)') tokens.colors.add(s.color);
if (s.bg && s.bg !== 'rgba(0, 0, 0, 0)') tokens.colors.add(s.bg);
if (s.borderRadius && s.borderRadius !== '0px') tokens.borderRadii.add(s.borderRadius);
if (s.boxShadow && s.boxShadow !== 'none') tokens.shadows.add(s.boxShadow);
if (s.fontSize) tokens.fontSizes.add(s.fontSize);
if (s.fontWeight) tokens.fontWeights.add(s.fontWeight);
// Extract all spacing values
[s.padding, s.margin].forEach(val => {
if (val && val !== '0px') {
val.split(' ').forEach(v => {
if (v && v !== '0px') tokens.spacing.add(v);
});
}
});
});
} catch (e) {
console.warn(`Error: ${selector}`, e);
}
});
// Return all tokens (no element details to save context)
return {
metadata: {
extractedAt: new Date().toISOString(),
url: window.location.href,
method: 'computed-styles'
},
tokens: {
colors: uniqueSorted(tokens.colors),
borderRadii: uniqueSorted(tokens.borderRadii), // ALL radius values
shadows: uniqueSorted(tokens.shadows), // ALL shadows
fontSizes: uniqueSorted(tokens.fontSizes),
fontWeights: uniqueSorted(tokens.fontWeights),
spacing: uniqueSorted(tokens.spacing)
}
};
};
// Execute and return results
return extractDesignTokens();
})();

View File

@@ -0,0 +1,221 @@
/**
* Extract Layout Structure from DOM
*
* Extracts real layout information from DOM to provide accurate
* structural data for UI replication.
*
* Usage: Execute via Chrome DevTools evaluate_script
*/
(() => {
/**
* Get element's bounding box relative to viewport
*/
const getBounds = (element) => {
const rect = element.getBoundingClientRect();
return {
x: Math.round(rect.x),
y: Math.round(rect.y),
width: Math.round(rect.width),
height: Math.round(rect.height)
};
};
/**
* Extract layout properties from an element
*/
const extractLayoutProps = (element) => {
const s = window.getComputedStyle(element);
return {
// Core layout
display: s.display,
position: s.position,
// Flexbox
flexDirection: s.flexDirection,
justifyContent: s.justifyContent,
alignItems: s.alignItems,
flexWrap: s.flexWrap,
gap: s.gap,
// Grid
gridTemplateColumns: s.gridTemplateColumns,
gridTemplateRows: s.gridTemplateRows,
gridAutoFlow: s.gridAutoFlow,
// Dimensions
width: s.width,
height: s.height,
maxWidth: s.maxWidth,
minWidth: s.minWidth,
// Spacing
padding: s.padding,
margin: s.margin
};
};
/**
* Identify layout pattern for an element
*/
const identifyPattern = (props) => {
const { display, flexDirection, gridTemplateColumns } = props;
if (display === 'flex') {
if (flexDirection === 'column') return 'flex-column';
if (flexDirection === 'row') return 'flex-row';
return 'flex';
}
if (display === 'grid') {
const cols = gridTemplateColumns;
if (cols && cols !== 'none') {
const colCount = cols.split(' ').length;
return `grid-${colCount}col`;
}
return 'grid';
}
if (display === 'inline-flex') return 'inline-flex';
if (display === 'block') return 'block';
return display;
};
/**
* Build layout tree recursively
*/
const buildLayoutTree = (element, depth = 0, maxDepth = 3) => {
if (depth > maxDepth) return null;
const props = extractLayoutProps(element);
const bounds = getBounds(element);
const pattern = identifyPattern(props);
// Get semantic role
const tagName = element.tagName.toLowerCase();
const classes = Array.from(element.classList).slice(0, 3); // Max 3 classes
const role = element.getAttribute('role');
// Build node
const node = {
tag: tagName,
classes: classes,
role: role,
pattern: pattern,
bounds: bounds,
layout: {
display: props.display,
position: props.position
}
};
// Add flex/grid specific properties
if (props.display === 'flex' || props.display === 'inline-flex') {
node.layout.flexDirection = props.flexDirection;
node.layout.justifyContent = props.justifyContent;
node.layout.alignItems = props.alignItems;
node.layout.gap = props.gap;
}
if (props.display === 'grid') {
node.layout.gridTemplateColumns = props.gridTemplateColumns;
node.layout.gridTemplateRows = props.gridTemplateRows;
node.layout.gap = props.gap;
}
// Process children for container elements
if (props.display === 'flex' || props.display === 'grid' || props.display === 'block') {
const children = Array.from(element.children);
if (children.length > 0 && children.length < 50) { // Limit to 50 children
node.children = children
.map(child => buildLayoutTree(child, depth + 1, maxDepth))
.filter(child => child !== null);
}
}
return node;
};
/**
* Find main layout containers
*/
const findMainContainers = () => {
const selectors = [
'body > header',
'body > nav',
'body > main',
'body > footer',
'[role="banner"]',
'[role="navigation"]',
'[role="main"]',
'[role="contentinfo"]'
];
const containers = [];
selectors.forEach(selector => {
const element = document.querySelector(selector);
if (element) {
const tree = buildLayoutTree(element, 0, 3);
if (tree) {
containers.push(tree);
}
}
});
return containers;
};
/**
* Analyze layout patterns
*/
const analyzePatterns = (containers) => {
const patterns = {
flexColumn: 0,
flexRow: 0,
grid: 0,
sticky: 0,
fixed: 0
};
const analyze = (node) => {
if (!node) return;
if (node.pattern === 'flex-column') patterns.flexColumn++;
if (node.pattern === 'flex-row') patterns.flexRow++;
if (node.pattern && node.pattern.startsWith('grid')) patterns.grid++;
if (node.layout.position === 'sticky') patterns.sticky++;
if (node.layout.position === 'fixed') patterns.fixed++;
if (node.children) {
node.children.forEach(analyze);
}
};
containers.forEach(analyze);
return patterns;
};
/**
* Main extraction function
*/
const extractLayout = () => {
const containers = findMainContainers();
const patterns = analyzePatterns(containers);
return {
metadata: {
extractedAt: new Date().toISOString(),
url: window.location.href,
method: 'layout-structure'
},
patterns: patterns,
structure: containers
};
};
// Execute and return results
return extractLayout();
})();

View File

@@ -329,7 +329,7 @@ for style in $styles; do
### Style ${style}
EOF3
style_guide="../style-consolidation/style-${style}/style-guide.md"
style_guide="../style-extraction/style-${style}/style-guide.md"
if [[ -f "$style_guide" ]]; then
head -n 10 "$style_guide" | tail -n +2 >> PREVIEW.md 2>/dev/null || echo "Design philosophy and tokens" >> PREVIEW.md
else

View File

@@ -52,7 +52,7 @@ auto_detect_pages() {
# Auto-detect style variants count
auto_detect_style_variants() {
local base_path="$1"
local style_dir="$base_path/../style-consolidation"
local style_dir="$base_path/../style-extraction"
if [ ! -d "$style_dir" ]; then
log_warning "Style consolidation directory not found: $style_dir"
@@ -260,7 +260,7 @@ fi
# Validate STYLE_VARIANTS against actual style directories
if [ "$STYLE_VARIANTS" -gt 0 ]; then
style_dir="$BASE_PATH/../style-consolidation"
style_dir="$BASE_PATH/../style-extraction"
if [ ! -d "$style_dir" ]; then
log_error "Style consolidation directory not found: $style_dir"
@@ -337,7 +337,7 @@ for page in "${PAGE_ARRAY[@]}"; do
# Define file paths
TEMPLATE_HTML="_templates/${page}-layout-${l}.html"
STRUCTURAL_CSS="_templates/${page}-layout-${l}.css"
TOKEN_CSS="../style-consolidation/style-${s}/tokens.css"
TOKEN_CSS="../style-extraction/style-${s}/tokens.css"
OUTPUT_HTML="${page}-style-${s}-layout-${l}.html"
# Copy template and replace placeholders
@@ -376,7 +376,7 @@ across all style variants. The HTML structure is identical for all ${page}-layou
prototypes, with only the design tokens (colors, fonts, spacing) varying.
## Design System Reference
Refer to \`../style-consolidation/style-${s}/style-guide.md\` for:
Refer to \`../style-extraction/style-${s}/style-guide.md\` for:
- Design philosophy
- Token usage guidelines
- Component patterns
@@ -742,9 +742,9 @@ for s in $(seq 1 "$STYLE_VARIANTS"); do
cat >> PREVIEW.md <<STYLEEOF
### Style ${s}
- **Tokens:** \`../style-consolidation/style-${s}/design-tokens.json\`
- **CSS Variables:** \`../style-consolidation/style-${s}/tokens.css\`
- **Style Guide:** \`../style-consolidation/style-${s}/style-guide.md\`
- **Tokens:** \`../style-extraction/style-${s}/design-tokens.json\`
- **CSS Variables:** \`../style-extraction/style-${s}/tokens.css\`
- **Style Guide:** \`../style-extraction/style-${s}/style-guide.md\`
STYLEEOF
done