Feat/brand theme - #8
Conversation
Central color source for the orange/warm-gray brand palette. Semantic tokens (Accent, Text, Status*) are pre-resolved at startup for the terminal's dark/light background using lipgloss.LightDark. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Apply the orange/warm-gray color scheme to fang's --help rendering via FangColorScheme. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Apply theme.Accent for headers and theme.TextDimmed for table borders and hint text. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Apply theme.Accent for hook step headers and theme.TextDimmed for hook body text. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Replace direct huh.NewForm calls with theme.NewForm() so all six prompt forms inherit the orange/warm-gray brand palette. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Apply adaptive orange/warm-gray colors to the interactive scaffolding form and status panel. Pre-resolve adaptive colors at startup instead of tracking BackgroundColorMsg at runtime. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
Apply adaptive orange/warm-gray colors to the hook list, delegate styles, and view background. Remove manual hasDarkBg tracking in favor of pre-resolved adaptive colors at startup. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-free
📝 WalkthroughWalkthroughAdds a shared runtime-aware theme package and applies it to HUH forms, project and hook TUIs, hook output, printed output, and Fang command rendering. Existing prompt behavior and command flow remain unchanged. ChangesShared theme integration
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
cmd/new_tui.go (1)
93-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse
theme.NewFormhere.This duplicates the wrapper already centralized in
internal/theme.NewForm, allowing this form to diverge from the other themed prompts over time.Proposed refactor
- m.form = huh.NewForm(huh.NewGroup(fields...)). - WithTheme(huh.ThemeFunc(func(_ bool) *huh.Styles { return theme.Theme() })). + m.form = theme.NewForm(huh.NewGroup(fields...)). WithWidth(min(m.width/2, 60)).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/new_tui.go` around lines 93 - 95, Update the form construction in the new TUI setup to use the centralized theme.NewForm wrapper instead of directly calling huh.NewForm with WithTheme. Preserve the existing fields and width configuration while removing the duplicated theme setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/theme/theme.go`:
- Around line 15-16: Remove the import-time lipgloss.HasDarkBackground call that
initializes isDark/ld in the theme package. Thread the runtime dark-mode value
through Theme and NewForm, use Huh’s isDark callback argument when constructing
the form theme, and handle tea.BackgroundColorMsg in the Bubble Tea loop if
background changes must update the UI.
---
Nitpick comments:
In `@cmd/new_tui.go`:
- Around line 93-95: Update the form construction in the new TUI setup to use
the centralized theme.NewForm wrapper instead of directly calling huh.NewForm
with WithTheme. Preserve the existing fields and width configuration while
removing the duplicated theme setup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 560500f4-60b3-49dd-bc18-e7c522f2c874
📒 Files selected for processing (7)
cmd/hook_output.gocmd/hooks_tui.gocmd/new_prompts.gocmd/new_tui.gocmd/print.gointernal/theme/theme.gomain.go
| isDark = lipgloss.HasDarkBackground(os.Stdin, os.Stdout) | ||
| ld = lipgloss.LightDark(isDark) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate import-time background probes and theme callbacks that ignore runtime mode.
rg -n --glob '*.go' \
'HasDarkBackground\(|ThemeFunc\(func\(_ bool\)|RequestBackgroundColor|BackgroundColorMsg' .Repository: N1xev/spin
Length of output: 446
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the theme implementation and the TUI caller for runtime background handling.
printf '\n== internal/theme/theme.go ==\n'
sed -n '1,220p' internal/theme/theme.go
printf '\n== cmd/new_tui.go ==\n'
sed -n '1,160p' cmd/new_tui.go
printf '\n== search for background-color handling ==\n'
rg -n --glob '*.go' 'BackgroundColorMsg|RequestBackgroundColor|SetColorProfile|HasDarkBackground|WithTheme\(huh.ThemeFunc|Theme\(\)' .Repository: N1xev/spin
Length of output: 10957
🌐 Web query:
lipgloss HasDarkBackground terminal.go query stdin stdout wait behavior Huh ThemeFunc isDark runtime argument
💡 Result:
In Charmbracelet libraries, detection and handling of terminal background colors are managed via specific functions in Lip Gloss and Huh. Lip Gloss HasDarkBackground The HasDarkBackground function in Lip Gloss (charm.land/lipgloss) detects whether a terminal has a light or dark background [1][2][3]. Standalone Usage: When not using Bubble Tea, you can perform manual detection by passing input and output files [1][4][5]: hasDarkBG:= lipgloss.HasDarkBackground(os.Stdin, os.Stdout) Detection Behavior: This function returns a boolean. By default, it returns true if an error is encountered during the detection process [1][2]. Bubble Tea Integration: In a Bubble Tea application, you should instead listen for tea.BackgroundColorMsg in your Update function and use the msg.IsDark method [1][6]. Thread Safety/Caching: The renderer maintains cached background detection states. Thread-safe functions like SetHasDarkBackground(b bool) exist primarily for testing purposes to override automatic detection [3]. Huh ThemeFunc and isDark In Huh (github.com/charmbracelet/huh), theme handling has been updated in v2.0.0 [7][8][9]. ThemeFunc Signature: Custom themes now use the ThemeFunc signature, which accepts a boolean argument to indicate whether the background is dark [10][7][9]: type ThemeFunc func(isDark bool) Styles Built-in Themes: All built-in themes (e.g., ThemeCharm, ThemeDracula) now require this bool parameter [8][9]. You typically pass the result of a detection function to these themes [9]: isDark:= lipgloss.HasDarkBackground form:= huh.NewForm(...).WithTheme(huh.ThemeCharm(isDark)) Summary of Behavior The core pattern across both libraries is to determine the terminal background state as a boolean (isDark) and pass this state explicitly into layout or styling functions (like lipgloss.LightDark or huh.Theme functions) to ensure the UI adapts correctly to the user's environment [5][6][9].
Citations:
- 1: https://github.com/charmbracelet/lipgloss/blob/v2.0.0/query.go
- 2: https://github.com/charmbracelet/lipgloss/blob/v2-complete-color/query.go
- 3: https://github.com/charmbracelet/lipgloss/blob/e35d216358713610a0f82a57c51ca769ece98fed/renderer.go
- 4: https://pkg.go.dev/github.com/charmbracelet/lipgloss/v2
- 5: https://pkg.go.dev/charm.land/lipgloss/v2
- 6: https://github.com/charmbracelet/lipgloss/blob/v2.0.0/color.go
- 7: docs: add v2 upgrade guide + release notes charmbracelet/huh#741
- 8: https://newreleases.io/project/github/charmbracelet/huh/release/v2.0.0
- 9: charmbracelet/huh@9a59654
- 10: https://github.com/charmbracelet/huh/blob/main/theme.go
Avoid import-time terminal background detection
HasDarkBackground(os.Stdin, os.Stdout) runs during package init and can block or emit terminal-query bytes before the UI starts. Thread isDark into Theme/NewForm instead, and handle tea.BackgroundColorMsg in the Bubble Tea loop if the TUI needs to react to background changes. NewForm also ignores Huh’s isDark callback arg, so the form theme is fixed at import time.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@internal/theme/theme.go` around lines 15 - 16, Remove the import-time
lipgloss.HasDarkBackground call that initializes isDark/ld in the theme package.
Thread the runtime dark-mode value through Theme and NewForm, use Huh’s isDark
callback argument when constructing the form theme, and handle
tea.BackgroundColorMsg in the Bubble Tea loop if background changes must update
the UI.
This PR replaces the default terminal colors throughout spin with a consistent orange and warm-gray brand palette that adapts to the user's terminal background at startup.
The colors are pre-resolved once at startup using lipgloss.LightDark plus lipgloss.HasDarkBackground instead of tracking BackgroundColorMsg at runtime. This eliminates hasDarkBg fields, style regeneration, and message handling that was previously spread across models.
What changed:
New internal/theme/ package (160 lines) containing the full brand palette, pre-resolved semantic tokens, Huh theme, fang color scheme, list styles, and convenience helpers
All huh.NewForm calls replaced with theme.NewForm() so prompt forms inherit the brand palette automatically
main.go uses theme.FangColorScheme for branded --help output
cmd/new_tui.go and cmd/hooks_tui.go use pre-resolved colors for all custom styles and view backgrounds instead of tracking isDark per model or using the compat package
cmd/print.go and cmd/hook_output.go use theme.Accent and theme.TextDimmed for headers and body text
Footer keybinding hints now match the same accent/muted color scheme as Huh help text
The semantic tokens (Accent, Text, TextMuted, TextDimmed, StatusError, StatusSuccess, StatusInfo) resolve adaptively at startup so the same binary looks correct on both light and dark terminals.