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": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"screenshots": "python3 scripts/capture-screenshots.py"
|
||||
},
|
||||
"dependencies": {
|
||||
"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!")
|
||||