mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix: make CLI status bar skin-aware
Route prompt_toolkit status bar colors through the skin engine so /skin updates the status bar alongside the rest of the interactive TUI. Add regression coverage for the new status bar style override keys and CLI style composition.
This commit is contained in:
parent
de849c410d
commit
c323217188
3 changed files with 268 additions and 8 deletions
|
|
@ -30,6 +30,14 @@ All fields are optional. Missing values inherit from the ``default`` skin.
|
|||
prompt: "#FFF8DC" # Prompt text color
|
||||
input_rule: "#CD7F32" # Input area horizontal rule
|
||||
response_border: "#FFD700" # Response box border (ANSI)
|
||||
status_bar_bg: "#1a1a2e" # Status bar background
|
||||
status_bar_text: "#C0C0C0" # Status bar default text
|
||||
status_bar_strong: "#FFD700" # Status bar highlighted text
|
||||
status_bar_dim: "#8B8682" # Status bar separators/muted text
|
||||
status_bar_good: "#8FBC8F" # Healthy context usage
|
||||
status_bar_warn: "#FFD700" # Warning context usage
|
||||
status_bar_bad: "#FF8C00" # High context usage
|
||||
status_bar_critical: "#FF6B6B" # Critical context usage
|
||||
session_label: "#DAA520" # Session label color
|
||||
session_border: "#8B8682" # Session ID dim color
|
||||
status_bar_bg: "#1a1a2e" # TUI status/usage bar background
|
||||
|
|
@ -170,9 +178,40 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#FFF8DC",
|
||||
"input_rule": "#CD7F32",
|
||||
"response_border": "#FFD700",
|
||||
"status_bar_bg": "#1a1a2e",
|
||||
"status_bar_text": "#C0C0C0",
|
||||
"status_bar_strong": "#FFD700",
|
||||
"status_bar_dim": "#8B8682",
|
||||
"status_bar_good": "#8FBC8F",
|
||||
"status_bar_warn": "#FFD700",
|
||||
"status_bar_bad": "#FF8C00",
|
||||
"status_bar_critical": "#FF6B6B",
|
||||
"session_label": "#DAA520",
|
||||
"session_border": "#8B8682",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#7A5A00",
|
||||
"banner_title": "#6B4C00",
|
||||
"banner_accent": "#7A5500",
|
||||
"banner_dim": "#8B7355",
|
||||
"banner_text": "#3D2B00",
|
||||
"prompt": "#3D2B00",
|
||||
"ui_accent": "#7A5500",
|
||||
"ui_label": "#01579B",
|
||||
"ui_ok": "#1B5E20",
|
||||
"input_rule": "#7A5A00",
|
||||
"response_border": "#6B4C00",
|
||||
"status_bar_bg": "#F2EEE3",
|
||||
"status_bar_text": "#5C4300",
|
||||
"status_bar_strong": "#6B4C00",
|
||||
"status_bar_dim": "#8B7355",
|
||||
"status_bar_good": "#1B5E20",
|
||||
"status_bar_warn": "#7A5500",
|
||||
"status_bar_bad": "#B85C00",
|
||||
"status_bar_critical": "#B3261E",
|
||||
"session_label": "#5C4300",
|
||||
"session_border": "#8B7355",
|
||||
},
|
||||
"spinner": {
|
||||
# Empty = use hardcoded defaults in display.py
|
||||
},
|
||||
|
|
@ -203,9 +242,40 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#F1E6CF",
|
||||
"input_rule": "#9F1C1C",
|
||||
"response_border": "#C7A96B",
|
||||
"status_bar_bg": "#2A1212",
|
||||
"status_bar_text": "#F1E6CF",
|
||||
"status_bar_strong": "#C7A96B",
|
||||
"status_bar_dim": "#6E584B",
|
||||
"status_bar_good": "#7BC96F",
|
||||
"status_bar_warn": "#C7A96B",
|
||||
"status_bar_bad": "#DD4A3A",
|
||||
"status_bar_critical": "#EF5350",
|
||||
"session_label": "#C7A96B",
|
||||
"session_border": "#6E584B",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#6B1010",
|
||||
"banner_title": "#5C4300",
|
||||
"banner_accent": "#8B1A1A",
|
||||
"banner_dim": "#5C4030",
|
||||
"banner_text": "#3A1800",
|
||||
"prompt": "#3A1800",
|
||||
"ui_accent": "#8B1A1A",
|
||||
"ui_label": "#5C4300",
|
||||
"ui_ok": "#1B5E20",
|
||||
"input_rule": "#6B1010",
|
||||
"response_border": "#7A1515",
|
||||
"status_bar_bg": "#F6E7DB",
|
||||
"status_bar_text": "#5C4300",
|
||||
"status_bar_strong": "#7A1515",
|
||||
"status_bar_dim": "#8A6F5A",
|
||||
"status_bar_good": "#1B5E20",
|
||||
"status_bar_warn": "#8B4000",
|
||||
"status_bar_bad": "#8B1A1A",
|
||||
"status_bar_critical": "#B3261E",
|
||||
"session_label": "#5C4300",
|
||||
"session_border": "#5C4A3A",
|
||||
},
|
||||
"spinner": {
|
||||
"waiting_faces": ["(⚔)", "(⛨)", "(▲)", "(<>)", "(/)"],
|
||||
"thinking_faces": ["(⚔)", "(⛨)", "(▲)", "(⌁)", "(<>)"],
|
||||
|
|
@ -267,9 +337,41 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#c9d1d9",
|
||||
"input_rule": "#444444",
|
||||
"response_border": "#aaaaaa",
|
||||
"status_bar_bg": "#1F1F1F",
|
||||
"status_bar_text": "#C9D1D9",
|
||||
"status_bar_strong": "#E6EDF3",
|
||||
"status_bar_dim": "#777777",
|
||||
"status_bar_good": "#B5B5B5",
|
||||
"status_bar_warn": "#AAAAAA",
|
||||
"status_bar_bad": "#D0D0D0",
|
||||
"status_bar_critical": "#F0F0F0",
|
||||
"session_label": "#888888",
|
||||
"session_border": "#555555",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#333333",
|
||||
"banner_title": "#222222",
|
||||
"banner_accent": "#333333",
|
||||
"banner_dim": "#555555",
|
||||
"banner_text": "#333333",
|
||||
"prompt": "#222222",
|
||||
"ui_accent": "#333333",
|
||||
"ui_label": "#444444",
|
||||
"ui_ok": "#444444",
|
||||
"ui_error": "#333333",
|
||||
"input_rule": "#333333",
|
||||
"response_border": "#444444",
|
||||
"status_bar_bg": "#EEEEEE",
|
||||
"status_bar_text": "#333333",
|
||||
"status_bar_strong": "#222222",
|
||||
"status_bar_dim": "#666666",
|
||||
"status_bar_good": "#444444",
|
||||
"status_bar_warn": "#555555",
|
||||
"status_bar_bad": "#333333",
|
||||
"status_bar_critical": "#111111",
|
||||
"session_label": "#444444",
|
||||
"session_border": "#666666",
|
||||
},
|
||||
"spinner": {},
|
||||
"branding": {
|
||||
"agent_name": "Hermes Agent",
|
||||
|
|
@ -298,9 +400,40 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#c9d1d9",
|
||||
"input_rule": "#4169e1",
|
||||
"response_border": "#7eb8f6",
|
||||
"status_bar_bg": "#151C2F",
|
||||
"status_bar_text": "#C9D1D9",
|
||||
"status_bar_strong": "#7EB8F6",
|
||||
"status_bar_dim": "#4B5563",
|
||||
"status_bar_good": "#63D0A6",
|
||||
"status_bar_warn": "#E6A855",
|
||||
"status_bar_bad": "#F7A072",
|
||||
"status_bar_critical": "#FF7A7A",
|
||||
"session_label": "#7eb8f6",
|
||||
"session_border": "#4b5563",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#1A3A7A",
|
||||
"banner_title": "#1A3570",
|
||||
"banner_accent": "#1E4090",
|
||||
"banner_dim": "#3B4555",
|
||||
"banner_text": "#1A2A50",
|
||||
"prompt": "#1A2A50",
|
||||
"ui_accent": "#1A3570",
|
||||
"ui_label": "#1E3A80",
|
||||
"ui_ok": "#1B5E20",
|
||||
"input_rule": "#1A3A7A",
|
||||
"response_border": "#2A4FA0",
|
||||
"status_bar_bg": "#E9EEF8",
|
||||
"status_bar_text": "#1A2A50",
|
||||
"status_bar_strong": "#1A3570",
|
||||
"status_bar_dim": "#5A6070",
|
||||
"status_bar_good": "#1B5E20",
|
||||
"status_bar_warn": "#8B5E00",
|
||||
"status_bar_bad": "#B85C38",
|
||||
"status_bar_critical": "#B3261E",
|
||||
"session_label": "#1A3570",
|
||||
"session_border": "#5A6070",
|
||||
},
|
||||
"spinner": {},
|
||||
"branding": {
|
||||
"agent_name": "Hermes Agent",
|
||||
|
|
@ -403,9 +536,40 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#EAF7FF",
|
||||
"input_rule": "#2A6FB9",
|
||||
"response_border": "#5DB8F5",
|
||||
"status_bar_bg": "#0F2440",
|
||||
"status_bar_text": "#EAF7FF",
|
||||
"status_bar_strong": "#A9DFFF",
|
||||
"status_bar_dim": "#496884",
|
||||
"status_bar_good": "#6ED7B0",
|
||||
"status_bar_warn": "#5DB8F5",
|
||||
"status_bar_bad": "#2A6FB9",
|
||||
"status_bar_critical": "#D94F4F",
|
||||
"session_label": "#A9DFFF",
|
||||
"session_border": "#496884",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#0D3060",
|
||||
"banner_title": "#0D3060",
|
||||
"banner_accent": "#154080",
|
||||
"banner_dim": "#2A4565",
|
||||
"banner_text": "#0A2850",
|
||||
"prompt": "#0A2850",
|
||||
"ui_accent": "#0D3060",
|
||||
"ui_label": "#0D3060",
|
||||
"ui_ok": "#1B5E20",
|
||||
"input_rule": "#0D3060",
|
||||
"response_border": "#1A5090",
|
||||
"status_bar_bg": "#E6F4FF",
|
||||
"status_bar_text": "#0A2850",
|
||||
"status_bar_strong": "#0D3060",
|
||||
"status_bar_dim": "#3A5575",
|
||||
"status_bar_good": "#1B5E20",
|
||||
"status_bar_warn": "#1A5090",
|
||||
"status_bar_bad": "#154080",
|
||||
"status_bar_critical": "#B3261E",
|
||||
"session_label": "#0D3060",
|
||||
"session_border": "#3A5575",
|
||||
},
|
||||
"spinner": {
|
||||
"waiting_faces": ["(≈)", "(Ψ)", "(∿)", "(◌)", "(◠)"],
|
||||
"thinking_faces": ["(Ψ)", "(∿)", "(≈)", "(⌁)", "(◌)"],
|
||||
|
|
@ -467,9 +631,42 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#F5F5F5",
|
||||
"input_rule": "#656565",
|
||||
"response_border": "#B7B7B7",
|
||||
"status_bar_bg": "#202020",
|
||||
"status_bar_text": "#D3D3D3",
|
||||
"status_bar_strong": "#F5F5F5",
|
||||
"status_bar_dim": "#656565",
|
||||
"status_bar_good": "#B7B7B7",
|
||||
"status_bar_warn": "#D3D3D3",
|
||||
"status_bar_bad": "#E7E7E7",
|
||||
"status_bar_critical": "#F5F5F5",
|
||||
"session_label": "#919191",
|
||||
"session_border": "#656565",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#666666",
|
||||
"banner_title": "#222222",
|
||||
"banner_accent": "#333333",
|
||||
"banner_dim": "#555555",
|
||||
"banner_text": "#333333",
|
||||
"prompt": "#222222",
|
||||
"ui_accent": "#333333",
|
||||
"ui_label": "#444444",
|
||||
"ui_ok": "#444444",
|
||||
"ui_error": "#333333",
|
||||
"ui_warn": "#444444",
|
||||
"input_rule": "#666666",
|
||||
"response_border": "#555555",
|
||||
"status_bar_bg": "#F0F0F0",
|
||||
"status_bar_text": "#333333",
|
||||
"status_bar_strong": "#222222",
|
||||
"status_bar_dim": "#777777",
|
||||
"status_bar_good": "#444444",
|
||||
"status_bar_warn": "#555555",
|
||||
"status_bar_bad": "#333333",
|
||||
"status_bar_critical": "#111111",
|
||||
"session_label": "#444444",
|
||||
"session_border": "#777777",
|
||||
},
|
||||
"spinner": {
|
||||
"waiting_faces": ["(◉)", "(◌)", "(◬)", "(⬤)", "(::)"],
|
||||
"thinking_faces": ["(◉)", "(◬)", "(◌)", "(○)", "(●)"],
|
||||
|
|
@ -532,9 +729,40 @@ _BUILTIN_SKINS: Dict[str, Dict[str, Any]] = {
|
|||
"prompt": "#FFF0D4",
|
||||
"input_rule": "#C75B1D",
|
||||
"response_border": "#F29C38",
|
||||
"status_bar_bg": "#2B160E",
|
||||
"status_bar_text": "#FFF0D4",
|
||||
"status_bar_strong": "#FFD39A",
|
||||
"status_bar_dim": "#6C4724",
|
||||
"status_bar_good": "#6BCB77",
|
||||
"status_bar_warn": "#F29C38",
|
||||
"status_bar_bad": "#E2832B",
|
||||
"status_bar_critical": "#EF5350",
|
||||
"session_label": "#FFD39A",
|
||||
"session_border": "#6C4724",
|
||||
},
|
||||
"colors_light": {
|
||||
"banner_border": "#7A3511",
|
||||
"banner_title": "#5C2D00",
|
||||
"banner_accent": "#8B4000",
|
||||
"banner_dim": "#5A3A1A",
|
||||
"banner_text": "#3A1E00",
|
||||
"prompt": "#3A1E00",
|
||||
"ui_accent": "#8B4000",
|
||||
"ui_label": "#5C2D00",
|
||||
"ui_ok": "#1B5E20",
|
||||
"input_rule": "#7A3511",
|
||||
"response_border": "#8B4513",
|
||||
"status_bar_bg": "#F8EADF",
|
||||
"status_bar_text": "#3A1E00",
|
||||
"status_bar_strong": "#5C2D00",
|
||||
"status_bar_dim": "#6B5540",
|
||||
"status_bar_good": "#1B5E20",
|
||||
"status_bar_warn": "#8B4513",
|
||||
"status_bar_bad": "#8B4000",
|
||||
"status_bar_critical": "#B3261E",
|
||||
"session_label": "#5C2D00",
|
||||
"session_border": "#6B5540",
|
||||
},
|
||||
"spinner": {
|
||||
"waiting_faces": ["(✦)", "(▲)", "(◇)", "(<>)", "(🔥)"],
|
||||
"thinking_faces": ["(✦)", "(▲)", "(◇)", "(⌁)", "(🔥)"],
|
||||
|
|
@ -770,6 +998,13 @@ def get_prompt_toolkit_style_overrides() -> Dict[str, str]:
|
|||
warn = skin.get_color("ui_warn", "#FF8C00")
|
||||
error = skin.get_color("ui_error", "#FF6B6B")
|
||||
status_bg = skin.get_color("status_bar_bg", "#1a1a2e")
|
||||
status_text = skin.get_color("status_bar_text", text)
|
||||
status_strong = skin.get_color("status_bar_strong", title)
|
||||
status_dim = skin.get_color("status_bar_dim", dim)
|
||||
status_good = skin.get_color("status_bar_good", skin.get_color("ui_ok", "#8FBC8F"))
|
||||
status_warn = skin.get_color("status_bar_warn", warn)
|
||||
status_bad = skin.get_color("status_bar_bad", skin.get_color("banner_accent", warn))
|
||||
status_critical = skin.get_color("status_bar_critical", error)
|
||||
voice_bg = skin.get_color("voice_status_bg", status_bg)
|
||||
menu_bg = skin.get_color("completion_menu_bg", "#1a1a2e")
|
||||
menu_current_bg = skin.get_color("completion_menu_current_bg", "#333355")
|
||||
|
|
@ -782,13 +1017,13 @@ def get_prompt_toolkit_style_overrides() -> Dict[str, str]:
|
|||
"prompt": prompt,
|
||||
"prompt-working": f"{dim} italic",
|
||||
"hint": f"{dim} italic",
|
||||
"status-bar": f"bg:{status_bg} {text}",
|
||||
"status-bar-strong": f"bg:{status_bg} {title} bold",
|
||||
"status-bar-dim": f"bg:{status_bg} {dim}",
|
||||
"status-bar-good": f"bg:{status_bg} {skin.get_color('ui_ok', '#8FBC8F')} bold",
|
||||
"status-bar-warn": f"bg:{status_bg} {warn} bold",
|
||||
"status-bar-bad": f"bg:{status_bg} {skin.get_color('banner_accent', warn)} bold",
|
||||
"status-bar-critical": f"bg:{status_bg} {error} bold",
|
||||
"status-bar": f"bg:{status_bg} {status_text}",
|
||||
"status-bar-strong": f"bg:{status_bg} {status_strong} bold",
|
||||
"status-bar-dim": f"bg:{status_bg} {status_dim}",
|
||||
"status-bar-good": f"bg:{status_bg} {status_good} bold",
|
||||
"status-bar-warn": f"bg:{status_bg} {status_warn} bold",
|
||||
"status-bar-bad": f"bg:{status_bg} {status_bad} bold",
|
||||
"status-bar-critical": f"bg:{status_bg} {status_critical} bold",
|
||||
"input-rule": input_rule,
|
||||
"image-badge": f"{label} bold",
|
||||
"completion-menu": f"bg:{menu_bg} {text}",
|
||||
|
|
|
|||
|
|
@ -267,8 +267,9 @@ class TestCliBrandingHelpers:
|
|||
assert get_active_goodbye() == "Farewell, warrior! ⚔"
|
||||
|
||||
def test_prompt_toolkit_style_overrides_cover_tui_classes(self):
|
||||
from hermes_cli.skin_engine import set_active_skin, get_prompt_toolkit_style_overrides
|
||||
from hermes_cli.skin_engine import set_active_skin, set_theme_mode, get_prompt_toolkit_style_overrides
|
||||
|
||||
set_theme_mode("dark")
|
||||
set_active_skin("ares")
|
||||
overrides = get_prompt_toolkit_style_overrides()
|
||||
required = {
|
||||
|
|
@ -277,6 +278,13 @@ class TestCliBrandingHelpers:
|
|||
"prompt",
|
||||
"prompt-working",
|
||||
"hint",
|
||||
"status-bar",
|
||||
"status-bar-strong",
|
||||
"status-bar-dim",
|
||||
"status-bar-good",
|
||||
"status-bar-warn",
|
||||
"status-bar-bad",
|
||||
"status-bar-critical",
|
||||
"input-rule",
|
||||
"image-badge",
|
||||
"completion-menu",
|
||||
|
|
@ -316,15 +324,26 @@ class TestCliBrandingHelpers:
|
|||
def test_prompt_toolkit_style_overrides_use_skin_colors(self):
|
||||
from hermes_cli.skin_engine import (
|
||||
set_active_skin,
|
||||
set_theme_mode,
|
||||
get_active_skin,
|
||||
get_prompt_toolkit_style_overrides,
|
||||
)
|
||||
|
||||
set_theme_mode("dark")
|
||||
set_active_skin("ares")
|
||||
skin = get_active_skin()
|
||||
overrides = get_prompt_toolkit_style_overrides()
|
||||
assert overrides["prompt"] == skin.get_color("prompt")
|
||||
assert overrides["input-rule"] == skin.get_color("input_rule")
|
||||
assert overrides["status-bar"] == (
|
||||
f"bg:{skin.get_color('status_bar_bg')} {skin.get_color('status_bar_text')}"
|
||||
)
|
||||
assert overrides["status-bar-strong"] == (
|
||||
f"bg:{skin.get_color('status_bar_bg')} {skin.get_color('status_bar_strong')} bold"
|
||||
)
|
||||
assert overrides["status-bar-critical"] == (
|
||||
f"bg:{skin.get_color('status_bar_bg')} {skin.get_color('status_bar_critical')} bold"
|
||||
)
|
||||
assert overrides["clarify-title"] == f"{skin.get_color('banner_title')} bold"
|
||||
assert overrides["sudo-prompt"] == f"{skin.get_color('ui_error')} bold"
|
||||
assert overrides["approval-title"] == f"{skin.get_color('ui_warn')} bold"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ class TestCliSkinPromptIntegration:
|
|||
def test_build_tui_style_dict_uses_skin_overrides(self):
|
||||
cli = _make_cli_stub()
|
||||
|
||||
from hermes_cli.skin_engine import set_theme_mode
|
||||
|
||||
set_theme_mode("dark")
|
||||
set_active_skin("ares")
|
||||
skin = get_active_skin()
|
||||
style_dict = cli._build_tui_style_dict()
|
||||
|
|
@ -66,6 +69,9 @@ class TestCliSkinPromptIntegration:
|
|||
assert style_dict["prompt"] == skin.get_color("prompt")
|
||||
assert style_dict["input-rule"] == skin.get_color("input_rule")
|
||||
assert style_dict["prompt-working"] == f"{skin.get_color('banner_dim')} italic"
|
||||
assert style_dict["status-bar"] == (
|
||||
f"bg:{skin.get_color('status_bar_bg')} {skin.get_color('status_bar_text')}"
|
||||
)
|
||||
assert style_dict["approval-title"] == f"{skin.get_color('ui_warn')} bold"
|
||||
|
||||
def test_apply_tui_skin_style_updates_running_app(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue