feat(screenshots): add automated screenshot utility and refresh all UI image assets
CI & Build NaxOS Web Dashboard / build (push) Successful in 1m7s
|
After Width: | Height: | Size: 32 KiB |
@@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"screenshots": "python3 scripts/capture-screenshots.py"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 312 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 312 KiB |
@@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
NaxOS Dashboard Automated Screenshot Utility
|
||||||
|
Builds the UI and launches headless Chrome to capture crisp, authentic application screenshots.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
UI_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
|
||||||
|
DIST_DIR = os.path.join(UI_DIR, "dist")
|
||||||
|
SCREENSHOTS_DIR = os.path.join(UI_DIR, "public", "screenshots")
|
||||||
|
PORT = 4199
|
||||||
|
|
||||||
|
# Detect Chrome binary
|
||||||
|
CHROME_CANDIDATES = [
|
||||||
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||||
|
"/usr/bin/google-chrome",
|
||||||
|
"/usr/bin/chromium-browser",
|
||||||
|
"/usr/bin/chromium",
|
||||||
|
]
|
||||||
|
CHROME_BIN = None
|
||||||
|
for candidate in CHROME_CANDIDATES:
|
||||||
|
if os.path.exists(candidate):
|
||||||
|
CHROME_BIN = candidate
|
||||||
|
break
|
||||||
|
|
||||||
|
if not CHROME_BIN:
|
||||||
|
print("Error: Google Chrome / Chromium not found. Please install Chrome to use automated screenshot capture.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.makedirs(SCREENSHOTS_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
# 1. Build UI dist
|
||||||
|
print("==> [1/3] Building NaxOS UI production bundle...")
|
||||||
|
subprocess.run(["npm", "run", "build"], cwd=UI_DIR, check=True)
|
||||||
|
|
||||||
|
# 2. Start HTTP server
|
||||||
|
print(f"==> [2/3] Serving UI dist at http://localhost:{PORT}...")
|
||||||
|
server_proc = subprocess.Popen(
|
||||||
|
[sys.executable, "-m", "http.server", str(PORT), "--directory", DIST_DIR],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
|
||||||
|
VIEWS = [
|
||||||
|
("dashboard-overview.png", "#overview", 1440, 960),
|
||||||
|
("system-telemetry.png", "#overview", 1440, 1280),
|
||||||
|
("storage-management.png", "#storage", 1440, 1000),
|
||||||
|
("app-store.png", "#apps", 1440, 1000),
|
||||||
|
("migration-wizard.png", "#wizard", 1440, 960),
|
||||||
|
("shares-management.png", "#shares", 1440, 960),
|
||||||
|
("gitops-sync.png", "#gitops", 1440, 960),
|
||||||
|
("system-logs.png", "#logs", 1440, 960),
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
time.sleep(1.5)
|
||||||
|
print("==> [3/3] Capturing authentic application screenshots via headless Chrome...")
|
||||||
|
for filename, hash_route, width, height in VIEWS:
|
||||||
|
target_file = os.path.join(SCREENSHOTS_DIR, filename)
|
||||||
|
url = f"http://localhost:{PORT}/{hash_route}"
|
||||||
|
print(f" Capturing {hash_route:<10} ({width}x{height}) -> {filename}...")
|
||||||
|
cmd = [
|
||||||
|
CHROME_BIN,
|
||||||
|
"--headless=new",
|
||||||
|
"--disable-gpu",
|
||||||
|
f"--window-size={width},{height}",
|
||||||
|
"--virtual-time-budget=3000",
|
||||||
|
f"--screenshot={target_file}",
|
||||||
|
url
|
||||||
|
]
|
||||||
|
res = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
if os.path.exists(target_file):
|
||||||
|
size = os.path.getsize(target_file)
|
||||||
|
print(f" [OK] {filename} ({size // 1024} KB)")
|
||||||
|
else:
|
||||||
|
print(f" [FAILED] {filename}: {res.stderr}", file=sys.stderr)
|
||||||
|
|
||||||
|
# Legacy alias
|
||||||
|
telemetry_src = os.path.join(SCREENSHOTS_DIR, "system-telemetry.png")
|
||||||
|
perses_dst = os.path.join(SCREENSHOTS_DIR, "perses-analytics.png")
|
||||||
|
if os.path.exists(telemetry_src):
|
||||||
|
shutil.copyfile(telemetry_src, perses_dst)
|
||||||
|
|
||||||
|
# Copy to sister documentation repositories if present
|
||||||
|
repo_root = os.path.abspath(os.path.join(UI_DIR, ".."))
|
||||||
|
for repo in ["naxos-os", "naxos-api", "naxos-docs"]:
|
||||||
|
doc_img_dir = os.path.join(repo_root, repo, "docs", "images")
|
||||||
|
if os.path.exists(doc_img_dir):
|
||||||
|
for filename, _, _, _ in VIEWS:
|
||||||
|
src_path = os.path.join(SCREENSHOTS_DIR, filename)
|
||||||
|
if os.path.exists(src_path):
|
||||||
|
shutil.copyfile(src_path, os.path.join(doc_img_dir, filename))
|
||||||
|
if os.path.exists(perses_dst):
|
||||||
|
shutil.copyfile(perses_dst, os.path.join(doc_img_dir, "perses-analytics.png"))
|
||||||
|
print(f" Synced screenshots to {repo}/docs/images/")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
server_proc.terminate()
|
||||||
|
server_proc.wait()
|
||||||
|
|
||||||
|
print("\nAll NaxOS UI screenshots successfully captured and distributed!")
|
||||||