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
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Analyze website screenshot structure using Gemini Vision API.
|
|
4
|
-
|
|
5
|
-
Usage:
|
|
6
|
-
python analyze-structure.py --screenshot ./analysis/desktop.png --output ./analysis
|
|
7
|
-
python analyze-structure.py -s desktop.png -o ./out --html source.html --css source.css
|
|
8
|
-
|
|
9
|
-
Options:
|
|
10
|
-
--screenshot Path to desktop screenshot
|
|
11
|
-
--output Output directory for structure.md
|
|
12
|
-
--html Path to source HTML file (optional, improves accuracy)
|
|
13
|
-
--css Path to filtered CSS file (optional, improves accuracy)
|
|
14
|
-
--model Gemini model (default: gemini-2.5-flash)
|
|
15
|
-
--verbose Enable verbose output
|
|
16
|
-
|
|
17
|
-
Output:
|
|
18
|
-
- structure.md: Markdown description of page structure
|
|
19
|
-
|
|
20
|
-
When HTML/CSS provided, extracts EXACT values from source instead of estimating.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
import argparse
|
|
24
|
-
import json
|
|
25
|
-
import os
|
|
26
|
-
import sys
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
|
|
29
|
-
# Add src directory to path for local imports
|
|
30
|
-
SCRIPT_DIR = Path(__file__).parent.resolve()
|
|
31
|
-
SRC_DIR = SCRIPT_DIR.parent
|
|
32
|
-
sys.path.insert(0, str(SRC_DIR))
|
|
33
|
-
|
|
34
|
-
# Import local env resolver (portable)
|
|
35
|
-
try:
|
|
36
|
-
from utils.env import resolve_env, load_env
|
|
37
|
-
load_env() # Load .env files on startup
|
|
38
|
-
except ImportError:
|
|
39
|
-
# Fallback: simple env getter
|
|
40
|
-
def resolve_env(key, default=None):
|
|
41
|
-
return os.environ.get(key, default)
|
|
42
|
-
|
|
43
|
-
# Check for google-genai dependency
|
|
44
|
-
try:
|
|
45
|
-
from google import genai
|
|
46
|
-
from google.genai import types
|
|
47
|
-
except ImportError:
|
|
48
|
-
print(json.dumps({
|
|
49
|
-
"success": False,
|
|
50
|
-
"error": "google-genai not installed",
|
|
51
|
-
"hint": "Run: pip install google-genai"
|
|
52
|
-
}, indent=2))
|
|
53
|
-
sys.exit(1)
|
|
54
|
-
|
|
55
|
-
# Import prompts from extracted module
|
|
56
|
-
from prompts.structure_analysis import build_structure_prompt
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Fallback structure when analysis fails
|
|
60
|
-
DEFAULT_STRUCTURE = """# Page Structure Analysis
|
|
61
|
-
|
|
62
|
-
## 1. Header Section
|
|
63
|
-
- Logo: Left-aligned, text-based
|
|
64
|
-
- Navigation: Horizontal, 4-5 items
|
|
65
|
-
- CTA Button: Right-aligned, primary color
|
|
66
|
-
- Mobile menu: Hamburger icon for small screens
|
|
67
|
-
|
|
68
|
-
## 2. Hero Section
|
|
69
|
-
- Layout: Centered
|
|
70
|
-
- Headline: Large (36-48px), bold, dark text
|
|
71
|
-
- Subheadline: Medium (18-20px), lighter text
|
|
72
|
-
- Primary CTA: Prominent button, primary color
|
|
73
|
-
- Background: Solid light color
|
|
74
|
-
|
|
75
|
-
## 3. Content Sections
|
|
76
|
-
### Features Section
|
|
77
|
-
- Layout: 3-column grid
|
|
78
|
-
- Items: 3 feature cards with icons
|
|
79
|
-
- Components: Icon + heading + description
|
|
80
|
-
|
|
81
|
-
### About/Info Section
|
|
82
|
-
- Layout: 2-column (text + image)
|
|
83
|
-
- Alternating left-right pattern
|
|
84
|
-
|
|
85
|
-
## 4. Footer Section
|
|
86
|
-
- Layout: 4-column grid
|
|
87
|
-
- Content: Logo, nav links, contact, social icons
|
|
88
|
-
- Copyright: Centered at bottom
|
|
89
|
-
|
|
90
|
-
## 5. Global Patterns
|
|
91
|
-
- Container max-width: 1200px
|
|
92
|
-
- Section padding: 64px vertical
|
|
93
|
-
- Card style: Subtle shadows, 8px border-radius
|
|
94
|
-
- Color scheme: Light mode
|
|
95
|
-
- Typography: Sans-serif (modern)
|
|
96
|
-
|
|
97
|
-
## 6. Responsive Hints
|
|
98
|
-
- Navigation collapses to hamburger on mobile
|
|
99
|
-
- Grid sections stack vertically
|
|
100
|
-
- Padding reduces on smaller screens
|
|
101
|
-
|
|
102
|
-
## 7. BEM Class Suggestions
|
|
103
|
-
- header, header__container, header__logo, header__nav, header__cta
|
|
104
|
-
- hero, hero__container, hero__title, hero__subtitle, hero__cta
|
|
105
|
-
- features, features__grid, feature-card, feature-card__icon, feature-card__title
|
|
106
|
-
- footer, footer__container, footer__column, footer__links
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def get_api_key():
|
|
111
|
-
"""Get Gemini API key from environment (supports GEMINI_API_KEY or GOOGLE_API_KEY)."""
|
|
112
|
-
return resolve_env('GEMINI_API_KEY') or resolve_env('GOOGLE_API_KEY')
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def load_dimensions(output_dir: str) -> dict:
|
|
116
|
-
"""Load extracted dimensions summary if available.
|
|
117
|
-
|
|
118
|
-
Args:
|
|
119
|
-
output_dir: Directory containing dimensions-summary.json
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
Dimensions dict or None if not found
|
|
123
|
-
"""
|
|
124
|
-
summary_path = Path(output_dir) / "dimensions-summary.json"
|
|
125
|
-
if summary_path.exists():
|
|
126
|
-
try:
|
|
127
|
-
with open(summary_path, 'r', encoding='utf-8') as f:
|
|
128
|
-
return json.load(f)
|
|
129
|
-
except (json.JSONDecodeError, IOError) as e:
|
|
130
|
-
print(f"Warning: Failed to load dimensions: {e}", file=sys.stderr)
|
|
131
|
-
return None
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def load_dom_hierarchy(output_dir: str) -> dict:
|
|
135
|
-
"""Load extracted DOM hierarchy if available.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
output_dir: Directory containing dom-hierarchy.json
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
Hierarchy dict or None if not found
|
|
142
|
-
"""
|
|
143
|
-
hierarchy_path = Path(output_dir) / "dom-hierarchy.json"
|
|
144
|
-
if hierarchy_path.exists():
|
|
145
|
-
try:
|
|
146
|
-
with open(hierarchy_path, 'r', encoding='utf-8') as f:
|
|
147
|
-
return json.load(f)
|
|
148
|
-
except (json.JSONDecodeError, IOError) as e:
|
|
149
|
-
print(f"Warning: Failed to load DOM hierarchy: {e}", file=sys.stderr)
|
|
150
|
-
return None
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def load_content_counts(output_dir: str) -> tuple:
|
|
154
|
-
"""Load content counts and summary if available.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
output_dir: Directory containing content-counts.json and content-summary.md
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
Tuple of (counts_dict, summary_text) or (None, None) if not found
|
|
161
|
-
"""
|
|
162
|
-
counts_path = Path(output_dir) / "content-counts.json"
|
|
163
|
-
summary_path = Path(output_dir) / "content-summary.md"
|
|
164
|
-
|
|
165
|
-
counts = None
|
|
166
|
-
summary = None
|
|
167
|
-
|
|
168
|
-
if counts_path.exists():
|
|
169
|
-
try:
|
|
170
|
-
with open(counts_path, 'r', encoding='utf-8') as f:
|
|
171
|
-
counts = json.load(f)
|
|
172
|
-
except (json.JSONDecodeError, IOError) as e:
|
|
173
|
-
print(f"Warning: Failed to load content counts: {e}", file=sys.stderr)
|
|
174
|
-
|
|
175
|
-
if summary_path.exists():
|
|
176
|
-
try:
|
|
177
|
-
with open(summary_path, 'r', encoding='utf-8') as f:
|
|
178
|
-
summary = f.read()
|
|
179
|
-
except IOError as e:
|
|
180
|
-
print(f"Warning: Failed to load content summary: {e}", file=sys.stderr)
|
|
181
|
-
|
|
182
|
-
return counts, summary
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def analyze_structure(
|
|
186
|
-
screenshot_path: str,
|
|
187
|
-
output_dir: str = None,
|
|
188
|
-
html_path: str = None,
|
|
189
|
-
css_path: str = None,
|
|
190
|
-
model: str = "gemini-2.5-flash",
|
|
191
|
-
verbose: bool = False
|
|
192
|
-
) -> str:
|
|
193
|
-
"""Analyze screenshot structure using Gemini Vision.
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
screenshot_path: Path to desktop screenshot
|
|
197
|
-
output_dir: Output directory (also reads dimensions-summary.json if present)
|
|
198
|
-
html_path: Optional path to source HTML (improves accuracy)
|
|
199
|
-
css_path: Optional path to filtered CSS (improves accuracy)
|
|
200
|
-
model: Gemini model to use
|
|
201
|
-
verbose: Enable verbose output
|
|
202
|
-
|
|
203
|
-
Returns:
|
|
204
|
-
Markdown structure analysis
|
|
205
|
-
"""
|
|
206
|
-
|
|
207
|
-
api_key = get_api_key()
|
|
208
|
-
if not api_key:
|
|
209
|
-
if verbose:
|
|
210
|
-
print("Warning: GEMINI_API_KEY not found, using default structure")
|
|
211
|
-
return DEFAULT_STRUCTURE
|
|
212
|
-
|
|
213
|
-
screenshot = Path(screenshot_path)
|
|
214
|
-
if not screenshot.exists():
|
|
215
|
-
if verbose:
|
|
216
|
-
print(f"Warning: Screenshot not found: {screenshot_path}")
|
|
217
|
-
return DEFAULT_STRUCTURE
|
|
218
|
-
|
|
219
|
-
# Load extracted dimensions if available (highest priority)
|
|
220
|
-
dimensions = None
|
|
221
|
-
if output_dir:
|
|
222
|
-
dimensions = load_dimensions(output_dir)
|
|
223
|
-
if dimensions and verbose:
|
|
224
|
-
print(f"Loaded extracted dimensions from {output_dir}/dimensions-summary.json")
|
|
225
|
-
|
|
226
|
-
# Load DOM hierarchy if available (enhances dimensions)
|
|
227
|
-
hierarchy = None
|
|
228
|
-
if output_dir:
|
|
229
|
-
hierarchy = load_dom_hierarchy(output_dir)
|
|
230
|
-
if hierarchy and verbose:
|
|
231
|
-
stats = hierarchy.get('stats', {})
|
|
232
|
-
print(f"Loaded DOM hierarchy: {stats.get('totalNodes', 0)} nodes, depth {stats.get('maxDepth', 0)}")
|
|
233
|
-
|
|
234
|
-
# Load content counts if available
|
|
235
|
-
content_counts, content_summary = None, None
|
|
236
|
-
if output_dir:
|
|
237
|
-
content_counts, content_summary = load_content_counts(output_dir)
|
|
238
|
-
if content_counts and verbose:
|
|
239
|
-
print(f"Loaded content counts: {content_counts.get('summary', {}).get('totalRepeatedItems', 0)} items")
|
|
240
|
-
|
|
241
|
-
# Load HTML/CSS if provided
|
|
242
|
-
html_content = None
|
|
243
|
-
css_content = None
|
|
244
|
-
|
|
245
|
-
if html_path and Path(html_path).exists():
|
|
246
|
-
with open(html_path, 'r', encoding='utf-8') as f:
|
|
247
|
-
html_content = f.read()
|
|
248
|
-
if verbose:
|
|
249
|
-
print(f"Loaded HTML: {len(html_content)} chars")
|
|
250
|
-
|
|
251
|
-
if css_path and Path(css_path).exists():
|
|
252
|
-
with open(css_path, 'r', encoding='utf-8') as f:
|
|
253
|
-
css_content = f.read()
|
|
254
|
-
if verbose:
|
|
255
|
-
print(f"Loaded CSS: {len(css_content)} chars")
|
|
256
|
-
|
|
257
|
-
# Build prompt with context (hierarchy+dimensions have highest priority)
|
|
258
|
-
prompt = build_structure_prompt(html_content, css_content, dimensions, content_summary, hierarchy)
|
|
259
|
-
|
|
260
|
-
if verbose:
|
|
261
|
-
if hierarchy and dimensions:
|
|
262
|
-
print("Using HIERARCHY prompt with DOM structure + EXACT dimensions")
|
|
263
|
-
elif dimensions:
|
|
264
|
-
print("Using ENHANCED prompt with EXACT extracted dimensions")
|
|
265
|
-
if content_summary:
|
|
266
|
-
print("Using prompt with EXACT content counts")
|
|
267
|
-
elif html_content and css_content:
|
|
268
|
-
print("Using prompt with HTML/CSS context")
|
|
269
|
-
|
|
270
|
-
try:
|
|
271
|
-
client = genai.Client(api_key=api_key)
|
|
272
|
-
|
|
273
|
-
# Load image
|
|
274
|
-
with open(screenshot, 'rb') as f:
|
|
275
|
-
img_bytes = f.read()
|
|
276
|
-
|
|
277
|
-
content = [
|
|
278
|
-
prompt,
|
|
279
|
-
types.Part.from_bytes(data=img_bytes, mime_type='image/png')
|
|
280
|
-
]
|
|
281
|
-
|
|
282
|
-
if verbose:
|
|
283
|
-
print(f"Analyzing structure with {model}...")
|
|
284
|
-
|
|
285
|
-
response = client.models.generate_content(
|
|
286
|
-
model=model,
|
|
287
|
-
contents=content
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
if hasattr(response, 'text') and response.text:
|
|
291
|
-
if verbose:
|
|
292
|
-
print("Structure analysis complete")
|
|
293
|
-
return response.text
|
|
294
|
-
else:
|
|
295
|
-
if verbose:
|
|
296
|
-
print("Warning: Empty response, using default structure")
|
|
297
|
-
return DEFAULT_STRUCTURE
|
|
298
|
-
|
|
299
|
-
except Exception as e:
|
|
300
|
-
if verbose:
|
|
301
|
-
print(f"Error during analysis: {e}")
|
|
302
|
-
return DEFAULT_STRUCTURE
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def main():
|
|
306
|
-
parser = argparse.ArgumentParser(
|
|
307
|
-
description="Analyze website screenshot structure using Gemini Vision"
|
|
308
|
-
)
|
|
309
|
-
parser.add_argument(
|
|
310
|
-
'--screenshot', '-s',
|
|
311
|
-
required=True,
|
|
312
|
-
help='Path to desktop screenshot'
|
|
313
|
-
)
|
|
314
|
-
parser.add_argument(
|
|
315
|
-
'--output', '-o',
|
|
316
|
-
required=True,
|
|
317
|
-
help='Output directory for structure.md (also reads dimensions-summary.json if present)'
|
|
318
|
-
)
|
|
319
|
-
parser.add_argument(
|
|
320
|
-
'--html',
|
|
321
|
-
default=None,
|
|
322
|
-
help='Path to source HTML file (optional, improves accuracy)'
|
|
323
|
-
)
|
|
324
|
-
parser.add_argument(
|
|
325
|
-
'--css',
|
|
326
|
-
default=None,
|
|
327
|
-
help='Path to filtered CSS file (optional, improves accuracy)'
|
|
328
|
-
)
|
|
329
|
-
parser.add_argument(
|
|
330
|
-
'--model', '-m',
|
|
331
|
-
default='gemini-2.5-flash',
|
|
332
|
-
help='Gemini model to use (default: gemini-2.5-flash)'
|
|
333
|
-
)
|
|
334
|
-
parser.add_argument(
|
|
335
|
-
'--verbose', '-v',
|
|
336
|
-
action='store_true',
|
|
337
|
-
help='Enable verbose output'
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
args = parser.parse_args()
|
|
341
|
-
|
|
342
|
-
# Create output directory
|
|
343
|
-
output_path = Path(args.output)
|
|
344
|
-
output_path.mkdir(parents=True, exist_ok=True)
|
|
345
|
-
|
|
346
|
-
# Analyze structure (dimensions loaded automatically from output_dir)
|
|
347
|
-
structure = analyze_structure(
|
|
348
|
-
screenshot_path=args.screenshot,
|
|
349
|
-
output_dir=args.output,
|
|
350
|
-
html_path=args.html,
|
|
351
|
-
css_path=args.css,
|
|
352
|
-
model=args.model,
|
|
353
|
-
verbose=args.verbose
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
# Save structure.md
|
|
357
|
-
md_path = output_path / "structure.md"
|
|
358
|
-
with open(md_path, 'w') as f:
|
|
359
|
-
f.write(structure)
|
|
360
|
-
|
|
361
|
-
if args.verbose:
|
|
362
|
-
print(f"Saved: {md_path}")
|
|
363
|
-
|
|
364
|
-
# Output result as JSON
|
|
365
|
-
result = {
|
|
366
|
-
"success": True,
|
|
367
|
-
"structure_file": str(md_path),
|
|
368
|
-
"model": args.model
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
print(json.dumps(result, indent=2))
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if __name__ == '__main__':
|
|
375
|
-
main()
|