ui-mirror-skill 1.0.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 +95 -0
- package/bin/cli.mjs +121 -0
- package/package.json +34 -0
- package/skill/SKILL.md +751 -0
- package/skill/references/analysis-dimensions.md +382 -0
- package/skill/references/component-catalog.md +758 -0
- package/skill/references/css-token-mapping.md +359 -0
- package/skill/references/output-template.md +249 -0
- package/skill/scripts/compare_tokens.py +741 -0
- package/skill/scripts/download_screenshot.py +125 -0
- package/skill/scripts/extract_design_tokens.py +617 -0
- package/skill/scripts/generate_migration.py +580 -0
- package/skill/scripts/generate_radar_chart.py +267 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
download_screenshot.py — Download a screenshot URL or decode a base64 data URL to a local file.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python3 download_screenshot.py <url> <output_path>
|
|
7
|
+
python3 download_screenshot.py --url <url> --output <output_path>
|
|
8
|
+
|
|
9
|
+
Handles:
|
|
10
|
+
- HTTP/HTTPS URLs: fetched via urllib.request
|
|
11
|
+
- data: URLs (data:image/png;base64,...): decoded from base64
|
|
12
|
+
|
|
13
|
+
Dependencies: Python 3 standard library only.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import urllib.request
|
|
20
|
+
import urllib.error
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
TIMEOUT = 15 # seconds
|
|
24
|
+
MAX_RETRIES = 1
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def download_url(url: str, output_path: str) -> None:
|
|
28
|
+
"""Download a URL to a local file with retry."""
|
|
29
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
30
|
+
|
|
31
|
+
last_error = None
|
|
32
|
+
for attempt in range(1 + MAX_RETRIES):
|
|
33
|
+
try:
|
|
34
|
+
req = urllib.request.Request(
|
|
35
|
+
url,
|
|
36
|
+
headers={"User-Agent": "ui-mirror-screenshot-downloader/1.0"},
|
|
37
|
+
)
|
|
38
|
+
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
|
|
39
|
+
data = resp.read()
|
|
40
|
+
with open(output_path, "wb") as f:
|
|
41
|
+
f.write(data)
|
|
42
|
+
return
|
|
43
|
+
except (urllib.error.URLError, urllib.error.HTTPError, OSError) as e:
|
|
44
|
+
last_error = e
|
|
45
|
+
if attempt < MAX_RETRIES:
|
|
46
|
+
print(
|
|
47
|
+
"Retry {}/{}: {}".format(attempt + 1, MAX_RETRIES, e),
|
|
48
|
+
file=sys.stderr,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
raise last_error # type: ignore[misc]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def decode_data_url(data_url: str, output_path: str) -> None:
|
|
55
|
+
"""Decode a base64 data URL and write to file."""
|
|
56
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
57
|
+
|
|
58
|
+
# Parse: data:[<mediatype>][;base64],<data>
|
|
59
|
+
if not data_url.startswith("data:"):
|
|
60
|
+
raise ValueError("Not a data URL: {}".format(data_url[:50]))
|
|
61
|
+
|
|
62
|
+
header_end = data_url.index(",")
|
|
63
|
+
header = data_url[5:header_end] # skip "data:"
|
|
64
|
+
encoded = data_url[header_end + 1:]
|
|
65
|
+
|
|
66
|
+
if ";base64" not in header:
|
|
67
|
+
raise ValueError("Only base64 data URLs are supported, got: {}".format(header))
|
|
68
|
+
|
|
69
|
+
raw = base64.b64decode(encoded)
|
|
70
|
+
with open(output_path, "wb") as f:
|
|
71
|
+
f.write(raw)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main():
|
|
75
|
+
# Parse args: positional or --url/--output flags
|
|
76
|
+
url = None
|
|
77
|
+
output_path = None
|
|
78
|
+
|
|
79
|
+
args = sys.argv[1:]
|
|
80
|
+
i = 0
|
|
81
|
+
positional = []
|
|
82
|
+
while i < len(args):
|
|
83
|
+
if args[i] == "--url" and i + 1 < len(args):
|
|
84
|
+
url = args[i + 1]
|
|
85
|
+
i += 2
|
|
86
|
+
elif args[i] == "--output" and i + 1 < len(args):
|
|
87
|
+
output_path = args[i + 1]
|
|
88
|
+
i += 2
|
|
89
|
+
elif not args[i].startswith("--"):
|
|
90
|
+
positional.append(args[i])
|
|
91
|
+
i += 1
|
|
92
|
+
else:
|
|
93
|
+
i += 1
|
|
94
|
+
|
|
95
|
+
if url is None and len(positional) >= 1:
|
|
96
|
+
url = positional[0]
|
|
97
|
+
if output_path is None and len(positional) >= 2:
|
|
98
|
+
output_path = positional[1]
|
|
99
|
+
|
|
100
|
+
if not url or not output_path:
|
|
101
|
+
print(
|
|
102
|
+
"Usage: {} <url> <output_path>".format(sys.argv[0]),
|
|
103
|
+
file=sys.stderr,
|
|
104
|
+
)
|
|
105
|
+
print(
|
|
106
|
+
" {} --url <url> --output <output_path>".format(sys.argv[0]),
|
|
107
|
+
file=sys.stderr,
|
|
108
|
+
)
|
|
109
|
+
sys.exit(1)
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
if url.startswith("data:"):
|
|
113
|
+
decode_data_url(url, output_path)
|
|
114
|
+
print("Decoded data URL -> {}".format(output_path))
|
|
115
|
+
else:
|
|
116
|
+
download_url(url, output_path)
|
|
117
|
+
size = os.path.getsize(output_path)
|
|
118
|
+
print("Downloaded {} -> {} ({} bytes)".format(url, output_path, size))
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print("Error: {}".format(e), file=sys.stderr)
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
main()
|