fix: force true color (no system downsampling); theme-drive the whole preview
6441106b38305ed8aa1eb91b854d5ade40bfc973
humdrum <me@humdrum.me> · 2026-06-29 08:41
parent dabc84eb
fix: force true color (no system downsampling); theme-drive the whole preview - Force lipgloss to the TrueColor profile so glint renders its exact theme hexes regardless of the terminal's detected profile or the OS light/dark setting — fixes unreadable prose when the system is light but the theme is flexoki-dark. - Preview is now fully theme-driven: backgrounds (document, code, code block, table, blockquote) use the theme paper; chroma syntax styling is disabled so code blocks no longer show glamour's hardcoded dark panel; headings (incl. H1's purple-bg/yellow-text) use the theme heading color on the theme bg; prose, code, and links use theme colors. App passes the theme colors into the preview.
4 files changed
go.mod +1 −1
@@ -9,6 +9,7 @@ github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/glamour v1.0.0
github.com/charmbracelet/huh v1.0.0
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
+ github.com/muesli/termenv v0.16.0
)
require (
@@ -39,7 +40,6 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
- github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yuin/goldmark v1.7.13 // indirect
internal/app/app.go +13 −2
@@ -86,8 +86,19 @@ editor: ed,
saveInput: ti,
}
a.preview = preview.New(a.glamourStyle())
- a.preview.SetBackground(string(th.Background))
+ a.preview.SetColors(previewColors(th))
return a
+}
+
+// previewColors maps a theme to the glamour preview's color set.
+func previewColors(th theme.Theme) preview.Colors {
+ return preview.Colors{
+ Background: string(th.Background),
+ Text: string(th.Text),
+ Heading: string(th.Heading),
+ Code: string(th.Code),
+ Link: string(th.Link),
+ }
}
// Load reads a file into the editor and switches to edit mode.
@@ -407,7 +418,7 @@ func (a *App) cycleTheme() (tea.Model, tea.Cmd) {
a.theme = theme.Next(a.theme.Name)
a.editor.SetTheme(a.theme)
a.preview.SetStyle(a.glamourStyle())
- a.preview.SetBackground(string(a.theme.Background))
+ a.preview.SetColors(previewColors(a.theme))
// Re-render an open preview so its glamour style follows the new theme
// (otherwise it keeps the old light/dark block until the next toggle).
if a.mode == ModePreview {
internal/preview/preview.go +39 −18
@@ -12,33 +12,55 @@ "github.com/charmbracelet/glamour/ansi"
"github.com/charmbracelet/glamour/styles"
)
-// paintCanvasBackground rewrites a glamour style so every block sits on the
-// theme background (and drops glamour's document margin), so code blocks,
-// inline code, and tables don't show their own darker panels in the preview.
-// The chroma config is deep-copied so glamour's shared global isn't mutated.
-func paintCanvasBackground(cfg *ansi.StyleConfig, hex string) {
+// applyTheme rewrites a glamour style so it matches the glint theme: every block
+// on the theme background (no glamour panels), headings/code/links/prose in the
+// theme colors, glamour's H1 purple-bg/yellow-text and dark code chroma removed.
+func applyTheme(cfg *ansi.StyleConfig, c Colors) {
var zero uint
cfg.Document.Margin = &zero
- if hex == "" {
+ if c.Background == "" {
return
}
- bg := hex
+ bg, text, heading, code, link := c.Background, c.Text, c.Heading, c.Code, c.Link
+
+ // Backgrounds → the theme paper (so nothing shows a darker panel).
cfg.Document.BackgroundColor = &bg
cfg.Code.BackgroundColor = &bg
cfg.CodeBlock.BackgroundColor = &bg
- if cfg.CodeBlock.Chroma != nil {
- chroma := *cfg.CodeBlock.Chroma
- chroma.Background.BackgroundColor = &bg
- cfg.CodeBlock.Chroma = &chroma
+ cfg.Table.BackgroundColor = &bg
+ cfg.BlockQuote.BackgroundColor = &bg
+ // Disable chroma syntax styling so code renders plainly on the theme bg
+ // instead of glamour's hardcoded dark code panel.
+ cfg.CodeBlock.Chroma = nil
+ cfg.CodeBlock.Color = &code
+ cfg.Code.Color = &code
+
+ // Prose + headings + links in theme colors. Clear H1's purple background.
+ cfg.Document.Color = &text
+ cfg.Text.Color = &text
+ for _, h := range []*ansi.StyleBlock{&cfg.Heading, &cfg.H1, &cfg.H2, &cfg.H3, &cfg.H4, &cfg.H5, &cfg.H6} {
+ h.Color = &heading
+ h.BackgroundColor = &bg
}
- cfg.Table.BackgroundColor = &bg
+ cfg.Link.Color = &link
+ cfg.LinkText.Color = &link
}
// Model wraps a Glamour renderer and a viewport.
+// Colors are the theme hexes the preview paints glamour with, so the read view
+// matches the editor exactly (no glamour panel colors, no system reliance).
+type Colors struct {
+ Background string
+ Text string
+ Heading string
+ Code string
+ Link string
+}
+
type Model struct {
vp viewport.Model
style string
- bg string // theme background hex, so the preview matches the canvas
+ colors Colors
width int
height int
}
@@ -54,10 +76,9 @@ height: 24,
}
}
-// SetBackground sets the document background (a hex like "#100F0F") so the
-// glamour preview blends into the themed canvas instead of showing glamour's
-// own panel color.
-func (m *Model) SetBackground(hex string) { m.bg = hex }
+// SetColors sets the theme colors glamour renders with so the preview matches
+// the editor canvas (background, prose, headings, code, links).
+func (m *Model) SetColors(c Colors) { m.colors = c }
// SetSize resizes the viewport.
func (m *Model) SetSize(w, h int) {
@@ -130,7 +151,7 @@ cfg := styles.DarkStyleConfig
if m.style == "light" {
cfg = styles.LightStyleConfig
}
- paintCanvasBackground(&cfg, m.bg)
+ applyTheme(&cfg, m.colors)
opts = append(opts, glamour.WithStyles(cfg))
case knownStyles[m.style]:
// A user-chosen named style keeps its own look.
main.go +8 −0
@@ -12,7 +12,15 @@ "glint/internal/configui"
"glint/internal/keyprobe"
tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
+ "github.com/muesli/termenv"
)
+
+func init() {
+ // Render glint's exact theme hexes regardless of the terminal's detected
+ // profile or OS appearance — never downsample or adapt to the system.
+ lipgloss.SetColorProfile(termenv.TrueColor)
+}
// version is the build version, overridden at release time via
// -ldflags "-X main.version=<v>" (the Homebrew formula sets it).