mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-14 02:42:04 +08:00
feat: add progressive exploration to layout extraction script
Enhanced extract-layout-structure.js with intelligent selector discovery: - Auto-detects missing main containers when <3 found - Scans large visible containers (≥500×300px) - Extracts class patterns (main/content/wrapper/container/page/layout/app) - Suggests new selectors to add to script - Returns exploration data with recommendations Added commonClassSelectors strategy: - .main, .content, .main-content, .page-content - .container.main, .wrapper > .main - div[class*="main-wrapper"], div[class*="content-wrapper"] Updated layout-extract.md with progressive exploration documentation. Version: 2.1.0 → 2.2.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -137,15 +137,50 @@ ELSE:
|
|||||||
**Usage**: Read the script file and use content directly in `mcp__chrome-devtools__evaluate_script()`
|
**Usage**: Read the script file and use content directly in `mcp__chrome-devtools__evaluate_script()`
|
||||||
|
|
||||||
**Script returns**:
|
**Script returns**:
|
||||||
- `metadata`: Extraction timestamp, URL, method
|
- `metadata`: Extraction timestamp, URL, method, version
|
||||||
- `patterns`: Layout pattern statistics (flexColumn, flexRow, grid counts)
|
- `patterns`: Layout pattern statistics (flexColumn, flexRow, grid counts)
|
||||||
- `structure`: Hierarchical DOM tree with layout properties
|
- `structure`: Hierarchical DOM tree with layout properties
|
||||||
|
- `exploration`: (Optional) Progressive exploration results when standard selectors fail
|
||||||
|
|
||||||
**Benefits**:
|
**Benefits**:
|
||||||
- ✅ Real flex/grid configuration (justifyContent, alignItems, gap, etc.)
|
- ✅ Real flex/grid configuration (justifyContent, alignItems, gap, etc.)
|
||||||
- ✅ Accurate element bounds (x, y, width, height)
|
- ✅ Accurate element bounds (x, y, width, height)
|
||||||
- ✅ Structural hierarchy with depth control
|
- ✅ Structural hierarchy with depth control
|
||||||
- ✅ Layout pattern identification (flex-row, flex-column, grid-NCol)
|
- ✅ Layout pattern identification (flex-row, flex-column, grid-NCol)
|
||||||
|
- ✅ Progressive exploration: Auto-discovers missing selectors
|
||||||
|
|
||||||
|
**Progressive Exploration Strategy** (v2.2.0+):
|
||||||
|
|
||||||
|
When script finds <3 main containers, it automatically:
|
||||||
|
1. **Scans** all large visible containers (≥500×300px)
|
||||||
|
2. **Extracts** class patterns matching: `main|content|wrapper|container|page|layout|app`
|
||||||
|
3. **Suggests** new selectors to add to script
|
||||||
|
4. **Returns** exploration data in `result.exploration`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"triggered": true,
|
||||||
|
"discoveredCandidates": [{classes, bounds, display}],
|
||||||
|
"suggestedSelectors": [".wrapper", ".page-index"],
|
||||||
|
"recommendation": ".wrapper, .page-index, .app-container"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using Exploration Results**:
|
||||||
|
```javascript
|
||||||
|
// After extraction, check for suggestions
|
||||||
|
IF result.exploration?.triggered:
|
||||||
|
REPORT: result.exploration.warning
|
||||||
|
REPORT: "Suggested selectors: " + result.exploration.recommendation
|
||||||
|
|
||||||
|
// Update script by adding to commonClassSelectors array
|
||||||
|
// Then re-run extraction for better coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
**Selector Update Workflow**:
|
||||||
|
1. Run extraction on unfamiliar site
|
||||||
|
2. Check `result.exploration.suggestedSelectors`
|
||||||
|
3. Add relevant selectors to script's `commonClassSelectors`
|
||||||
|
4. Re-run extraction → improved container detection
|
||||||
|
|
||||||
### Step 3: Memory Check
|
### Step 3: Memory Check
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
/**
|
/**
|
||||||
* Extract Layout Structure from DOM
|
* Extract Layout Structure from DOM - Enhanced Version
|
||||||
*
|
*
|
||||||
* Extracts real layout information from DOM to provide accurate
|
* Extracts real layout information from DOM to provide accurate
|
||||||
* structural data for UI replication.
|
* structural data for UI replication.
|
||||||
*
|
*
|
||||||
|
* Features:
|
||||||
|
* - Framework detection (Nuxt.js, Next.js, React, Vue, Angular)
|
||||||
|
* - Multi-strategy container detection (strict → relaxed → class-based → framework-specific)
|
||||||
|
* - Intelligent main content detection with common class names support
|
||||||
|
* - Supports modern SPA frameworks
|
||||||
|
* - Detects non-semantic main containers (.main, .content, etc.)
|
||||||
|
* - Progressive exploration: Auto-discovers missing selectors when standard patterns fail
|
||||||
|
* - Suggests new class names to add to script based on actual page structure
|
||||||
|
*
|
||||||
|
* Progressive Exploration:
|
||||||
|
* When fewer than 3 main containers are found, the script automatically:
|
||||||
|
* 1. Analyzes all large visible containers (≥500×300px)
|
||||||
|
* 2. Extracts class name patterns (main/content/wrapper/container/page/etc.)
|
||||||
|
* 3. Suggests new selectors to add to the script
|
||||||
|
* 4. Returns exploration data in result.exploration
|
||||||
|
*
|
||||||
* Usage: Execute via Chrome DevTools evaluate_script
|
* Usage: Execute via Chrome DevTools evaluate_script
|
||||||
|
* Version: 2.2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
@@ -62,7 +79,7 @@
|
|||||||
const identifyPattern = (props) => {
|
const identifyPattern = (props) => {
|
||||||
const { display, flexDirection, gridTemplateColumns } = props;
|
const { display, flexDirection, gridTemplateColumns } = props;
|
||||||
|
|
||||||
if (display === 'flex') {
|
if (display === 'flex' || display === 'inline-flex') {
|
||||||
if (flexDirection === 'column') return 'flex-column';
|
if (flexDirection === 'column') return 'flex-column';
|
||||||
if (flexDirection === 'row') return 'flex-row';
|
if (flexDirection === 'row') return 'flex-row';
|
||||||
return 'flex';
|
return 'flex';
|
||||||
@@ -77,12 +94,23 @@
|
|||||||
return 'grid';
|
return 'grid';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (display === 'inline-flex') return 'inline-flex';
|
|
||||||
if (display === 'block') return 'block';
|
if (display === 'block') return 'block';
|
||||||
|
|
||||||
return display;
|
return display;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect frontend framework
|
||||||
|
*/
|
||||||
|
const detectFramework = () => {
|
||||||
|
if (document.querySelector('#__nuxt')) return { name: 'Nuxt.js', version: 'unknown' };
|
||||||
|
if (document.querySelector('#__next')) return { name: 'Next.js', version: 'unknown' };
|
||||||
|
if (document.querySelector('[data-reactroot]')) return { name: 'React', version: 'unknown' };
|
||||||
|
if (document.querySelector('[ng-version]')) return { name: 'Angular', version: 'unknown' };
|
||||||
|
if (window.Vue) return { name: 'Vue.js', version: window.Vue.version || 'unknown' };
|
||||||
|
return { name: 'Unknown', version: 'unknown' };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build layout tree recursively
|
* Build layout tree recursively
|
||||||
*/
|
*/
|
||||||
@@ -139,35 +167,165 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find main layout containers
|
* Find main layout containers with multi-strategy approach
|
||||||
*/
|
*/
|
||||||
const findMainContainers = () => {
|
const findMainContainers = () => {
|
||||||
const selectors = [
|
const containers = [];
|
||||||
|
const found = new Set();
|
||||||
|
|
||||||
|
// Strategy 1: Strict selectors (body direct children)
|
||||||
|
const strictSelectors = [
|
||||||
'body > header',
|
'body > header',
|
||||||
'body > nav',
|
'body > nav',
|
||||||
'body > main',
|
'body > main',
|
||||||
'body > footer',
|
'body > footer'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Strategy 2: Relaxed selectors (any level)
|
||||||
|
const relaxedSelectors = [
|
||||||
|
'header',
|
||||||
|
'nav',
|
||||||
|
'main',
|
||||||
|
'footer',
|
||||||
'[role="banner"]',
|
'[role="banner"]',
|
||||||
'[role="navigation"]',
|
'[role="navigation"]',
|
||||||
'[role="main"]',
|
'[role="main"]',
|
||||||
'[role="contentinfo"]'
|
'[role="contentinfo"]'
|
||||||
];
|
];
|
||||||
|
|
||||||
const containers = [];
|
// Strategy 3: Common class-based main content selectors
|
||||||
|
const commonClassSelectors = [
|
||||||
|
'.main',
|
||||||
|
'.content',
|
||||||
|
'.main-content',
|
||||||
|
'.page-content',
|
||||||
|
'.container.main',
|
||||||
|
'.wrapper > .main',
|
||||||
|
'div[class*="main-wrapper"]',
|
||||||
|
'div[class*="content-wrapper"]'
|
||||||
|
];
|
||||||
|
|
||||||
selectors.forEach(selector => {
|
// Strategy 4: Framework-specific selectors
|
||||||
const element = document.querySelector(selector);
|
const frameworkSelectors = [
|
||||||
if (element) {
|
'#__nuxt header', '#__nuxt .main', '#__nuxt main', '#__nuxt footer',
|
||||||
const tree = buildLayoutTree(element, 0, 3);
|
'#__next header', '#__next .main', '#__next main', '#__next footer',
|
||||||
if (tree) {
|
'#app header', '#app .main', '#app main', '#app footer',
|
||||||
containers.push(tree);
|
'[data-app] header', '[data-app] .main', '[data-app] main', '[data-app] footer'
|
||||||
}
|
];
|
||||||
|
|
||||||
|
// Try all strategies
|
||||||
|
const allSelectors = [...strictSelectors, ...relaxedSelectors, ...commonClassSelectors, ...frameworkSelectors];
|
||||||
|
|
||||||
|
allSelectors.forEach(selector => {
|
||||||
|
try {
|
||||||
|
const elements = document.querySelectorAll(selector);
|
||||||
|
elements.forEach(element => {
|
||||||
|
// Avoid duplicates and invisible elements
|
||||||
|
if (!found.has(element) && element.offsetParent !== null) {
|
||||||
|
found.add(element);
|
||||||
|
const tree = buildLayoutTree(element, 0, 3);
|
||||||
|
if (tree && tree.bounds.width > 0 && tree.bounds.height > 0) {
|
||||||
|
containers.push(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`Selector failed: ${selector}`, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fallback: If no containers found, use body's direct children
|
||||||
|
if (containers.length === 0) {
|
||||||
|
Array.from(document.body.children).forEach(child => {
|
||||||
|
if (child.offsetParent !== null && !found.has(child)) {
|
||||||
|
const tree = buildLayoutTree(child, 0, 2);
|
||||||
|
if (tree && tree.bounds.width > 100 && tree.bounds.height > 100) {
|
||||||
|
containers.push(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return containers;
|
return containers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Progressive exploration: Discover main containers when standard selectors fail
|
||||||
|
* Analyzes large visible containers and suggests class name patterns
|
||||||
|
*/
|
||||||
|
const exploreMainContainers = () => {
|
||||||
|
const candidates = [];
|
||||||
|
const minWidth = 500;
|
||||||
|
const minHeight = 300;
|
||||||
|
|
||||||
|
// Find all large visible divs
|
||||||
|
const allDivs = document.querySelectorAll('div');
|
||||||
|
allDivs.forEach(div => {
|
||||||
|
const rect = div.getBoundingClientRect();
|
||||||
|
const style = window.getComputedStyle(div);
|
||||||
|
|
||||||
|
// Filter: large size, visible, not header/footer
|
||||||
|
if (rect.width >= minWidth &&
|
||||||
|
rect.height >= minHeight &&
|
||||||
|
div.offsetParent !== null &&
|
||||||
|
!div.closest('header') &&
|
||||||
|
!div.closest('footer')) {
|
||||||
|
|
||||||
|
const classes = Array.from(div.classList);
|
||||||
|
const area = rect.width * rect.height;
|
||||||
|
|
||||||
|
candidates.push({
|
||||||
|
element: div,
|
||||||
|
classes: classes,
|
||||||
|
area: area,
|
||||||
|
bounds: {
|
||||||
|
width: Math.round(rect.width),
|
||||||
|
height: Math.round(rect.height)
|
||||||
|
},
|
||||||
|
display: style.display,
|
||||||
|
depth: getElementDepth(div)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by area (largest first) and take top candidates
|
||||||
|
candidates.sort((a, b) => b.area - a.area);
|
||||||
|
|
||||||
|
// Extract unique class patterns from top candidates
|
||||||
|
const classPatterns = new Set();
|
||||||
|
candidates.slice(0, 20).forEach(c => {
|
||||||
|
c.classes.forEach(cls => {
|
||||||
|
// Identify potential main content class patterns
|
||||||
|
if (cls.match(/main|content|container|wrapper|page|body|layout|app/i)) {
|
||||||
|
classPatterns.add(cls);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
candidates: candidates.slice(0, 10).map(c => ({
|
||||||
|
classes: c.classes,
|
||||||
|
bounds: c.bounds,
|
||||||
|
display: c.display,
|
||||||
|
depth: c.depth
|
||||||
|
})),
|
||||||
|
suggestedSelectors: Array.from(classPatterns).map(cls => `.${cls}`)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get element depth in DOM tree
|
||||||
|
*/
|
||||||
|
const getElementDepth = (element) => {
|
||||||
|
let depth = 0;
|
||||||
|
let current = element;
|
||||||
|
while (current.parentElement) {
|
||||||
|
depth++;
|
||||||
|
current = current.parentElement;
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyze layout patterns
|
* Analyze layout patterns
|
||||||
*/
|
*/
|
||||||
@@ -199,21 +357,53 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main extraction function
|
* Main extraction function with progressive exploration
|
||||||
*/
|
*/
|
||||||
const extractLayout = () => {
|
const extractLayout = () => {
|
||||||
|
const framework = detectFramework();
|
||||||
const containers = findMainContainers();
|
const containers = findMainContainers();
|
||||||
const patterns = analyzePatterns(containers);
|
const patterns = analyzePatterns(containers);
|
||||||
|
|
||||||
return {
|
// Progressive exploration: if too few containers found, explore and suggest
|
||||||
|
let exploration = null;
|
||||||
|
const minExpectedContainers = 3; // At least header, main, footer
|
||||||
|
|
||||||
|
if (containers.length < minExpectedContainers) {
|
||||||
|
exploration = exploreMainContainers();
|
||||||
|
|
||||||
|
// Add warning message
|
||||||
|
exploration.warning = `Only ${containers.length} containers found. Consider adding these selectors to the script:`;
|
||||||
|
exploration.recommendation = exploration.suggestedSelectors.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
metadata: {
|
metadata: {
|
||||||
extractedAt: new Date().toISOString(),
|
extractedAt: new Date().toISOString(),
|
||||||
url: window.location.href,
|
url: window.location.href,
|
||||||
method: 'layout-structure'
|
framework: framework,
|
||||||
|
method: 'layout-structure-enhanced',
|
||||||
|
version: '2.2.0'
|
||||||
|
},
|
||||||
|
statistics: {
|
||||||
|
totalContainers: containers.length,
|
||||||
|
patterns: patterns
|
||||||
},
|
},
|
||||||
patterns: patterns,
|
|
||||||
structure: containers
|
structure: containers
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add exploration results if triggered
|
||||||
|
if (exploration) {
|
||||||
|
result.exploration = {
|
||||||
|
triggered: true,
|
||||||
|
reason: 'Insufficient containers found with standard selectors',
|
||||||
|
discoveredCandidates: exploration.candidates,
|
||||||
|
suggestedSelectors: exploration.suggestedSelectors,
|
||||||
|
warning: exploration.warning,
|
||||||
|
recommendation: exploration.recommendation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute and return results
|
// Execute and return results
|
||||||
|
|||||||
Reference in New Issue
Block a user