diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c6a2a251..8376f9bdca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## [Unreleased] +### Added +- New **Zeus** appearance skin (opt-in, dark-only): OLED-near-black surfaces (`#0F0F0F`–`#181818`) paired with the default gold accent and gold-tinted borders/focus rings. Selectable via Settings → Appearance → Skin or `/theme skin:zeus`. Fills a niche distinct from Geist Contrast (which redefines the accent) and Nous (steel-blue accent) by preserving the default gold while replacing the navy-tinted surfaces with warm near-blacks. + ## [v0.51.195] — 2026-06-01 — Release FO (stage-batch7 — hide attachment path markers in chat UI) ### Fixed diff --git a/THEMES.md b/THEMES.md index abb93270fc..70a30db9c1 100644 --- a/THEMES.md +++ b/THEMES.md @@ -58,9 +58,12 @@ absent for light. System mode tracks the OS preference at runtime. | **Catppuccin** | Catppuccin Latte/Mocha palette with Mauve accent. | | **Nous** | Steel-blue accent with dashed technical surfaces. | | **Geist Contrast** (`geist-contrast`) | Geist-inspired monochrome surfaces with a restrained dark-mode `#FFF175` accent. | +| **Zeus** | OLED-near-black surfaces with the default gold accent. Deep and warm. Defines dark overrides only; light mode falls back to the default skin. | -Each skin defines paired light + dark variants so it reads cleanly on either -theme. The skin is applied as `data-skin=""` on `` (the default +Most skins define paired light + dark variants so they read cleanly on either +theme. Dark-only skins (e.g. **Zeus**) intentionally omit a light variant; in +light mode the default skin takes over. +The skin is applied as `data-skin=""` on `` (the default skin clears the attribute). --- diff --git a/api/config.py b/api/config.py index c0ca41fe30..bc1c9b1a53 100644 --- a/api/config.py +++ b/api/config.py @@ -4850,6 +4850,7 @@ def _get_session_agent_lock(session_id: str) -> threading.Lock: "catppuccin", "nous", "geist-contrast", + "zeus", } _SETTINGS_LEGACY_THEME_MAP = { # Legacy full themes now map onto the closest supported theme + accent skin pair. diff --git a/docs/pr-media/zeus/zeus-convo.png b/docs/pr-media/zeus/zeus-convo.png new file mode 100644 index 0000000000..8a4cd31132 Binary files /dev/null and b/docs/pr-media/zeus/zeus-convo.png differ diff --git a/docs/pr-media/zeus/zeus-settings.png b/docs/pr-media/zeus/zeus-settings.png new file mode 100644 index 0000000000..5bc949d2b2 Binary files /dev/null and b/docs/pr-media/zeus/zeus-settings.png differ diff --git a/static/boot.js b/static/boot.js index 034dc82c6c..eff9fb4189 100644 --- a/static/boot.js +++ b/static/boot.js @@ -1413,6 +1413,7 @@ const _SKINS=[ {name:'Nous', colors:['#4682B4','#3A6E9A','#2C5F88']}, {name:'Neon', colors:['#B347FF','#C76BFF','#00DDFF']}, {name:'Geist Contrast', value:'geist-contrast', colors:['#000000','#ffffff','#FFF175']}, + {name:'Zeus', colors:['#FFD700','#FFBF00','#1A1A00']}, ]; const _VALID_THEMES=new Set((_THEMES||[]).map(t=>t.value)); const _VALID_SKINS=new Set((_SKINS||[]).map(s=>(s.value||s.name).toLowerCase())); diff --git a/static/i18n.js b/static/i18n.js index 2103ea6cae..2b0e4498c3 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -202,7 +202,7 @@ const LOCALES = { cmd_terminal: 'Open the workspace terminal', cmd_new: 'Start a new chat session', cmd_usage: 'Toggle token usage display on/off', - cmd_theme: 'Switch appearance (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Switch appearance (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Switch agent personality', cmd_skills: 'List available Hermes skills', available_commands: 'Available commands:', @@ -1522,7 +1522,7 @@ const LOCALES = { cmd_terminal: 'Apri il terminale del workspace', cmd_new: 'Avvia una nuova sessione di chat', cmd_usage: 'Attiva/disattiva visualizzazione uso token', - cmd_theme: 'Cambia aspetto (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Cambia aspetto (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: "Cambia personalità dell'agente", cmd_skills: 'Elenca le skill Hermes disponibili', available_commands: 'Comandi disponibili:', @@ -2834,7 +2834,7 @@ const LOCALES = { cmd_terminal: 'ワークスペースのターミナルを開く', cmd_new: '新しいチャットセッションを開始', cmd_usage: 'トークン使用量表示の ON/OFF を切り替え', - cmd_theme: '外観を切り替え (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: '外観を切り替え (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'エージェントのパーソナリティを切り替え', cmd_skills: '利用可能な Hermes スキルを一覧表示', available_commands: '利用可能なコマンド:', @@ -4108,7 +4108,7 @@ const LOCALES = { cmd_terminal: 'Открыть терминал рабочей области', cmd_new: 'Начать новую сессию чата', cmd_usage: 'Показать или скрыть использование токенов', - cmd_theme: 'Переключить внешний вид (тема: system/dark/light, скин: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Переключить внешний вид (тема: system/dark/light, скин: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Переключить личность агента', cmd_skills: 'Показать доступные навыки Hermes', available_commands: 'Доступные команды:', @@ -5391,7 +5391,7 @@ const LOCALES = { cmd_terminal: 'Abrir terminal del espacio de trabajo', cmd_new: 'Iniciar una nueva sesión de chat', cmd_usage: 'Activar o desactivar el uso de tokens', - cmd_theme: 'Cambiar apariencia (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Cambiar apariencia (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Cambiar la personalidad del agente', cmd_skills: 'Listar las skills de Hermes disponibles', available_commands: 'Comandos disponibles:', @@ -6615,7 +6615,7 @@ const LOCALES = { cmd_terminal: 'Workspace-Terminal öffnen', cmd_new: 'Neue Chat-Sitzung starten', cmd_usage: 'Token-Verbrauchsanzeige umschalten', - cmd_theme: 'Darstellung wechseln (Theme: system/dark/light, Skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Darstellung wechseln (Theme: system/dark/light, Skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Agenten-Persönlichkeit wechseln', cmd_skills: 'Verfügbare Hermes-Skills auflisten', available_commands: 'Verfügbare Befehle:', @@ -10388,7 +10388,7 @@ const LOCALES = { cmd_workspace: 'Trocar workspace por nome', cmd_new: 'Iniciar nova sessão de chat', cmd_usage: 'Alternar exibição de uso de tokens', - cmd_theme: 'Trocar aparência (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Trocar aparência (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Trocar personalidade do agente', cmd_skills: 'Listar skills disponíveis do Hermes', available_commands: 'Comandos disponíveis:', @@ -11586,7 +11586,7 @@ const LOCALES = { cmd_terminal: '워크스페이스 터미널 열기', cmd_new: '새 채팅 세션 시작', cmd_usage: '토큰 사용량 표시 켜기/끄기', - cmd_theme: 'Switch appearance (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Switch appearance (theme: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Switch agent personality', cmd_skills: 'List available Hermes skills', available_commands: '사용 가능한 명령:', @@ -12891,7 +12891,7 @@ const LOCALES = { cmd_terminal: 'Ouvrez le terminal de l\'espace de travail', cmd_new: 'Démarrer une nouvelle session de discussion', cmd_usage: 'Activer/désactiver l\'affichage de l\'utilisation du jeton', - cmd_theme: 'Changer d\'apparence (thème : system/dark/light, skin : default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Changer d\'apparence (thème : system/dark/light, skin : default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Personnalité de l\'agent de commutation', cmd_skills: 'Lister les compétences Hermès disponibles', available_commands: 'Commandes disponibles :', @@ -14148,7 +14148,7 @@ const LOCALES = { cmd_terminal: 'Çalışma alanı terminalini açın', cmd_new: 'Yeni bir sohbet oturumu başlatın', cmd_usage: 'Token kullanımı ekranını aç/kapat', - cmd_theme: 'Görünümü değiştir (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast)', + cmd_theme: 'Görünümü değiştir (tema: system/dark/light, skin: default/ares/mono/slate/poseidon/sisyphus/charizard/sienna/catppuccin/nous/geist-contrast/zeus)', cmd_personality: 'Temsilci kişiliğini değiştir', cmd_skills: 'Mevcut Hermes becerilerini listele', available_commands: 'Mevcut komutlar:', diff --git a/static/index.html b/static/index.html index 32efe8583e..8535af0938 100644 --- a/static/index.html +++ b/static/index.html @@ -17,7 +17,7 @@ - + diff --git a/static/style.css b/static/style.css index d755b4353c..e160a6fef3 100644 --- a/static/style.css +++ b/static/style.css @@ -598,6 +598,33 @@ :root[data-skin="geist-contrast"] textarea#msg{scrollbar-width:none;overflow-y:auto;} :root[data-skin="geist-contrast"] textarea#msg::-webkit-scrollbar{display:none;} + /* ── Skin: Zeus ── + OLED-near-black skin: replaces the default dark-blue/navy surfaces with warm + near-blacks while keeping the gold accent unchanged. Borders are gold-tinted + for warmth so the accent feels connected to the chrome. + Dark-only — no light-mode variant. */ + :root.dark[data-skin="zeus"]{ + --bg:#0F0F0F;--sidebar:#111111;--surface:#181818;--code-bg:#181818; + --topbar-bg:rgba(15,15,15,.98);--main-bg:rgba(15,15,15,0.6); + --border:#2A2A1E;--border2:rgba(255,215,0,0.18); + --input-bg:rgba(255,255,255,.04);--hover-bg:rgba(255,215,0,.06); + --surface-subtle:rgba(255,255,255,.03);--surface-subtle-hover:rgba(255,255,255,.05); + --border-subtle:rgba(255,215,0,.08);--border-muted:rgba(255,215,0,.14); + --focus-ring:rgba(255,215,0,.4);--focus-glow:rgba(255,215,0,.1); + } + :root.dark[data-skin="zeus"] .topbar{background:#0F0F0F;border-bottom:1px solid rgba(255,215,0,0.15);} + :root.dark[data-skin="zeus"] .sidebar{background:#111111;border-right:1px solid rgba(255,215,0,0.12);} + :root.dark[data-skin="zeus"] .session-item.active{background:rgba(255,215,0,0.1);border-left:2px solid #FFD700;color:#FFD700;} + :root.dark[data-skin="zeus"] .session-item:hover:not(.active){background:rgba(255,215,0,0.05);} + :root.dark[data-skin="zeus"] .composer-box{background:#141414;border-color:rgba(255,215,0,0.2);} + :root.dark[data-skin="zeus"] .msg-row[data-role="assistant"] .msg-body{background:#181818;border-color:rgba(255,215,0,0.1);} + :root.dark[data-skin="zeus"] .msg-row[data-role="user"] .msg-body{background:#1C1600;border-color:rgba(255,215,0,0.25);} + :root.dark[data-skin="zeus"] .app-dialog{background:linear-gradient(180deg,rgba(24,24,24,.99),rgba(15,15,15,.99));border-color:rgba(255,215,0,0.2);} + :root.dark[data-skin="zeus"] .app-dialog-overlay{background:rgba(0,0,0,.72);} + :root.dark[data-skin="zeus"] .kanban-modal{background:linear-gradient(180deg,rgba(24,24,24,.99),rgba(15,15,15,.99));border-color:rgba(255,215,0,0.2);} + :root.dark[data-skin="zeus"] .kanban-modal-overlay{background:rgba(0,0,0,.72);} + :root.dark[data-skin="zeus"] .kanban-board-switcher-menu{background:linear-gradient(180deg,rgba(24,24,24,.99),rgba(15,15,15,.99));border-color:rgba(255,215,0,0.15);} + /* #594: app-dialog light mode overrides — base styles use hardcoded dark gradients */ :root:not(.dark) .app-dialog{ background:linear-gradient(180deg,rgba(240,237,232,.99),rgba(228,224,216,.99)); diff --git a/tests/test_zeus_skin.py b/tests/test_zeus_skin.py new file mode 100644 index 0000000000..7d06445740 --- /dev/null +++ b/tests/test_zeus_skin.py @@ -0,0 +1,49 @@ +"""Zeus skin registration and dark-surface affordances.""" + +from pathlib import Path + +REPO = Path(__file__).parent.parent +CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8") +BOOT_JS = (REPO / "static" / "boot.js").read_text(encoding="utf-8") +CONFIG_PY = (REPO / "api" / "config.py").read_text(encoding="utf-8") +INDEX_HTML = (REPO / "static" / "index.html").read_text(encoding="utf-8") +I18N_JS = (REPO / "static" / "i18n.js").read_text(encoding="utf-8") + + +def test_zeus_skin_is_registered_in_all_files(): + assert "{name:'Zeus'" in BOOT_JS + assert "zeus:1" in INDEX_HTML + assert '"zeus"' in CONFIG_PY + + +def test_zeus_dark_surfaces_are_near_black(): + assert ':root.dark[data-skin="zeus"]' in CSS + assert "--bg:#0F0F0F" in CSS + assert "--sidebar:#111111" in CSS + assert "--surface:#181818" in CSS + + +def test_zeus_preserves_default_gold_accent(): + # Zeus does NOT redefine --accent; it stays with the default gold. + # Verify gold-tinted border/focus vars are present instead. + assert "--border2:rgba(255,215,0,0.18)" in CSS + assert "--focus-ring:rgba(255,215,0,.4)" in CSS + assert "--hover-bg:rgba(255,215,0,.06)" in CSS + + +def test_zeus_active_session_uses_gold_highlight(): + assert ':root.dark[data-skin="zeus"] .session-item.active' in CSS + assert "border-left:2px solid #FFD700" in CSS + + +def test_zeus_modals_are_not_navy(): + # Modals/dialogs default to a hardcoded navy gradient — Zeus must override + assert ':root.dark[data-skin="zeus"] .app-dialog' in CSS + assert ':root.dark[data-skin="zeus"] .kanban-modal' in CSS + assert "rgba(24,24,24,.99)" in CSS + + +def test_zeus_i18n_lists_skin_in_all_locales(): + # Zeus is the last skin in each locale's cmd_theme string, so it appears + # as `…/zeus)` rather than `/zeus/`. There are 10 locales. + assert I18N_JS.count("zeus)") == 10