From 27646bab570cb272f1fbbae77f4e6694c99baa68 Mon Sep 17 00:00:00 2001 From: Deano Calver Date: Fri, 3 Apr 2026 12:52:25 +0300 Subject: [PATCH] Add e2e output cleanup, zsc_modern_ai theme, and submodule bumps Introduce a shared output-cleanup library (bash + PowerShell) so that auto-generated timestamped run directories are removed after a successful e2e run. Failed runs and explicitly-set OUTPUT_DIR paths are always preserved; KEEP_OUTPUT=1 opts out of cleanup on success. Also adds the zsc_modern_ai UI theme and advances SpiderApp and Spiderweb submodules to their latest release heads. Co-Authored-By: Claude Sonnet 4.6 --- SpiderApp | 2 +- Spiderweb | 2 +- e2e/README.md | 6 + e2e/output-cleanup.sh | 41 +++ e2e/test-cross-platform-agent-relay.ps1 | 5 + e2e/test-cross-platform-agent-relay.sh | 10 +- ...st-cross-platform-computer-browser-demo.sh | 10 +- e2e/test-cross-platform-node-workspace.ps1 | 5 + e2e/test-cross-platform-node-workspace.sh | 10 +- e2e/test-macos-computer-browser-node.sh | 13 +- e2e/test-mcp-bridge.sh | 10 +- e2e/windows-e2e-common.ps1 | 46 +++ themes/zsc_modern_ai/layouts/workspace.json | 23 ++ themes/zsc_modern_ai/manifest.json | 17 + themes/zsc_modern_ai/profiles/fullscreen.json | 9 + themes/zsc_modern_ai/profiles/phone.json | 9 + themes/zsc_modern_ai/profiles/tablet.json | 9 + themes/zsc_modern_ai/styles/components.json | 306 ++++++++++++++++++ themes/zsc_modern_ai/tokens/base.json | 28 ++ themes/zsc_modern_ai/tokens/dark.json | 10 + themes/zsc_modern_ai/tokens/light.json | 14 + 21 files changed, 575 insertions(+), 10 deletions(-) create mode 100644 e2e/output-cleanup.sh create mode 100644 themes/zsc_modern_ai/layouts/workspace.json create mode 100644 themes/zsc_modern_ai/manifest.json create mode 100644 themes/zsc_modern_ai/profiles/fullscreen.json create mode 100644 themes/zsc_modern_ai/profiles/phone.json create mode 100644 themes/zsc_modern_ai/profiles/tablet.json create mode 100644 themes/zsc_modern_ai/styles/components.json create mode 100644 themes/zsc_modern_ai/tokens/base.json create mode 100644 themes/zsc_modern_ai/tokens/dark.json create mode 100644 themes/zsc_modern_ai/tokens/light.json diff --git a/SpiderApp b/SpiderApp index ae61012..625b724 160000 --- a/SpiderApp +++ b/SpiderApp @@ -1 +1 @@ -Subproject commit ae610127c90d7bcf8fafe6b850b86601310e8bba +Subproject commit 625b7247d14b6bf0163df54d760fb3aa695c1328 diff --git a/Spiderweb b/Spiderweb index ed205e7..85799ce 160000 --- a/Spiderweb +++ b/Spiderweb @@ -1 +1 @@ -Subproject commit ed205e79f6bbc1880c4efea3d625bc37bc2f61f7 +Subproject commit 85799cecf80f269995f132f25c3db59716635a2f diff --git a/e2e/README.md b/e2e/README.md index 11ffcbb..34c2e9e 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -31,6 +31,8 @@ OUTPUT_DIR=/Users/deanocalver/Documents/Projects/Spider/e2e/out/manual-computer- bash /Users/deanocalver/Documents/Projects/Spider/e2e/test-macos-computer-browser-node.sh ``` +Default timestamped run directories are now treated as transient and removed automatically after a successful run. Set `KEEP_OUTPUT=1` when you want to keep a generated run directory, or provide an explicit `OUTPUT_DIR` when you want to preserve artifacts in a known location. + ### Prerequisites - macOS @@ -50,6 +52,8 @@ Each run writes a timestamped artifact directory under: `/Users/deanocalver/Documents/Projects/Spider/e2e/out/` +Successful runs clean up those auto-generated directories by default. Failed runs are preserved for debugging, and you can keep successful artifacts too with `KEEP_OUTPUT=1` or an explicit `OUTPUT_DIR`. + Important artifacts: - `artifacts/providers.before-bind.json` @@ -97,6 +101,8 @@ Each run writes a timestamped artifact directory under: `/Users/deanocalver/Documents/Projects/Spider/e2e/out/` +Successful runs clean up those auto-generated directories by default. Failed runs are preserved for debugging, and `KEEP_OUTPUT=1` keeps successful artifacts too. + Important artifacts: - `artifacts/control_handoff.json` diff --git a/e2e/output-cleanup.sh b/e2e/output-cleanup.sh new file mode 100644 index 0000000..f6a26b7 --- /dev/null +++ b/e2e/output-cleanup.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +e2e_is_truthy() { + case "${1:-}" in + 1|true|TRUE|yes|YES|on|ON) + return 0 + ;; + *) + return 1 + ;; + esac +} + +e2e_cleanup_output_dir() { + local exit_code="$1" + local output_dir="$2" + local output_dir_was_explicit="${3:-0}" + local keep_output="${4:-}" + + if [[ ! -d "$output_dir" ]]; then + return 0 + fi + + if e2e_is_truthy "$keep_output"; then + echo "[INFO] Preserving E2E artifacts at $output_dir because KEEP_OUTPUT is enabled" + return 0 + fi + + if [[ "$exit_code" -ne 0 ]]; then + echo "[INFO] Preserving failed E2E artifacts at $output_dir" + return 0 + fi + + if [[ "$output_dir_was_explicit" == "1" ]]; then + echo "[INFO] Preserving E2E artifacts at $output_dir because OUTPUT_DIR was set explicitly" + return 0 + fi + + rm -rf "$output_dir" + echo "[INFO] Removed transient E2E artifacts at $output_dir" +} diff --git a/e2e/test-cross-platform-agent-relay.ps1 b/e2e/test-cross-platform-agent-relay.ps1 index 9497e8a..0ab7272 100644 --- a/e2e/test-cross-platform-agent-relay.ps1 +++ b/e2e/test-cross-platform-agent-relay.ps1 @@ -11,6 +11,7 @@ $WindowsReviewerPrompt = Join-Path $RootDir "e2e/prompts/agent-relay-windows-rev $Validator = Join-Path $RootDir "e2e/validate_agent_relay.py" $RelayRunner = Join-Path $RootDir "e2e/agent_relay_runner.py" +$OutputDirWasExplicit = Test-Path Env:OUTPUT_DIR $OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { New-RunDirectory "cross-platform-agent-relay-windows" } $OutputDir = Ensure-RunDirectoryUnderRepo $OutputDir $RunDirRel = Get-RunDirectoryRelative $OutputDir @@ -45,6 +46,7 @@ $LocalRemoteNodeLog = Join-Path $LogDir "windows-remote-node.log" $LocalRemoteNodeErrLog = Join-Path $LogDir "windows-remote-node.stderr.log" $LocalRemoteNode = $null $script:HelperEnv = $null +$script:RunSucceeded = $false function Cleanup { if ($null -ne $script:LocalRemoteNode -and -not $script:LocalRemoteNode.HasExited) { @@ -289,6 +291,9 @@ try { Write-Host " $RelayValidationJson" Write-Host " $LinuxWorkerLast" Write-Host " $WindowsReviewerLast" + $script:RunSucceeded = $true } finally { Cleanup + $exitCode = if ($script:RunSucceeded) { 0 } else { 1 } + Remove-RunDirectoryIfTransient -ExitCode $exitCode -Path $OutputDir -WasExplicit $OutputDirWasExplicit } diff --git a/e2e/test-cross-platform-agent-relay.sh b/e2e/test-cross-platform-agent-relay.sh index 4eb4889..7e08e8f 100755 --- a/e2e/test-cross-platform-agent-relay.sh +++ b/e2e/test-cross-platform-agent-relay.sh @@ -3,13 +3,19 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "$ROOT_DIR/e2e/output-cleanup.sh" LINUX_HELPER="$ROOT_DIR/e2e/run_linux_spiderweb_host.sh" REMOTE_TEMPLATE_DIR="$ROOT_DIR/e2e/fixtures/agent-relay" LINUX_WORKER_PROMPT="$ROOT_DIR/e2e/prompts/agent-relay-linux-worker.txt" MACOS_REVIEWER_PROMPT="$ROOT_DIR/e2e/prompts/agent-relay-macos-reviewer.txt" VALIDATOR="$ROOT_DIR/e2e/validate_agent_relay.py" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/e2e/out/cross-platform-agent-relay-$(date +%Y%m%d-%H%M%S)-$$}" +OUTPUT_DIR_WAS_EXPLICIT=0 +if [[ -n "${OUTPUT_DIR+x}" ]]; then + OUTPUT_DIR_WAS_EXPLICIT=1 +else + OUTPUT_DIR="$ROOT_DIR/e2e/out/cross-platform-agent-relay-$(date +%Y%m%d-%H%M%S)-$$" +fi case "$OUTPUT_DIR" in "$ROOT_DIR"/*) RUN_DIR_REL="${OUTPUT_DIR#"$ROOT_DIR/"}" ;; *) @@ -38,6 +44,7 @@ REMOTE_NODE_PORT="${REMOTE_NODE_PORT:-28952}" REMOTE_NODE_NAME="${REMOTE_NODE_NAME:-cross-macos-review-node}" REMOTE_EXPORT_NAME="${REMOTE_EXPORT_NAME:-remote-smoke}" REMOTE_BIND_PATH="/remote" +KEEP_OUTPUT="${KEEP_OUTPUT:-}" LINUX_WORKER_JSONL="$LOG_DIR/linux-worker-codex.jsonl" LINUX_WORKER_STDERR="$LOG_DIR/linux-worker-codex.stderr.log" @@ -192,6 +199,7 @@ cleanup() { if [[ -f "$LINUX_HELPER" ]] && command -v orbctl >/dev/null 2>&1; then orb_run cleanup >/dev/null 2>&1 || true fi + e2e_cleanup_output_dir "$exit_code" "$OUTPUT_DIR" "$OUTPUT_DIR_WAS_EXPLICIT" "$KEEP_OUTPUT" exit "$exit_code" } trap cleanup EXIT diff --git a/e2e/test-cross-platform-computer-browser-demo.sh b/e2e/test-cross-platform-computer-browser-demo.sh index f70f25b..702740b 100644 --- a/e2e/test-cross-platform-computer-browser-demo.sh +++ b/e2e/test-cross-platform-computer-browser-demo.sh @@ -3,13 +3,19 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "$ROOT_DIR/e2e/output-cleanup.sh" LINUX_HELPER="$ROOT_DIR/e2e/run_linux_cross_platform_computer_browser_host.sh" FIXTURE_DIR="$ROOT_DIR/e2e/fixtures/macos-computer-browser" FIXTURE_SWIFT="$FIXTURE_DIR/ComputerFixtureApp.swift" BROWSER_FIXTURE_DIR="$FIXTURE_DIR/browser_fixture" WRITE_CAPABILITY_MANIFESTS="$ROOT_DIR/e2e/write_capability_manifests.py" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/e2e/out/cross-platform-computer-browser-demo-$(date +%Y%m%d-%H%M%S)-$$}" +OUTPUT_DIR_WAS_EXPLICIT=0 +if [[ -n "${OUTPUT_DIR+x}" ]]; then + OUTPUT_DIR_WAS_EXPLICIT=1 +else + OUTPUT_DIR="$ROOT_DIR/e2e/out/cross-platform-computer-browser-demo-$(date +%Y%m%d-%H%M%S)-$$" +fi case "$OUTPUT_DIR" in "$ROOT_DIR"/*) RUN_DIR_REL="${OUTPUT_DIR#"$ROOT_DIR/"}" ;; *) @@ -37,6 +43,7 @@ LOCAL_WORKSPACE_NODE_PORT="${LOCAL_WORKSPACE_NODE_PORT:-}" REMOTE_NODE_PORT="${REMOTE_NODE_PORT:-}" REMOTE_NODE_NAME="${REMOTE_NODE_NAME:-cross-macos-computer-browser-node}" REMOTE_EXPORT_NAME="${REMOTE_EXPORT_NAME:-macos-demo-export}" +KEEP_OUTPUT="${KEEP_OUTPUT:-}" MACOS_BROWSER_FIXTURE_PORT="${MACOS_BROWSER_FIXTURE_PORT:-}" MACOS_BROWSER_URL="" @@ -226,6 +233,7 @@ cleanup() { if [[ -f "$LINUX_HELPER" ]] && command -v orbctl >/dev/null 2>&1; then orb_run cleanup >/dev/null 2>&1 || true fi + e2e_cleanup_output_dir "$exit_code" "$OUTPUT_DIR" "$OUTPUT_DIR_WAS_EXPLICIT" "$KEEP_OUTPUT" exit "$exit_code" } trap cleanup EXIT diff --git a/e2e/test-cross-platform-node-workspace.ps1 b/e2e/test-cross-platform-node-workspace.ps1 index 134ba43..a25bbf6 100644 --- a/e2e/test-cross-platform-node-workspace.ps1 +++ b/e2e/test-cross-platform-node-workspace.ps1 @@ -7,6 +7,7 @@ $RootDir = $script:RepoRoot $RemoteFixtureDir = Join-Path $RootDir "e2e/fixtures/remote-smoke" $LinuxHelper = Join-Path $RootDir "e2e/run_linux_spiderweb_host.sh" +$OutputDirWasExplicit = Test-Path Env:OUTPUT_DIR $OutputDir = if ($env:OUTPUT_DIR) { $env:OUTPUT_DIR } else { New-RunDirectory "cross-platform-node-workspace-windows" } $OutputDir = Ensure-RunDirectoryUnderRepo $OutputDir $RunDirRel = Get-RunDirectoryRelative $OutputDir @@ -30,6 +31,7 @@ $LocalRemoteNodeLog = Join-Path $LogDir "windows-remote-node.log" $LocalRemoteNodeErrLog = Join-Path $LogDir "windows-remote-node.stderr.log" $LocalRemoteNode = $null $script:HelperEnv = $null +$script:RunSucceeded = $false function Cleanup { if ($null -ne $script:LocalRemoteNode -and -not $script:LocalRemoteNode.HasExited) { @@ -142,6 +144,9 @@ try { Write-Host " $handoffFile" Write-Host " $resultFile" Write-Host " $remoteSmokeFile" + $script:RunSucceeded = $true } finally { Cleanup + $exitCode = if ($script:RunSucceeded) { 0 } else { 1 } + Remove-RunDirectoryIfTransient -ExitCode $exitCode -Path $OutputDir -WasExplicit $OutputDirWasExplicit } diff --git a/e2e/test-cross-platform-node-workspace.sh b/e2e/test-cross-platform-node-workspace.sh index 8140dcb..d72e89f 100755 --- a/e2e/test-cross-platform-node-workspace.sh +++ b/e2e/test-cross-platform-node-workspace.sh @@ -3,10 +3,16 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "$ROOT_DIR/e2e/output-cleanup.sh" LINUX_HELPER="$ROOT_DIR/e2e/run_linux_spiderweb_host.sh" REMOTE_FIXTURE_DIR="$ROOT_DIR/e2e/fixtures/remote-smoke" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/e2e/out/cross-platform-node-workspace-$(date +%Y%m%d-%H%M%S)-$$}" +OUTPUT_DIR_WAS_EXPLICIT=0 +if [[ -n "${OUTPUT_DIR+x}" ]]; then + OUTPUT_DIR_WAS_EXPLICIT=1 +else + OUTPUT_DIR="$ROOT_DIR/e2e/out/cross-platform-node-workspace-$(date +%Y%m%d-%H%M%S)-$$" +fi case "$OUTPUT_DIR" in "$ROOT_DIR"/*) RUN_DIR_REL="${OUTPUT_DIR#"$ROOT_DIR/"}" ;; *) @@ -31,6 +37,7 @@ LOCAL_WORKSPACE_NODE_PORT="${LOCAL_WORKSPACE_NODE_PORT:-28911}" REMOTE_NODE_PORT="${REMOTE_NODE_PORT:-28912}" REMOTE_NODE_NAME="${REMOTE_NODE_NAME:-cross-macos-remote-node}" REMOTE_EXPORT_NAME="${REMOTE_EXPORT_NAME:-remote-smoke}" +KEEP_OUTPUT="${KEEP_OUTPUT:-}" LOCAL_REMOTE_NODE_LOG="$LOG_DIR/macos-remote-node.log" LOCAL_REMOTE_NODE_PID="" @@ -118,6 +125,7 @@ cleanup() { if [[ -f "$LINUX_HELPER" ]] && command -v orbctl >/dev/null 2>&1; then orb_run cleanup >/dev/null 2>&1 || true fi + e2e_cleanup_output_dir "$exit_code" "$OUTPUT_DIR" "$OUTPUT_DIR_WAS_EXPLICIT" "$KEEP_OUTPUT" exit "$exit_code" } trap cleanup EXIT diff --git a/e2e/test-macos-computer-browser-node.sh b/e2e/test-macos-computer-browser-node.sh index b4f23e2..b9472ed 100755 --- a/e2e/test-macos-computer-browser-node.sh +++ b/e2e/test-macos-computer-browser-node.sh @@ -3,13 +3,19 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "$ROOT_DIR/e2e/output-cleanup.sh" SPIDERWEB_DIR="$ROOT_DIR/Spiderweb" FIXTURE_DIR="$ROOT_DIR/e2e/fixtures/macos-computer-browser" FIXTURE_SWIFT="$FIXTURE_DIR/ComputerFixtureApp.swift" BROWSER_FIXTURE_DIR="$FIXTURE_DIR/browser_fixture" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/e2e/out/macos-computer-browser-node-$(date +%Y%m%d-%H%M%S)-$$}" +OUTPUT_DIR_WAS_EXPLICIT=0 +if [[ -n "${OUTPUT_DIR+x}" ]]; then + OUTPUT_DIR_WAS_EXPLICIT=1 +else + OUTPUT_DIR="$ROOT_DIR/e2e/out/macos-computer-browser-node-$(date +%Y%m%d-%H%M%S)-$$" +fi LOG_DIR="$OUTPUT_DIR/logs" STATE_DIR="$OUTPUT_DIR/state" ARTIFACT_DIR="$OUTPUT_DIR/artifacts" @@ -26,6 +32,7 @@ BROWSER_FIXTURE_PORT="${BROWSER_FIXTURE_PORT:-}" CONTROL_URL="" KEEP_TEMP="${KEEP_TEMP:-0}" +KEEP_OUTPUT="${KEEP_OUTPUT:-$KEEP_TEMP}" SKIP_BUILD="${SKIP_BUILD:-0}" COMPUTER_INCLUDE_SCREENSHOT="${COMPUTER_INCLUDE_SCREENSHOT:-0}" BROWSER_INCLUDE_SCREENSHOT="${BROWSER_INCLUDE_SCREENSHOT:-0}" @@ -663,9 +670,7 @@ cleanup() { kill "$SPIDERWEB_PID" >/dev/null 2>&1 || true wait "$SPIDERWEB_PID" >/dev/null 2>&1 || true fi - if [[ "$KEEP_TEMP" != "1" && -d "$OUTPUT_DIR" ]]; then - rm -rf "$OUTPUT_DIR" - fi + e2e_cleanup_output_dir "$exit_code" "$OUTPUT_DIR" "$OUTPUT_DIR_WAS_EXPLICIT" "$KEEP_OUTPUT" exit "$exit_code" } trap cleanup EXIT diff --git a/e2e/test-mcp-bridge.sh b/e2e/test-mcp-bridge.sh index 1dcbd1a..de43c5f 100644 --- a/e2e/test-mcp-bridge.sh +++ b/e2e/test-mcp-bridge.sh @@ -9,11 +9,18 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source "$ROOT_DIR/e2e/output-cleanup.sh" SPIDERWEB_DIR="$ROOT_DIR/Spiderweb" -OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/e2e/out/mcp-bridge-$(date +%Y%m%d-%H%M%S)-$$}" +OUTPUT_DIR_WAS_EXPLICIT=0 +if [[ -n "${OUTPUT_DIR+x}" ]]; then + OUTPUT_DIR_WAS_EXPLICIT=1 +else + OUTPUT_DIR="$ROOT_DIR/e2e/out/mcp-bridge-$(date +%Y%m%d-%H%M%S)-$$" +fi LOG_DIR="$OUTPUT_DIR/logs" ARTIFACT_DIR="$OUTPUT_DIR/artifacts" +KEEP_OUTPUT="${KEEP_OUTPUT:-}" MCP_BRIDGE="$SPIDERWEB_DIR/zig-out/bin/spiderweb-mcp-bridge" # On Windows the binary has an .exe extension @@ -145,6 +152,7 @@ cleanup() { if [[ -n "${FIXTURE_DIR:-}" && -d "$FIXTURE_DIR" ]]; then rm -rf "$FIXTURE_DIR" fi + e2e_cleanup_output_dir "$exit_code" "$OUTPUT_DIR" "$OUTPUT_DIR_WAS_EXPLICIT" "$KEEP_OUTPUT" exit "$exit_code" } trap cleanup EXIT diff --git a/e2e/windows-e2e-common.ps1 b/e2e/windows-e2e-common.ps1 index 0a46924..804089c 100644 --- a/e2e/windows-e2e-common.ps1 +++ b/e2e/windows-e2e-common.ps1 @@ -35,6 +35,22 @@ function New-RunDirectory { return Join-Path $script:RepoRoot ("e2e/out/{0}-{1}-{2}" -f $Prefix, $stamp, $PID) } +function Test-TruthyString { + param([AllowNull()][string]$Value) + + if ($null -eq $Value) { + return $false + } + + switch ($Value.Trim().ToLowerInvariant()) { + "1" { return $true } + "true" { return $true } + "yes" { return $true } + "on" { return $true } + default { return $false } + } +} + function Ensure-RunDirectoryUnderRepo { param([string]$Path) @@ -54,6 +70,36 @@ function Get-RunDirectoryRelative { return $full.Substring($repo.Length + 1).Replace("\", "/") } +function Remove-RunDirectoryIfTransient { + param( + [int]$ExitCode, + [string]$Path, + [bool]$WasExplicit = $false + ) + + if (-not (Test-Path -LiteralPath $Path)) { + return + } + + if (Test-TruthyString $env:KEEP_OUTPUT -or Test-TruthyString $env:KEEP_TEMP) { + Write-Info "Preserving E2E artifacts at $Path because KEEP_OUTPUT is enabled" + return + } + + if ($ExitCode -ne 0) { + Write-Info "Preserving failed E2E artifacts at $Path" + return + } + + if ($WasExplicit) { + Write-Info "Preserving E2E artifacts at $Path because OUTPUT_DIR was set explicitly" + return + } + + Remove-Item -LiteralPath $Path -Recurse -Force + Write-Info "Removed transient E2E artifacts at $Path" +} + function Wait-ForFile { param( [string]$Path, diff --git a/themes/zsc_modern_ai/layouts/workspace.json b/themes/zsc_modern_ai/layouts/workspace.json new file mode 100644 index 0000000..1c26970 --- /dev/null +++ b/themes/zsc_modern_ai/layouts/workspace.json @@ -0,0 +1,23 @@ +{ + "schema_version": 1, + "desktop": { + "open_panels": ["control", "chat", "debug"], + "focused_panel": "chat", + "close_others": false + }, + "phone": { + "open_panels": ["control"], + "focused_panel": "control", + "close_others": false + }, + "tablet": { + "open_panels": ["control", "chat"], + "focused_panel": "chat", + "close_others": false + }, + "fullscreen": { + "open_panels": ["control", "chat"], + "focused_panel": "chat", + "close_others": false + } +} diff --git a/themes/zsc_modern_ai/manifest.json b/themes/zsc_modern_ai/manifest.json new file mode 100644 index 0000000..532dc7e --- /dev/null +++ b/themes/zsc_modern_ai/manifest.json @@ -0,0 +1,17 @@ +{ + "schema_version": 1, + "id": "zsc.modern_ai", + "name": "ZSC Modern AI", + "author": "Spider / ZiggyStarClaw", + "license": "MIT", + "defaults": { + "variant": "dark", + "profile": "desktop", + "image_sampling": "linear", + "pixel_snap_textured": false + }, + "capabilities": { + "requires_multi_window": false, + "requires_custom_shaders": false + } +} diff --git a/themes/zsc_modern_ai/profiles/fullscreen.json b/themes/zsc_modern_ai/profiles/fullscreen.json new file mode 100644 index 0000000..ad983ea --- /dev/null +++ b/themes/zsc_modern_ai/profiles/fullscreen.json @@ -0,0 +1,9 @@ +{ + "profile": "fullscreen", + "overrides": { + "ui_scale": 1.24, + "typography": { "body_size": 18.0, "heading_size": 22.0, "title_size": 26.0 }, + "spacing": { "sm": 13.0, "md": 22.0, "lg": 30.0 }, + "components": { "hit_target_min_px": 58.0 } + } +} diff --git a/themes/zsc_modern_ai/profiles/phone.json b/themes/zsc_modern_ai/profiles/phone.json new file mode 100644 index 0000000..078f94c --- /dev/null +++ b/themes/zsc_modern_ai/profiles/phone.json @@ -0,0 +1,9 @@ +{ + "profile": "phone", + "overrides": { + "ui_scale": 1.12, + "typography": { "body_size": 17.0, "heading_size": 20.0 }, + "spacing": { "sm": 11.0, "md": 18.0 }, + "components": { "hit_target_min_px": 50.0 } + } +} diff --git a/themes/zsc_modern_ai/profiles/tablet.json b/themes/zsc_modern_ai/profiles/tablet.json new file mode 100644 index 0000000..1972a2f --- /dev/null +++ b/themes/zsc_modern_ai/profiles/tablet.json @@ -0,0 +1,9 @@ +{ + "profile": "tablet", + "overrides": { + "ui_scale": 1.08, + "typography": { "body_size": 16.0, "heading_size": 19.0 }, + "spacing": { "sm": 10.0, "md": 18.0 }, + "components": { "hit_target_min_px": 46.0 } + } +} diff --git a/themes/zsc_modern_ai/styles/components.json b/themes/zsc_modern_ai/styles/components.json new file mode 100644 index 0000000..2456d97 --- /dev/null +++ b/themes/zsc_modern_ai/styles/components.json @@ -0,0 +1,306 @@ +{ + "surfaces": { + "background": { + "gradient4": { + "tl": "#101A28FF", + "tr": "#0A1220FF", + "bl": "#08111BFF", + "br": "#0C1420FF" + } + }, + "surface": "colors.surface", + "menu_bar": { + "gradient4": { + "tl": "#122033F7", + "tr": "#0B1728F7", + "bl": "#0A1322F7", + "br": "#0E1725F7" + } + }, + "status_bar": { + "gradient4": { + "tl": "#0B1320F4", + "tr": "#0B1320F4", + "bl": "#0E1827FC", + "br": "#0E1827FC" + } + } + }, + "button": { + "primary": { + "radius": "radius.full", + "fill": { + "gradient4": { + "tl": "#4CC5FFFF", + "tr": "#3E8FFFFF", + "bl": "#2AA8F2FF", + "br": "#3457FFFF" + } + }, + "text": "#F7FBFFFF", + "border": "#00000000", + "states": { + "hover": { + "fill": { + "gradient4": { + "tl": "#6FD7FFFF", + "tr": "#57A4FFFF", + "bl": "#39B5FFFF", + "br": "#476BFFFF" + } + } + }, + "pressed": { + "fill": { + "gradient4": { + "tl": "#2EA4EEFF", + "tr": "#2C7BE4FF", + "bl": "#218FD0FF", + "br": "#274BDBFF" + } + } + }, + "disabled": { + "fill": "#2A3A55A8", + "text": "#B9C7D880" + } + } + }, + "secondary": { + "radius": "radius.full", + "fill": "#0F1A2ACC", + "text": "colors.text_primary", + "border": "#2B3E5DFF", + "states": { + "hover": { + "fill": "#132338E6", + "border": "#4575B8FF" + }, + "pressed": { + "fill": "#162B43F0", + "border": "#5BC6FFFF" + } + } + }, + "ghost": { + "radius": "radius.full", + "fill": "#00000000", + "text": "colors.primary", + "border": "#00000000", + "states": { + "hover": { + "fill": "#53C9FF1A", + "border": "#3B6D97AA" + }, + "pressed": { + "fill": "#53C9FF2E" + } + } + } + }, + "checkbox": { + "radius": "radius.sm", + "fill": "#0D1623F2", + "border": "#314867FF", + "fill_checked": "colors.primary", + "border_checked": "#64D6FFFF", + "check": "#F6FCFFFF", + "states": { + "hover": { + "border": "#4A6E9DFF" + }, + "focused": { + "border": "#64D6FFFF" + } + } + }, + "text_input": { + "radius": "radius.md", + "fill": "#0B1523EB", + "border": "#263854FF", + "text": "colors.text_primary", + "placeholder": "colors.text_secondary", + "selection": "#53C9FF3A", + "caret": "#EAF6FFFF", + "states": { + "hover": { + "border": "#3E628DFF" + }, + "focused": { + "border": "#5FD4FFFF", + "fill": "#0D192AF4" + }, + "read_only": { + "fill": "#0A1320CC" + }, + "disabled": { + "fill": "#0A1320A6", + "border": "#22324899", + "text": "#AEBBCD88" + } + } + }, + "panel": { + "radius": "radius.lg", + "fill": { + "gradient4": { + "tl": "#0F1825F3", + "tr": "#0D1724F3", + "bl": "#101A27FC", + "br": "#101A27FC" + } + }, + "border": "#233246FF", + "header_overlay": { + "gradient4": { + "tl": "#1A2B45CC", + "tr": "#12243BCC", + "bl": "#0A142200", + "br": "#0A142200" + } + }, + "focus_border": "#64D6FFFF" + }, + "menu": { + "item": { + "radius": "radius.md", + "fill": "#00000000", + "text": "colors.text_primary", + "border": "#00000000", + "states": { + "hover": { + "fill": "#53C9FF14", + "border": "#29486B88" + }, + "selected": { + "fill": "#53C9FF1F", + "border": "#3B71AA99" + }, + "selected_hover": { + "fill": "#53C9FF2C", + "border": "#4B96DBBB" + } + } + } + }, + "tabs": { + "radius": "radius.md", + "fill": "#00000000", + "text": "colors.text_secondary", + "border": "#00000000", + "underline": "#53C9FF00", + "underline_thickness": 2.0, + "states": { + "hover": { + "fill": "#53C9FF12", + "text": "colors.text_primary" + }, + "active": { + "fill": "#53C9FF18", + "text": "colors.text_primary", + "underline": "#5FD4FFFF" + }, + "active_hover": { + "fill": "#53C9FF24", + "text": "#F7FBFFFF", + "underline": "#7BE3FFFF" + } + } + }, + "shell": { + "menu_bar_fill": { + "gradient4": { + "tl": "#122033F7", + "tr": "#0B1728F7", + "bl": "#0A1322F7", + "br": "#0E1725F7" + } + }, + "status_bar_fill": { + "gradient4": { + "tl": "#0B1320F4", + "tr": "#0B1320F4", + "bl": "#0E1827FC", + "br": "#0E1827FC" + } + }, + "status_bar_border": "#203047FF", + "dock_fill": "#0B1522F4", + "dock_border": "#22344DFF", + "panel_header_fill": "#12253BCC", + "sidebar_fill": "#0A1420FA" + }, + "status": { + "neutral": { + "fill": "#5E7EA22A", + "border": "#45627FFF", + "text": "colors.text_secondary" + }, + "info": { + "fill": "#45AFFFF1", + "border": "#6AD1FFFF", + "text": "#EAF9FFFF" + }, + "success": { + "fill": "#46D69A22", + "border": "#57DFABFF", + "text": "#E8FFF5FF" + }, + "warning": { + "fill": "#F9B24A24", + "border": "#FFC56BFF", + "text": "#FFF6E6FF" + }, + "danger": { + "fill": "#FF706B22", + "border": "#FF8C82FF", + "text": "#FFF1EFFF" + } + }, + "scrollbar": { + "track": "#13213288", + "thumb": "#2E4667FF", + "thumb_hover": "#4C78ABFF", + "thumb_active": "#62D7FFFF", + "border": "#091320AA", + "radius": "radius.full" + }, + "list_row": { + "hover_fill": "#53C9FF10", + "hover_border": "#25415F88", + "selected_fill": "#53C9FF1E", + "selected_hover_fill": "#53C9FF2A", + "selected_border": "#54CFFFFF", + "selected_text": "#F6FBFFFF" + }, + "syntax": { + "key": "#77CDFFFF", + "string": "#7CE5B0FF", + "number": "#FFBE69FF", + "keyword": "#A89CFFFF", + "punctuation": "colors.text_secondary", + "comment": "#70829AFF", + "plain": "colors.text_primary" + }, + "charts": { + "series_1": "#66BDFFFF", + "series_2": "#FFB86CFF", + "series_3": "#AD93FFFF", + "series_4": "#68E3A5FF", + "series_5": "#F67CC6FF", + "series_6": "#F4E082FF", + "fill_alpha": 0.22, + "background": "#0A1320F6", + "border": "#223349FF" + }, + "focus_ring": { + "thickness": 2.0, + "color": "#62D7FFFF", + "glow": { + "color": "#3BC5FF88", + "blur_px": 16.0, + "spread_px": 2.0, + "steps": 5 + } + } +} diff --git a/themes/zsc_modern_ai/tokens/base.json b/themes/zsc_modern_ai/tokens/base.json new file mode 100644 index 0000000..2ee5935 --- /dev/null +++ b/themes/zsc_modern_ai/tokens/base.json @@ -0,0 +1,28 @@ +{ + "colors": { + "background": [0.056, 0.071, 0.094, 1.0], + "surface": [0.086, 0.110, 0.145, 1.0], + "primary": [0.365, 0.789, 1.000, 1.0], + "success": [0.314, 0.860, 0.623, 1.0], + "danger": [0.996, 0.439, 0.431, 1.0], + "warning": [0.992, 0.742, 0.333, 1.0], + "text_primary": [0.938, 0.956, 0.982, 1.0], + "text_secondary": [0.620, 0.690, 0.792, 1.0], + "border": [0.170, 0.224, 0.306, 1.0], + "divider": [0.124, 0.165, 0.227, 1.0] + }, + "typography": { + "font_family": "Space Grotesk", + "title_size": 22.0, + "heading_size": 18.0, + "body_size": 15.0, + "caption_size": 12.0 + }, + "spacing": { "xs": 4.0, "sm": 8.0, "md": 16.0, "lg": 24.0, "xl": 32.0 }, + "radius": { "sm": 8.0, "md": 12.0, "lg": 18.0, "full": 9999.0 }, + "shadows": { + "sm": { "blur": 4.0, "spread": 0.0, "offset_x": 0.0, "offset_y": 1.0 }, + "md": { "blur": 12.0, "spread": 0.0, "offset_x": 0.0, "offset_y": 4.0 }, + "lg": { "blur": 28.0, "spread": 0.0, "offset_x": 0.0, "offset_y": 10.0 } + } +} diff --git a/themes/zsc_modern_ai/tokens/dark.json b/themes/zsc_modern_ai/tokens/dark.json new file mode 100644 index 0000000..b99448f --- /dev/null +++ b/themes/zsc_modern_ai/tokens/dark.json @@ -0,0 +1,10 @@ +{ + "colors": { + "background": [0.045, 0.059, 0.078, 1.0], + "surface": [0.071, 0.094, 0.125, 1.0], + "text_primary": [0.945, 0.962, 0.988, 1.0], + "text_secondary": [0.600, 0.675, 0.780, 1.0], + "border": [0.155, 0.212, 0.294, 1.0], + "divider": [0.110, 0.153, 0.212, 1.0] + } +} diff --git a/themes/zsc_modern_ai/tokens/light.json b/themes/zsc_modern_ai/tokens/light.json new file mode 100644 index 0000000..ddd1a9f --- /dev/null +++ b/themes/zsc_modern_ai/tokens/light.json @@ -0,0 +1,14 @@ +{ + "colors": { + "background": [0.951, 0.967, 0.984, 1.0], + "surface": [0.982, 0.989, 0.998, 1.0], + "primary": [0.180, 0.520, 0.940, 1.0], + "success": [0.122, 0.608, 0.416, 1.0], + "danger": [0.827, 0.224, 0.275, 1.0], + "warning": [0.855, 0.545, 0.102, 1.0], + "text_primary": [0.082, 0.110, 0.153, 1.0], + "text_secondary": [0.365, 0.435, 0.525, 1.0], + "border": [0.776, 0.843, 0.918, 1.0], + "divider": [0.847, 0.894, 0.953, 1.0] + } +}