design-clone 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -34
- package/SKILL.md +69 -45
- package/bin/cli.js +22 -4
- package/bin/commands/clone-site.js +31 -171
- package/bin/commands/help.js +19 -6
- package/bin/commands/init.js +9 -86
- package/bin/commands/uninstall.js +105 -0
- package/bin/commands/update.js +70 -0
- package/bin/commands/verify.js +7 -14
- package/bin/utils/paths.js +28 -0
- package/bin/utils/validate.js +2 -22
- package/bin/utils/version.js +23 -0
- package/docs/code-standards.md +789 -0
- package/docs/codebase-summary.md +533 -286
- package/docs/index.md +74 -0
- package/docs/project-overview-pdr.md +797 -0
- package/docs/system-architecture.md +718 -0
- package/package.json +14 -17
- package/src/ai/prompts/design-tokens/basic.md +80 -0
- package/src/ai/prompts/design-tokens/section-with-css.md +41 -0
- package/src/ai/prompts/design-tokens/section.md +48 -0
- package/src/ai/prompts/design-tokens/with-css.md +87 -0
- package/src/ai/prompts/structure-analysis/basic.md +55 -0
- package/src/ai/prompts/structure-analysis/with-context.md +59 -0
- package/src/ai/prompts/structure-analysis/with-dimensions.md +63 -0
- package/src/ai/prompts/structure-analysis/with-hierarchy.md +73 -0
- package/src/ai/prompts/ux-audit/aggregation.md +42 -0
- package/src/ai/prompts/ux-audit/desktop.md +92 -0
- package/src/ai/prompts/ux-audit/mobile.md +93 -0
- package/src/ai/prompts/ux-audit/tablet.md +92 -0
- package/src/core/animation/animation-extractor-ast.js +183 -0
- package/src/core/animation/animation-extractor-output.js +152 -0
- package/src/core/animation/animation-extractor.js +178 -0
- package/src/core/animation/state-capture-detection.js +200 -0
- package/src/core/animation/state-capture.js +193 -0
- package/src/core/capture/browser-context-pool.js +96 -0
- package/src/core/capture/multi-page-screenshot-page.js +110 -0
- package/src/core/capture/multi-page-screenshot.js +208 -0
- package/src/core/capture/screenshot-extraction.js +186 -0
- package/src/core/capture/screenshot-helpers.js +175 -0
- package/src/core/capture/screenshot-orchestrator.js +174 -0
- package/src/core/capture/screenshot-viewport.js +93 -0
- package/src/core/capture/screenshot.js +192 -0
- package/src/core/content/content-counter-dom.js +191 -0
- package/src/core/content/content-counter.js +76 -0
- package/src/core/css/breakpoint-detector.js +66 -0
- package/src/core/css/chromium-defaults.json +23 -0
- package/src/core/css/computed-style-extractor.js +102 -0
- package/src/core/css/css-chunker.js +103 -0
- package/src/core/css/filter-css-dead-code.js +120 -0
- package/src/core/css/filter-css-html-analyzer.js +110 -0
- package/src/core/css/filter-css-selector-matcher.js +172 -0
- package/src/core/css/filter-css.js +206 -0
- package/src/core/css/merge-css-atrule-processor.js +158 -0
- package/src/core/css/merge-css-file-io.js +68 -0
- package/src/core/css/merge-css.js +148 -0
- package/src/core/detection/framework-detector-routing.js +68 -0
- package/src/core/detection/framework-detector-signals.js +65 -0
- package/src/core/detection/framework-detector.js +198 -0
- package/src/core/dimension/dimension-extractor-card-detector.js +82 -0
- package/src/core/dimension/dimension-extractor.js +317 -0
- package/src/core/dimension/dimension-output-ai-summary.js +111 -0
- package/src/core/dimension/dimension-output.js +173 -0
- package/src/core/dimension/dom-tree-analyzer-tree-builders.js +95 -0
- package/src/core/dimension/dom-tree-analyzer.js +191 -0
- package/src/core/discovery/app-state-snapshot-capture.js +195 -0
- package/src/core/discovery/app-state-snapshot-utils.js +178 -0
- package/src/core/discovery/app-state-snapshot.js +131 -0
- package/src/core/discovery/discover-pages-routes.js +84 -0
- package/src/core/discovery/discover-pages-utils.js +177 -0
- package/src/core/discovery/discover-pages.js +191 -0
- package/src/core/html/html-extractor-inline-styler.js +70 -0
- package/src/core/html/html-extractor.js +147 -0
- package/src/core/html/semantic-enhancer-mappings.js +200 -0
- package/src/core/html/semantic-enhancer-page.js +148 -0
- package/src/core/html/semantic-enhancer.js +135 -0
- package/src/core/links/rewrite-links-css-rewriter.js +53 -0
- package/src/core/links/rewrite-links.js +173 -0
- package/src/core/media/asset-validator.js +118 -0
- package/src/core/media/extract-assets-downloader.js +187 -0
- package/src/core/media/extract-assets-page-scraper.js +115 -0
- package/src/core/media/extract-assets.js +159 -0
- package/src/core/media/video-capture-convert.js +200 -0
- package/src/core/media/video-capture.js +201 -0
- package/src/core/{lazy-loader.js → page-prep/lazy-loader.js} +37 -39
- package/src/core/section/section-cropper-helpers.js +43 -0
- package/src/core/{section-cropper.js → section/section-cropper.js} +11 -88
- package/src/core/section/section-detector-strategies.js +139 -0
- package/src/core/section/section-detector-utils.js +100 -0
- package/src/core/section/section-detector.js +88 -0
- package/src/core/tests/test-section-cropper.js +2 -2
- package/src/core/tests/test-section-detector.js +2 -2
- package/src/post-process/enhance-assets.js +29 -4
- package/src/post-process/fetch-images-unsplash-client.js +123 -0
- package/src/post-process/fetch-images.js +60 -263
- package/src/post-process/inject-gosnap.js +88 -0
- package/src/post-process/inject-icons-svg-replacer.js +76 -0
- package/src/post-process/inject-icons.js +47 -200
- package/src/route-discoverers/base-discoverer-utils.js +137 -0
- package/src/route-discoverers/base-discoverer.js +29 -118
- package/src/route-discoverers/index.js +1 -1
- package/src/shared/config.js +38 -0
- package/src/shared/error-codes.js +31 -0
- package/src/shared/viewports.js +46 -0
- package/src/utils/browser.js +0 -7
- package/src/utils/helpers.js +4 -0
- package/src/utils/log.js +12 -0
- package/src/utils/playwright-loader.js +76 -0
- package/src/utils/playwright.js +3 -69
- package/src/utils/progress.js +32 -0
- package/src/verification/generate-audit-report-css-fixes.js +52 -0
- package/src/verification/generate-audit-report-sections.js +158 -0
- package/src/verification/generate-audit-report.js +5 -281
- package/src/verification/quality-scorer.js +92 -0
- package/src/verification/verify-footer-checks.js +103 -0
- package/src/verification/verify-footer-helpers.js +178 -0
- package/src/verification/verify-footer.js +23 -381
- package/src/verification/verify-header-checks.js +104 -0
- package/src/verification/verify-header-helpers.js +156 -0
- package/src/verification/verify-header.js +23 -365
- package/src/verification/verify-layout-report.js +101 -0
- package/src/verification/verify-layout.js +13 -259
- package/src/verification/verify-menu-checks.js +104 -0
- package/src/verification/verify-menu-helpers.js +112 -0
- package/src/verification/verify-menu.js +17 -285
- package/src/verification/verify-slider-checks.js +115 -0
- package/src/verification/verify-slider-constants.js +65 -0
- package/src/verification/verify-slider-helpers.js +164 -0
- package/src/verification/verify-slider.js +23 -414
- package/.env.example +0 -14
- package/docs/basic-clone.md +0 -63
- package/docs/cli-reference.md +0 -316
- package/docs/design-clone-architecture.md +0 -492
- package/docs/pixel-perfect.md +0 -117
- package/docs/project-roadmap.md +0 -382
- package/docs/troubleshooting.md +0 -170
- package/requirements.txt +0 -5
- package/src/ai/__pycache__/analyze-structure.cpython-313.pyc +0 -0
- package/src/ai/__pycache__/extract-design-tokens.cpython-313.pyc +0 -0
- package/src/ai/analyze-structure.py +0 -375
- package/src/ai/extract-design-tokens.py +0 -782
- package/src/ai/prompts/__init__.py +0 -2
- package/src/ai/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/ai/prompts/__pycache__/design_tokens.cpython-313.pyc +0 -0
- package/src/ai/prompts/__pycache__/structure_analysis.cpython-313.pyc +0 -0
- package/src/ai/prompts/__pycache__/ux_audit.cpython-313.pyc +0 -0
- package/src/ai/prompts/design_tokens.py +0 -316
- package/src/ai/prompts/structure_analysis.py +0 -592
- package/src/ai/prompts/ux_audit.py +0 -198
- package/src/ai/ux-audit.js +0 -596
- package/src/core/animation-extractor.js +0 -526
- package/src/core/app-state-snapshot.js +0 -511
- package/src/core/content-counter.js +0 -342
- package/src/core/design-tokens.js +0 -103
- package/src/core/dimension-extractor.js +0 -438
- package/src/core/dimension-output.js +0 -305
- package/src/core/discover-pages.js +0 -542
- package/src/core/dom-tree-analyzer.js +0 -298
- package/src/core/extract-assets.js +0 -468
- package/src/core/filter-css.js +0 -499
- package/src/core/framework-detector.js +0 -538
- package/src/core/html-extractor.js +0 -212
- package/src/core/merge-css.js +0 -407
- package/src/core/multi-page-screenshot.js +0 -380
- package/src/core/rewrite-links.js +0 -226
- package/src/core/screenshot.js +0 -701
- package/src/core/section-detector.js +0 -386
- package/src/core/semantic-enhancer.js +0 -492
- package/src/core/state-capture.js +0 -598
- package/src/core/video-capture.js +0 -546
- package/src/utils/__init__.py +0 -16
- package/src/utils/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/utils/__pycache__/env.cpython-313.pyc +0 -0
- package/src/utils/env.py +0 -134
- /package/src/core/{css-extractor.js → css/css-extractor.js} +0 -0
- /package/src/core/{cookie-handler.js → page-prep/cookie-handler.js} +0 -0
- /package/src/core/{page-readiness.js → page-prep/page-readiness.js} +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Viewport Checks
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates all per-viewport header checks using helpers.
|
|
5
|
+
* Separated from verify-header-helpers.js to keep each file under 200 lines.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
HEADER_SELECTORS,
|
|
10
|
+
findElement,
|
|
11
|
+
countVisibleElements,
|
|
12
|
+
checkHeaderPosition,
|
|
13
|
+
checkLogoPosition
|
|
14
|
+
} from './verify-header-helpers.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Test header at a specific viewport — runs all header checks
|
|
18
|
+
* @param {import('playwright').Page} page
|
|
19
|
+
* @param {string} viewportName
|
|
20
|
+
* @param {Object} VIEWPORTS - Viewport map
|
|
21
|
+
* @param {boolean} verbose
|
|
22
|
+
* @returns {Promise<Object>} Viewport result object
|
|
23
|
+
*/
|
|
24
|
+
export async function testHeaderViewport(page, viewportName, VIEWPORTS, verbose = false) {
|
|
25
|
+
const viewport = VIEWPORTS[viewportName];
|
|
26
|
+
await page.setViewportSize(viewport);
|
|
27
|
+
await new Promise(r => setTimeout(r, 500));
|
|
28
|
+
|
|
29
|
+
const result = { viewport: viewportName, dimensions: viewport, tests: [], passed: 0, failed: 0, warnings: [] };
|
|
30
|
+
if (verbose) console.error(`\n📱 Testing ${viewportName} (${viewport.width}x${viewport.height})...`);
|
|
31
|
+
|
|
32
|
+
const headerResult = await findElement(page, HEADER_SELECTORS.container);
|
|
33
|
+
if (!headerResult) {
|
|
34
|
+
result.tests.push({ name: 'Header container exists', passed: false, error: 'No header container found' });
|
|
35
|
+
result.failed++;
|
|
36
|
+
if (verbose) console.error(` ✗ Header not found`);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
result.tests.push({ name: 'Header container exists', passed: true, selector: headerResult.selector });
|
|
41
|
+
result.passed++;
|
|
42
|
+
if (verbose) console.error(` ✓ Header found: ${headerResult.selector}`);
|
|
43
|
+
|
|
44
|
+
const positionInfo = await checkHeaderPosition(page, headerResult.selector);
|
|
45
|
+
|
|
46
|
+
const logoResult = await findElement(page, HEADER_SELECTORS.logo);
|
|
47
|
+
if (logoResult) {
|
|
48
|
+
const logoPosition = await checkLogoPosition(page, logoResult.selector, viewport.width);
|
|
49
|
+
result.tests.push({ name: 'Logo present', passed: true, selector: logoResult.selector, position: logoPosition?.position || 'unknown' });
|
|
50
|
+
result.passed++;
|
|
51
|
+
if (verbose) console.error(` ✓ Logo found: ${logoResult.selector} (${logoPosition?.position})`);
|
|
52
|
+
} else {
|
|
53
|
+
result.tests.push({ name: 'Logo present', passed: false, error: 'No logo found' });
|
|
54
|
+
result.failed++;
|
|
55
|
+
if (verbose) console.error(` ✗ Logo not found`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const navLinks = await countVisibleElements(page, HEADER_SELECTORS.navLinks);
|
|
59
|
+
const expectedLinks = viewportName === 'desktop' ? 2 : 0;
|
|
60
|
+
|
|
61
|
+
if (navLinks.count >= expectedLinks) {
|
|
62
|
+
result.tests.push({ name: 'Navigation links visible', passed: true, count: navLinks.count, selector: navLinks.selector });
|
|
63
|
+
result.passed++;
|
|
64
|
+
if (verbose) console.error(` ✓ ${navLinks.count} nav links visible`);
|
|
65
|
+
} else if (viewportName !== 'desktop' && navLinks.count === 0) {
|
|
66
|
+
result.tests.push({ name: 'Navigation links (may be in hamburger)', passed: true, count: navLinks.count, note: 'Links may be hidden in mobile menu' });
|
|
67
|
+
result.passed++;
|
|
68
|
+
if (verbose) console.error(` ✓ Nav links hidden (expected on ${viewportName})`);
|
|
69
|
+
} else {
|
|
70
|
+
result.tests.push({ name: 'Navigation links visible', passed: false, count: navLinks.count, error: `Expected at least ${expectedLinks} links on ${viewportName}` });
|
|
71
|
+
result.failed++;
|
|
72
|
+
if (verbose) console.error(` ✗ Only ${navLinks.count} nav links (expected >= ${expectedLinks})`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (viewportName === 'desktop') {
|
|
76
|
+
const ctaResult = await findElement(page, HEADER_SELECTORS.cta);
|
|
77
|
+
if (ctaResult) {
|
|
78
|
+
result.tests.push({ name: 'CTA button present', passed: true, selector: ctaResult.selector });
|
|
79
|
+
result.passed++;
|
|
80
|
+
if (verbose) console.error(` ✓ CTA found: ${ctaResult.selector}`);
|
|
81
|
+
} else {
|
|
82
|
+
result.warnings.push('No CTA button found in header');
|
|
83
|
+
if (verbose) console.error(` ⚠ No CTA button found`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (positionInfo) {
|
|
88
|
+
result.tests.push({ name: 'Header sticky/fixed behavior', passed: true, position: positionInfo.position, note: (!positionInfo.isSticky && !positionInfo.isFixed) ? 'Header uses static/relative positioning' : undefined });
|
|
89
|
+
result.passed++;
|
|
90
|
+
if (verbose) console.error(` ✓ Header position: ${positionInfo.position}`);
|
|
91
|
+
|
|
92
|
+
if ((positionInfo.isSticky || positionInfo.isFixed) && positionInfo.zIndex !== 'auto') {
|
|
93
|
+
const zIndexOk = positionInfo.zIndex >= 100;
|
|
94
|
+
result.tests.push({ name: 'Z-index layering', passed: zIndexOk, zIndex: positionInfo.zIndex, note: zIndexOk ? 'Header on top layer' : 'Z-index may be too low' });
|
|
95
|
+
if (zIndexOk) result.passed++;
|
|
96
|
+
else result.warnings.push(`Header z-index (${positionInfo.zIndex}) may be too low`);
|
|
97
|
+
if (verbose) console.error(` ${zIndexOk ? '✓' : '⚠'} Z-index: ${positionInfo.zIndex}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
result.headerHeight = positionInfo.height;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Verification Helpers
|
|
3
|
+
*
|
|
4
|
+
* Selectors, DOM query utilities, and header-specific checks extracted
|
|
5
|
+
* from verify-header.js to keep each file under 200 lines.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Header element selectors
|
|
9
|
+
export const HEADER_SELECTORS = {
|
|
10
|
+
container: [
|
|
11
|
+
'header',
|
|
12
|
+
'[role="banner"]',
|
|
13
|
+
'.header',
|
|
14
|
+
'#header',
|
|
15
|
+
'.site-header',
|
|
16
|
+
'.page-header',
|
|
17
|
+
'.masthead'
|
|
18
|
+
],
|
|
19
|
+
logo: [
|
|
20
|
+
'header img[alt*="logo" i]',
|
|
21
|
+
'[role="banner"] img',
|
|
22
|
+
'.logo img',
|
|
23
|
+
'.site-logo img',
|
|
24
|
+
'.logo',
|
|
25
|
+
'.site-logo',
|
|
26
|
+
'header a[href="/"] img',
|
|
27
|
+
'.brand img',
|
|
28
|
+
'.navbar-brand img'
|
|
29
|
+
],
|
|
30
|
+
nav: [
|
|
31
|
+
'header nav',
|
|
32
|
+
'header [role="navigation"]',
|
|
33
|
+
'.header-nav',
|
|
34
|
+
'.main-navigation',
|
|
35
|
+
'.primary-nav',
|
|
36
|
+
'.site-nav',
|
|
37
|
+
'.navbar-nav'
|
|
38
|
+
],
|
|
39
|
+
cta: [
|
|
40
|
+
'header button.cta',
|
|
41
|
+
'header a[class*="button"]',
|
|
42
|
+
'header a[class*="btn"]',
|
|
43
|
+
'.header-action',
|
|
44
|
+
'.nav-cta',
|
|
45
|
+
'header .btn-primary',
|
|
46
|
+
'header a[href*="contact"]',
|
|
47
|
+
'header a[href*="signup"]',
|
|
48
|
+
'header a[href*="login"]'
|
|
49
|
+
],
|
|
50
|
+
navLinks: [
|
|
51
|
+
'header nav a',
|
|
52
|
+
'header [role="navigation"] a',
|
|
53
|
+
'.main-navigation a',
|
|
54
|
+
'.nav-item a',
|
|
55
|
+
'.menu-item a'
|
|
56
|
+
]
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find first matching element from a list of selectors
|
|
61
|
+
* @param {import('playwright').Page} page
|
|
62
|
+
* @param {string[]} selectors
|
|
63
|
+
* @returns {Promise<{element: ElementHandle, selector: string}|null>}
|
|
64
|
+
*/
|
|
65
|
+
export async function findElement(page, selectors) {
|
|
66
|
+
for (const selector of selectors) {
|
|
67
|
+
try {
|
|
68
|
+
const element = await page.$(selector);
|
|
69
|
+
if (element) return { element, selector };
|
|
70
|
+
} catch (err) { /* continue */ }
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Count visible elements (returns first non-zero match)
|
|
77
|
+
* @param {import('playwright').Page} page
|
|
78
|
+
* @param {string[]} selectors
|
|
79
|
+
* @returns {Promise<{count: number, selector: string|null}>}
|
|
80
|
+
*/
|
|
81
|
+
export async function countVisibleElements(page, selectors) {
|
|
82
|
+
for (const selector of selectors) {
|
|
83
|
+
try {
|
|
84
|
+
const count = await page.evaluate((sel) => {
|
|
85
|
+
const items = document.querySelectorAll(sel);
|
|
86
|
+
let visible = 0;
|
|
87
|
+
items.forEach(item => {
|
|
88
|
+
const style = window.getComputedStyle(item);
|
|
89
|
+
const rect = item.getBoundingClientRect();
|
|
90
|
+
if (
|
|
91
|
+
style.display !== 'none' &&
|
|
92
|
+
style.visibility !== 'hidden' &&
|
|
93
|
+
style.opacity !== '0' &&
|
|
94
|
+
rect.width > 0 &&
|
|
95
|
+
rect.height > 0
|
|
96
|
+
) visible++;
|
|
97
|
+
});
|
|
98
|
+
return visible;
|
|
99
|
+
}, selector);
|
|
100
|
+
|
|
101
|
+
if (count > 0) return { count, selector };
|
|
102
|
+
} catch (err) { /* continue */ }
|
|
103
|
+
}
|
|
104
|
+
return { count: 0, selector: null };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check header position/stickiness properties
|
|
109
|
+
* @param {import('playwright').Page} page
|
|
110
|
+
* @param {string} headerSelector
|
|
111
|
+
* @returns {Promise<{position: string, isSticky: boolean, isFixed: boolean, zIndex: number|string, top: number, height: number, width: number}|null>}
|
|
112
|
+
*/
|
|
113
|
+
export async function checkHeaderPosition(page, headerSelector) {
|
|
114
|
+
return await page.evaluate((sel) => {
|
|
115
|
+
const header = document.querySelector(sel);
|
|
116
|
+
if (!header) return null;
|
|
117
|
+
|
|
118
|
+
const style = window.getComputedStyle(header);
|
|
119
|
+
const rect = header.getBoundingClientRect();
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
position: style.position,
|
|
123
|
+
isSticky: style.position === 'sticky',
|
|
124
|
+
isFixed: style.position === 'fixed',
|
|
125
|
+
zIndex: parseInt(style.zIndex) || 'auto',
|
|
126
|
+
top: rect.top,
|
|
127
|
+
height: rect.height,
|
|
128
|
+
width: rect.width
|
|
129
|
+
};
|
|
130
|
+
}, headerSelector);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check logo position relative to header width
|
|
135
|
+
* @param {import('playwright').Page} page
|
|
136
|
+
* @param {string} logoSelector
|
|
137
|
+
* @param {number} headerWidth
|
|
138
|
+
* @returns {Promise<{position: string, x: number, y: number, width: number, height: number}|null>}
|
|
139
|
+
*/
|
|
140
|
+
export async function checkLogoPosition(page, logoSelector, headerWidth) {
|
|
141
|
+
return await page.evaluate((sel, width) => {
|
|
142
|
+
const logo = document.querySelector(sel);
|
|
143
|
+
if (!logo) return null;
|
|
144
|
+
|
|
145
|
+
const rect = logo.getBoundingClientRect();
|
|
146
|
+
const centerThreshold = width * 0.35;
|
|
147
|
+
|
|
148
|
+
let position = 'unknown';
|
|
149
|
+
if (rect.left < centerThreshold) position = 'left';
|
|
150
|
+
else if (rect.left > width - centerThreshold) position = 'right';
|
|
151
|
+
else position = 'center';
|
|
152
|
+
|
|
153
|
+
return { position, x: rect.left, y: rect.top, width: rect.width, height: rect.height };
|
|
154
|
+
}, logoSelector, headerWidth);
|
|
155
|
+
}
|
|
156
|
+
|