summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-08 10:05:04 +0300
committerPaul Buetow <paul@buetow.org>2025-09-08 10:05:04 +0300
commit0dcf347c3fbc6e4ffb7e46294f5dd92dbbcd98ef (patch)
tree7503979ac4d44843f1cfcc8da54b77b4a24ff4bd
parentcead3ebde8f3aee0ef8677158d37f4d04c6629dc (diff)
tmux: improve white-on-purple legibility; bump version to v0.9.0v0.9.0
-rw-r--r--README.md3
-rw-r--r--internal/tmux/status.go85
-rw-r--r--internal/version.go2
3 files changed, 73 insertions, 17 deletions
diff --git a/README.md b/README.md
index 1be6238..32ce84f 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,9 @@ Hexai can surface live progress in tmux's status line via a user option. Add thi
set -g status-right '#{@hexai_status} #[fg=colour8]| %H:%M'
```
+- Note: `colour8` is typically “bright black” (a dim grey) in many themes.
+ If it’s low-contrast on your background, change it (e.g., `colour7` or `white`).
+
- CLI updates `@hexai_status` at start (⏳ provider:model) and on completion with compact stats (↑sent, ↓recv, rpm, reqs).
- LSP emits an initial heartbeat on client initialize and periodic compact stats (provider, model, rpm, reqs, bytes).
- The TUI action runner sets a ready heartbeat and a completion heartbeat with stats.
diff --git a/internal/tmux/status.go b/internal/tmux/status.go
index 1d2a8ee..6d76bcb 100644
--- a/internal/tmux/status.go
+++ b/internal/tmux/status.go
@@ -9,6 +9,16 @@ import (
"codeberg.org/snonux/hexai/internal/textutil"
)
+// baseFGToken is a placeholder inserted by status formatters wherever the
+// base foreground color should be restored. The theming layer (applyTheme)
+// replaces this token with a tmux color sequence matching the active theme's
+// foreground, which fixes readability when a theme sets a non-default fg.
+const (
+ baseFGToken = "\x1EHEXAI_BASE_FG\x1E"
+ arrowUpToken = "\x1EHEXAI_ARROW_UP\x1E"
+ arrowDownToken = "\x1EHEXAI_ARROW_DOWN\x1E"
+)
+
// Enabled reports whether tmux status updates are enabled via env (default: on).
func Enabled() bool {
v := strings.TrimSpace(os.Getenv("HEXAI_TMUX_STATUS"))
@@ -48,17 +58,17 @@ func FormatLLMStatsStatusColored(provider, model string, reqs int64, rpm float64
in := textutil.HumanBytes(inBytes)
out := textutil.HumanBytes(outBytes)
// Keep it compact; colorize prefix and arrows; use fg resets so a themed bg can persist.
- // colour8 = dim grey, colour3 = yellow (up), colour2 = green (down)
+ // Arrows use theme-aware styles; bytes immediately switch to base fg for contrast.
return fmt.Sprintf(
- "#[fg=colour8]LLM:#[fg=default]%s:%s #[fg=colour3]↑%s#[fg=default] #[fg=colour2]↓%s#[fg=default] %.1frpm %dr",
- provider, model, in, out, rpm, reqs,
+ "%sLLM:%s:%s %s↑%s%s %s↓%s%s %.1frpm %dr",
+ baseFGToken, provider, model, arrowUpToken, baseFGToken, in, arrowDownToken, baseFGToken, out, rpm, reqs,
)
}
// FormatLLMStartStatus renders a short colored heartbeat at start/initialize time.
// Example: "LLM:openai:gpt-4.1 ⏳"
func FormatLLMStartStatus(provider, model string) string {
- return fmt.Sprintf("#[fg=colour8]LLM:#[fg=default]%s:%s #[fg=colour11]⏳#[fg=default]", provider, model)
+ return fmt.Sprintf("%sLLM:%s:%s #[fg=colour11]⏳%s", baseFGToken, provider, model, baseFGToken)
}
// applyTheme wraps the status string with a user-selected tmux style if requested.
@@ -68,23 +78,66 @@ func applyTheme(s string) string {
// Allow explicit fg/bg override
fg := strings.TrimSpace(os.Getenv("HEXAI_TMUX_STATUS_FG"))
bg := strings.TrimSpace(os.Getenv("HEXAI_TMUX_STATUS_BG"))
- if fg != "" || bg != "" {
+ // Determine base foreground and background from env or theme presets
+ baseFG := ""
+ wrap := false
+ if fg != "" || bg != "" { // explicit override path
+ wrap = true
if fg == "" {
- fg = "default"
+ baseFG = "default"
+ } else {
+ baseFG = fg
+ }
+ // bg used as provided (may be empty)
+ } else {
+ switch theme {
+ case "white-on-purple", "purple", "magenta", "white-on-magenta":
+ baseFG, bg, wrap = "white", "magenta", true
+ case "black-on-yellow", "yellow", "black-on-gold":
+ baseFG, bg, wrap = "black", "yellow", true
+ case "white-on-blue", "blue", "white-on-navy":
+ baseFG, bg, wrap = "white", "blue", true
}
- if bg == "" {
- bg = "default"
+ if baseFG == "" { // no theme selected
+ baseFG = "default"
+ }
+ }
+
+ // Theme-aware arrow styles
+ upStyle, downStyle := "#[fg=colour3]", "#[fg=colour2]" // defaults: yellow up, green down
+ if fg != "" || bg != "" { // explicit override path: match arrows to base fg, bold for visibility
+ upStyle = "#[bold,fg=" + baseFG + "]"
+ downStyle = upStyle
+ } else {
+ switch theme {
+ case "white-on-purple", "purple", "magenta", "white-on-magenta":
+ upStyle, downStyle = "#[bold,fg=black]", "#[bold,fg=black]"
+ case "black-on-yellow", "yellow", "black-on-gold":
+ upStyle, downStyle = "#[bold,fg=black]", "#[bold,fg=black]"
+ case "white-on-blue", "blue", "white-on-navy":
+ upStyle, downStyle = "#[bold,fg=white]", "#[bold,fg=white]"
}
- return "#[fg=" + fg + ",bg=" + bg + "]" + s + "#[fg=default,bg=default]"
}
- if theme == "white-on-purple" || theme == "purple" || theme == "magenta" || theme == "white-on-magenta" {
- return "#[fg=white,bg=magenta]" + s + "#[fg=default,bg=default]"
+
+ // Replace base-foreground and arrow placeholders with selected styles
+ if strings.Contains(s, baseFGToken) {
+ s = strings.ReplaceAll(s, baseFGToken, "#[fg="+baseFG+"]")
+ }
+ if strings.Contains(s, arrowUpToken) {
+ s = strings.ReplaceAll(s, arrowUpToken, upStyle)
}
- if theme == "black-on-yellow" || theme == "yellow" || theme == "black-on-gold" {
- return "#[fg=black,bg=yellow]" + s + "#[fg=default,bg=default]"
+ if strings.Contains(s, arrowDownToken) {
+ s = strings.ReplaceAll(s, arrowDownToken, downStyle)
+ }
+
+ if !wrap {
+ return s
}
- if theme == "white-on-blue" || theme == "blue" || theme == "white-on-navy" {
- return "#[fg=white,bg=blue]" + s + "#[fg=default,bg=default]"
+ // Wrap with base fg and optional bg, then reset at the end
+ prefix := "#[fg=" + baseFG
+ if bg != "" {
+ prefix += ",bg=" + bg
}
- return s
+ prefix += "]"
+ return prefix + s + "#[fg=default,bg=default]"
}
diff --git a/internal/version.go b/internal/version.go
index e32d6fb..3162341 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -1,4 +1,4 @@
// Summary: Hexai semantic version identifier used by CLI and LSP binaries.
package internal
-const Version = "0.8.0"
+const Version = "0.9.0"