Skip to content

feat(#1756): Add dynamic agent color styling system#1757

Open
aheritier wants to merge 3 commits intodocker:mainfrom
aheritier:feat/agent-color-coding
Open

feat(#1756): Add dynamic agent color styling system#1757
aheritier wants to merge 3 commits intodocker:mainfrom
aheritier:feat/agent-color-coding

Conversation

@aheritier
Copy link
Contributor

@aheritier aheritier commented Feb 16, 2026

Closes #1756

Summary

Assigns each agent in a multi-agent team a unique, deterministic color so users can visually distinguish agents across the entire TUI — message badges, sidebar entries, tab bar indicators, and handoff/transfer-task displays.

Colors are generated dynamically from hue values (0–360) defined per theme, with saturation and lightness auto-adapted based on the theme's background (dark → brighter, light → darker). This follows Option 2 from the design discussion, as agreed with @rumpl and @krissetto.

What changed

New: Color utility library (pkg/tui/styles/colorutil.go)

Shared color math extracted from tabbar/tab.go and theme.go, plus new functionality:

  • Hex parsing, sRGB linearization, HSL ↔ RGB conversion
  • WCAG 2.x luminance & contrast ratio (ContrastRatio, EnsureContrast, MutedContrastFg)
  • Hue-based palette generation (GenerateBadgePalette, GenerateAccentPalette)
  • CIE76 perceptual distance for validating color distinctness

New: Agent color registry (pkg/tui/styles/agent_colors.go)

Thread-safe, cached mapping from agent name → palette index:

  • SetAgentOrder(names) — called when team info updates
  • AgentBadgeStyleFor(name) / AgentAccentStyleFor(name) — return precomputed lipgloss styles
  • InvalidateAgentColorCache() — called on theme change to regenerate styles
  • Wraps around palette size for teams larger than 16 agents

Theme integration

  • New optional agent_hues field in ThemeColors (list of hue values 0–360)
  • Falls back to 16 well-spaced default hues (blue, purple, teal, orange, pink, green, etc.)
  • Theme authors only need to provide hue values; saturation/lightness/contrast are handled automatically

TUI component updates

  • Message badges — per-agent colored (AgentBadgeStyleFor(sender))
  • Sidebar agent names — per-agent accent color (AgentAccentStyleFor(agent.Name))
  • Handoff / transfer-task displays — both source and target agents get their own badge color
  • Tab bar — color helpers (MutedContrastFg, EnsureContrast) consolidated into shared colorutil.go

Validation

All 10 built-in themes pass automated checks:

  • ✅ WCAG AA badge contrast (fg/bg ≥ 4.5:1)
  • ✅ WCAG AA accent contrast (vs background ≥ 3.0:1)
  • ✅ CIE76 pairwise distinctness (ΔE ≥ 10 between all palette entries)
  • Full color audit report available via go test -v ./pkg/tui/styles/...

Screenshots

Screenshot 2026-02-17 at 21 01 09 Screenshot 2026-02-17 at 21 01 26 Screenshot 2026-02-17 at 21 03 32 Screenshot 2026-02-17 at 21 03 45 Screenshot 2026-02-17 at 21 04 01 Screenshot 2026-02-17 at 21 04 14 Screenshot 2026-02-17 at 21 04 24 Screenshot 2026-02-17 at 21 04 37 Screenshot 2026-02-17 at 21 04 54 Screenshot 2026-02-17 at 21 05 01

Dark themes

Theme Screenshot
default
tokyo-night
dracula
one-dark
nord
catppuccin-mocha
gruvbox-dark
solarized-dark

Light themes

Theme Screenshot
catppuccin-latte
gruvbox-light

Files changed

File Change
pkg/tui/styles/colorutil.go New — shared color math & palette generation
pkg/tui/styles/colorutil_test.go New — unit tests for all color utilities
pkg/tui/styles/agent_colors.go New — agent color registry & cached style lookups
pkg/tui/styles/agent_colors_test.go New — registry tests + WCAG/distinctness validation across all themes
pkg/tui/styles/theme.go Add AgentHues field, remove duplicated color helpers, hook cache invalidation
pkg/tui/styles/theme_test.go Update mergeColors test for new AgentHues slice field
pkg/tui/styles/themes/default.yaml Add agent_hues with 16 default values
pkg/tui/service/sessionstate.go Call SetAgentOrder when available agents change
pkg/tui/components/message/message.go Use AgentBadgeStyleFor(sender)
pkg/tui/components/sidebar/sidebar.go Use AgentAccentStyleFor(agent.Name)
pkg/tui/components/tabbar/tab.go Remove ~100 lines of color helpers (moved to colorutil.go)
pkg/tui/components/tabbar/tabbar.go Use shared MutedContrastFg / EnsureContrast
pkg/tui/components/tool/handoff/handoff.go Per-agent badge colors for source/target
pkg/tui/components/tool/transfertask/transfertask.go Per-agent badge colors for source/target

Copilot AI review requested due to automatic review settings February 16, 2026 20:21
@aheritier aheritier requested a review from a team as a code owner February 16, 2026 20:21
@aheritier aheritier changed the title feat(styles): Add dynamic agent color styling system feat(#1756): Add dynamic agent color styling system Feb 16, 2026
@rumpl
Copy link
Member

rumpl commented Feb 16, 2026

Very cool idea, I just wonder how well this plays with our theme system?

@aheritier aheritier force-pushed the feat/agent-color-coding branch from 8e5f912 to cebba6a Compare February 16, 2026 20:25
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a dynamic, deterministic agent color system to the TUI so agent badges and sidebar accents are derived from a fixed palette based on the agent’s order (as reported by the runtime team info).

Changes:

  • Introduces badge and accent color palettes plus helper APIs to render agent-specific styles.
  • Tracks agent ordering via a shared registry updated from TeamInfoEvent/SetAvailableAgents.
  • Updates multiple TUI components to render agent badges/names using the new dynamic style functions.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/tui/styles/agent_colors.go Implements palettes, agent→index registry, and dynamic badge/accent style helpers.
pkg/tui/styles/agent_colors_test.go Adds tests for agent ordering/indexing and determinism of generated styles.
pkg/tui/service/sessionstate.go Updates agent-order registry when available agents are set from runtime events.
pkg/tui/components/tool/transfertask/transfertask.go Switches to dynamic agent badge styling for sender/target agent.
pkg/tui/components/tool/handoff/handoff.go Switches to dynamic agent badge styling for sender/target agent.
pkg/tui/components/sidebar/sidebar.go Switches current-agent prefix and name rendering to dynamic accent styling per agent.
pkg/tui/components/message/message.go Switches message sender prefix to dynamic agent badge styling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 93 to 110
// AgentBadgeColorsFor returns the badge foreground/background colors for a given agent name.
func AgentBadgeColorsFor(agentName string) AgentBadgeColors {
idx := agentIndex(agentName)
bgHex := agentColorPalette[idx]

theme := CurrentTheme()
fgHex := bestForegroundHex(
bgHex,
theme.Colors.TextBright,
theme.Colors.Background,
"#000000",
"#ffffff",
)

return AgentBadgeColors{
Fg: lipgloss.Color(fgHex),
Bg: lipgloss.Color(bgHex),
}
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentBadgeColorsFor recomputes contrast (hex parsing + luminance math) on every call. Since badge rendering can happen frequently (e.g., per message render), consider memoizing the resolved foreground per (theme ref, palette index/bgHex) or precomputing on theme changes to avoid repeated parsing and math in hot render paths.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

precomputed all badge/accent styles in rebuildAgentColorCache(), called from
SetAgentOrder and InvalidateAgentColorCache (hooked into ApplyTheme). The hot render path now reads from cached slices.

@aheritier aheritier force-pushed the feat/agent-color-coding branch from cebba6a to 69d26b4 Compare February 16, 2026 20:45
@aheritier
Copy link
Contributor Author

Very cool idea, I just wonder how well this plays with our theme system?

not yet configurable, it's what I wanted to discuss with you.

@aheritier aheritier force-pushed the feat/agent-color-coding branch from 69d26b4 to da34a5c Compare February 16, 2026 21:28
@aheritier
Copy link
Contributor Author

Not sure at all cc @rumpl @krissetto @dgageot :

Option 1: Add agent_colors section to ThemeColors

Add agent_badge_colors and agent_accent_colors as string arrays to ThemeColors. Theme authors can override the full palettes. The merge logic would be: if the override provides
a non-empty array, use it entirely; otherwise fall back to the default.

In theme YAML

colors:
  agent_badge_colors:
    - "#1e66f5"
    - "#8839ef"
    - "#40a02b"
    # ...
  agent_accent_colors:
    - "#1e66f5"
    - "#8839ef"
    # ...

Pros: Full control per theme, follows existing pattern, theme authors can curate exactly. Cons: Verbose (16×2 = 32 hex values to override), most theme authors won't bother, the
merge is all-or-nothing per palette.

Option 2: Derive agent colors from a single agent_base_hues list

Theme provides a list of hue values (0–360) or a few seed colors. The system generates the full palette by adjusting saturation/lightness based on the theme's background
lightness (dark theme → brighter badges, light theme → darker badges).

colors:
  agent_hues: [220, 280, 170, 30, 330, 140, 210, 270, 45, 0, 190, 25, 235, 295, 160, 350]

Pros: Compact, auto-adapts to dark/light backgrounds, fewer values to maintain. Cons: More complex implementation, less precise control, HSL→hex conversion needed.

Option 3: Theme provides a color_scale and system generates from it

Use the theme's existing status/accent colors as seeds — brand, success, error, warning, info, accent, highlight, badge_accent, badge_info, badge_success — and derive the rest
programmatically to fill the 16 slots.

  • No new YAML fields needed — agent palette is derived from: brand, accent, success, error, warning, info, highlight, badge_accent, badge_info, badge_success + 6 generated fills

Pros: Zero config for theme authors — it "just works" with any theme. No new YAML fields. Cons: Less control, the generated fills may not be aesthetically ideal, hard to predict
exact output.

Option 4: Hybrid — auto-derive with optional override

Combine Option 3 (auto-derive from existing theme colors) as the default, with Option 1 (explicit array) as an optional override. If the theme YAML provides agent_badge_colors,
use it. Otherwise, derive from the theme's existing color definitions.

Pros: Works out of the box for all existing themes, theme authors who care can override. Cons: Two code paths to maintain, but the override path is trivial.

@aheritier
Copy link
Contributor Author

copilot feedbacks were addressed

@rumpl
Copy link
Member

rumpl commented Feb 16, 2026

My preference would be Option 2, WDYT @krissetto ?

@krissetto
Copy link
Contributor

krissetto commented Feb 16, 2026

@rumpl I also prefer Option 2. Explicit theme values could be nice to have if someone wants to define them, but generating the hues is better IMHO since there could be n agents in a team.

Also @aheritier there is some code in tabbar/tab.go that does dynamic color selection for some bits of the UI that would've been too tedious to theme manually, we could adjust and consolidate the approach into something reusable across multiple places in the TUI as well so we end up with a consistent look and feel

Implement a flexible agent color palette with deterministic
color assignment based on agent order. Introduces new functions
to generate badge and accent styles for agents dynamically.

Changes include:
- Create agent color palettes for badges and accents
- Add agent order tracking mechanism
- Implement color selection based on agent index
- Update components to use new dynamic styling functions
…til.go

Extract color math functions from tabbar/tab.go and theme.go into a
shared colorutil.go module. Adds HSL conversion, CIELAB perceptual
distance, and hue-based palette generation functions.

This eliminates duplicate luminance/contrast implementations and
provides the foundation for theme-integrated agent color generation.

Assisted-By: cagent
…ation

Replace hardcoded agent color palettes with dynamic generation from
HSL hue values. Each theme can define agent_hues (16 hue values
0-360) in its YAML; colors auto-adapt saturation and lightness
based on the theme background (dark vs light).

Validation across all 10 built-in themes:
- WCAG AA badge contrast (≥4.5:1 fg/bg) — all pass
- WCAG AA accent contrast (≥3.0:1 vs background) — all pass
- CIE76 pairwise distinctness (ΔE≥10) — all pass
- Color audit report available via go test -v

Assisted-By: cagent
@aheritier aheritier force-pushed the feat/agent-color-coding branch from da34a5c to bd0fa58 Compare February 17, 2026 20:05
@aheritier
Copy link
Contributor Author

@rumpl @krissetto @dgageot please review with caution, it's bigger than I expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(tui): per-agent color coding in sidebar and chat badges

3 participants