refactor(theme): move chrome style set into theme package
- commit
- 5c7684b8d48a7105a7fe33efafd59d5110623d82
- author
- brice · · 2026-07-17 07:59 UTC
- parent
- 4ab3b08
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +169 -127
M internal/usecase/daemon/copymode.go +3 -2
| @@ -10,6 +10,7 @@ import ( | |||
| 10 | 10 | scopy "github.com/bnema/vev/internal/usecase/copy" | |
| 11 | 11 | "github.com/bnema/vev/internal/usecase/keys" | |
| 12 | 12 | "github.com/bnema/vev/internal/usecase/mouse" | |
| 13 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |
| 13 | 14 | "github.com/bnema/vev/internal/usecase/ui" | |
| 14 | 15 | "github.com/bnema/vev/internal/usecase/visualsearch" | |
| 15 | 16 | "github.com/bnema/vev/pkg/renderer" | |
| @@ -415,8 +416,8 @@ func composeCopyClientFrame(mode *scopy.Mode, target domain.Rect, frame renderer | |||
| 415 | 416 | if mode == nil || target.Width <= 0 || target.Height <= 0 || frame.Width <= 0 || frame.Height <= 0 { | |
| 416 | 417 | return frame, nil | |
| 417 | 418 | } | |
| 418 | - | styles := newThemeStyles(bars.theme) | |
| 419 | - | copyFrame := mode.Render(styles.copyStatus, styles.selection) | |
| 419 | + | styles := themeui.NewStyles(bars.theme) | |
| 420 | + | copyFrame := mode.Render(styles.CopyStatus, styles.Selection) | |
| 420 | 421 | bodyRows := max(copyFrame.Height-1, 0) | |
| 421 | 422 | for y := 0; y < target.Height && y < bodyRows && target.Y+y < frame.Height-1; y++ { | |
| 422 | 423 | dstX := max(target.X, 0) | |
| @@ -10,6 +10,7 @@ import ( | |||||
| 10 | scopy "github.com/bnema/vev/internal/usecase/copy" | 10 | scopy "github.com/bnema/vev/internal/usecase/copy" | ||
| 11 | "github.com/bnema/vev/internal/usecase/keys" | 11 | "github.com/bnema/vev/internal/usecase/keys" | ||
| 12 | "github.com/bnema/vev/internal/usecase/mouse" | 12 | "github.com/bnema/vev/internal/usecase/mouse" | ||
| 13 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |||
| 13 | "github.com/bnema/vev/internal/usecase/ui" | 14 | "github.com/bnema/vev/internal/usecase/ui" | ||
| 14 | "github.com/bnema/vev/internal/usecase/visualsearch" | 15 | "github.com/bnema/vev/internal/usecase/visualsearch" | ||
| 15 | "github.com/bnema/vev/pkg/renderer" | 16 | "github.com/bnema/vev/pkg/renderer" | ||
| @@ -415,8 +416,8 @@ func composeCopyClientFrame(mode *scopy.Mode, target domain.Rect, frame renderer | |||||
| 415 | if mode == nil || target.Width <= 0 || target.Height <= 0 || frame.Width <= 0 || frame.Height <= 0 { | 416 | if mode == nil || target.Width <= 0 || target.Height <= 0 || frame.Width <= 0 || frame.Height <= 0 { | ||
| 416 | return frame, nil | 417 | return frame, nil | ||
| 417 | } | 418 | } | ||
| 418 | - | styles := newThemeStyles(bars.theme) | 419 | + | styles := themeui.NewStyles(bars.theme) |
| 419 | - | copyFrame := mode.Render(styles.copyStatus, styles.selection) | 420 | + | copyFrame := mode.Render(styles.CopyStatus, styles.Selection) |
| 420 | bodyRows := max(copyFrame.Height-1, 0) | 421 | bodyRows := max(copyFrame.Height-1, 0) | ||
| 421 | for y := 0; y < target.Height && y < bodyRows && target.Y+y < frame.Height-1; y++ { | 422 | for y := 0; y < target.Height && y < bodyRows && target.Y+y < frame.Height-1; y++ { | ||
| 422 | dstX := max(target.X, 0) | 423 | dstX := max(target.X, 0) | ||
M internal/usecase/daemon/floating_render.go +1 -1
| @@ -117,7 +117,7 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||
| 117 | 117 | } | |
| 118 | 118 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry | |
| 119 | 119 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | |
| 120 | - | drawFloatingBorder(frame, geometry.Bounds, floating.title, newThemeStyles(theme).border) | |
| 120 | + | drawFloatingBorder(frame, geometry.Bounds, floating.title, themeui.NewStyles(theme).Border) | |
| 121 | 121 | if full || popupChanged { | |
| 122 | 122 | return frame, []renderer.Damage{renderer.FullRedraw()} | |
| 123 | 123 | } | |
| @@ -117,7 +117,7 @@ func composeCapturedFloatingFrame(input floatingComposeInput) (renderer.Frame, [ | |||||
| 117 | } | 117 | } | ||
| 118 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry | 118 | popupChanged := !cache.valid || cache.floatingGeneration != floating.generation || cache.floatingGeometry != geometry | ||
| 119 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | 119 | titleChanged := popupChanged || cache.floatingTitleGeneration != floating.titleGeneration | ||
| 120 | - | drawFloatingBorder(frame, geometry.Bounds, floating.title, newThemeStyles(theme).border) | 120 | + | drawFloatingBorder(frame, geometry.Bounds, floating.title, themeui.NewStyles(theme).Border) |
| 121 | if full || popupChanged { | 121 | if full || popupChanged { | ||
| 122 | return frame, []renderer.Damage{renderer.FullRedraw()} | 122 | return frame, []renderer.Damage{renderer.FullRedraw()} | ||
| 123 | } | 123 | } | ||
M internal/usecase/daemon/modal_compose.go +3 -2
| @@ -2,12 +2,13 @@ package daemon | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/bnema/vev/internal/domain" | |
| 5 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |
| 5 | 6 | "github.com/bnema/vev/internal/usecase/ui" | |
| 6 | 7 | "github.com/bnema/vev/pkg/renderer" | |
| 7 | 8 | ) | |
| 8 | 9 | ||
| 9 | - | func composeModalClientFrame(base renderer.Frame, modal ui.Modal, styles themeStyles, contentStyle renderer.Style, renderInner func(domain.Size, ...renderer.Style) renderer.Frame) (renderer.Frame, []renderer.Damage) { | |
| 10 | - | inner := modal.Composite(base, styles.border) | |
| 10 | + | func composeModalClientFrame(base renderer.Frame, modal ui.Modal, styles themeui.Styles, contentStyle renderer.Style, renderInner func(domain.Size, ...renderer.Style) renderer.Frame) (renderer.Frame, []renderer.Damage) { | |
| 11 | + | inner := modal.Composite(base, styles.Border) | |
| 11 | 12 | modalFrame := renderInner(domain.Size{Cols: inner.Width, Rows: inner.Height}, contentStyle) | |
| 12 | 13 | for y := range min(inner.Height, modalFrame.Height) { | |
| 13 | 14 | for x := range min(inner.Width, modalFrame.Width) { | |
| @@ -2,12 +2,13 @@ package daemon | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/bnema/vev/internal/domain" | 4 | "github.com/bnema/vev/internal/domain" | ||
| 5 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |||
| 5 | "github.com/bnema/vev/internal/usecase/ui" | 6 | "github.com/bnema/vev/internal/usecase/ui" | ||
| 6 | "github.com/bnema/vev/pkg/renderer" | 7 | "github.com/bnema/vev/pkg/renderer" | ||
| 7 | ) | 8 | ) | ||
| 8 | 9 | ||||
| 9 | - | func composeModalClientFrame(base renderer.Frame, modal ui.Modal, styles themeStyles, contentStyle renderer.Style, renderInner func(domain.Size, ...renderer.Style) renderer.Frame) (renderer.Frame, []renderer.Damage) { | 10 | + | func composeModalClientFrame(base renderer.Frame, modal ui.Modal, styles themeui.Styles, contentStyle renderer.Style, renderInner func(domain.Size, ...renderer.Style) renderer.Frame) (renderer.Frame, []renderer.Damage) { |
| 10 | - | inner := modal.Composite(base, styles.border) | 11 | + | inner := modal.Composite(base, styles.Border) |
| 11 | modalFrame := renderInner(domain.Size{Cols: inner.Width, Rows: inner.Height}, contentStyle) | 12 | modalFrame := renderInner(domain.Size{Cols: inner.Width, Rows: inner.Height}, contentStyle) | ||
| 12 | for y := range min(inner.Height, modalFrame.Height) { | 13 | for y := range min(inner.Height, modalFrame.Height) { | ||
| 13 | for x := range min(inner.Width, modalFrame.Width) { | 14 | for x := range min(inner.Width, modalFrame.Width) { | ||
M internal/usecase/daemon/palette.go +5 -4
| @@ -9,6 +9,7 @@ import ( | |||
| 9 | 9 | "github.com/bnema/vev/internal/usecase/layout" | |
| 10 | 10 | "github.com/bnema/vev/internal/usecase/palette" | |
| 11 | 11 | "github.com/bnema/vev/internal/usecase/picker" | |
| 12 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |
| 12 | 13 | "github.com/bnema/vev/internal/usecase/ui" | |
| 13 | 14 | "github.com/bnema/vev/pkg/renderer" | |
| 14 | 15 | ) | |
| @@ -496,10 +497,10 @@ func (e paletteExec) JumpRecentSession(rank int) error { | |||
| 496 | 497 | return nil | |
| 497 | 498 | } | |
| 498 | 499 | ||
| 499 | - | func composePaletteClientFrame(model *palette.Model, base renderer.Frame, cfg domain.PaletteConfig, guidance string, styles ...themeStyles) (renderer.Frame, []renderer.Damage) { | |
| 500 | - | styleSet := resolveThemeStyles(styles) | |
| 500 | + | func composePaletteClientFrame(model *palette.Model, base renderer.Frame, cfg domain.PaletteConfig, guidance string, styles ...themeui.Styles) (renderer.Frame, []renderer.Damage) { | |
| 501 | + | styleSet := resolveStyles(styles) | |
| 501 | 502 | modal := paletteModalFor(domain.Size{Cols: base.Width, Rows: base.Height}, cfg) | |
| 502 | - | return composeModalClientFrame(base, modal, styleSet, styleSet.selection, func(size domain.Size, _ ...renderer.Style) renderer.Frame { | |
| 503 | - | return model.Render(size, palette.RenderOptions{Styles: palette.RenderStyles{Selection: styleSet.selection, Description: styleSet.paletteDesc}, Guidance: guidance}) | |
| 503 | + | return composeModalClientFrame(base, modal, styleSet, styleSet.Selection, func(size domain.Size, _ ...renderer.Style) renderer.Frame { | |
| 504 | + | return model.Render(size, palette.RenderOptions{Styles: palette.RenderStyles{Selection: styleSet.Selection, Description: styleSet.PaletteDesc}, Guidance: guidance}) | |
| 504 | 505 | }) | |
| 505 | 506 | } | |
| @@ -9,6 +9,7 @@ import ( | |||||
| 9 | "github.com/bnema/vev/internal/usecase/layout" | 9 | "github.com/bnema/vev/internal/usecase/layout" | ||
| 10 | "github.com/bnema/vev/internal/usecase/palette" | 10 | "github.com/bnema/vev/internal/usecase/palette" | ||
| 11 | "github.com/bnema/vev/internal/usecase/picker" | 11 | "github.com/bnema/vev/internal/usecase/picker" | ||
| 12 | + | themeui "github.com/bnema/vev/internal/usecase/theme" | |||
| 12 | "github.com/bnema/vev/internal/usecase/ui" | 13 | "github.com/bnema/vev/internal/usecase/ui" | ||
| 13 | "github.com/bnema/vev/pkg/renderer" | 14 | "github.com/bnema/vev/pkg/renderer" | ||
| 14 | ) | 15 | ) | ||
| @@ -496,10 +497,10 @@ func (e paletteExec) JumpRecentSession(rank int) error { | |||||
| 496 | return nil | 497 | return nil | ||
| 497 | } | 498 | } | ||
| 498 | 499 | ||||
| 499 | - | func composePaletteClientFrame(model *palette.Model, base renderer.Frame, cfg domain.PaletteConfig, guidance string, styles ...themeStyles) (renderer.Frame, []renderer.Damage) { | 500 | + | func composePaletteClientFrame(model *palette.Model, base renderer.Frame, cfg domain.PaletteConfig, guidance string, styles ...themeui.Styles) (renderer.Frame, []renderer.Damage) { |
| 500 | - | styleSet := resolveThemeStyles(styles) | 501 | + | styleSet := resolveStyles(styles) |
| 501 | modal := paletteModalFor(domain.Size{Cols: base.Width, Rows: base.Height}, cfg) | 502 | modal := paletteModalFor(domain.Size{Cols: base.Width, Rows: base.Height}, cfg) | ||
| 502 | - | return composeModalClientFrame(base, modal, styleSet, styleSet.selection, func(size domain.Size, _ ...renderer.Style) renderer.Frame { | 503 | + | return composeModalClientFrame(base, modal, styleSet, styleSet.Selection, func(size domain.Size, _ ...renderer.Style) renderer.Frame { |
| 503 | - | return model.Render(size, palette.RenderOptions{Styles: palette.RenderStyles{Selection: styleSet.selection, Description: styleSet.paletteDesc}, Guidance: guidance}) | 504 | + | return model.Render(size, palette.RenderOptions{Styles: palette.RenderStyles{Selection: styleSet.Selection, Description: styleSet.PaletteDesc}, Guidance: guidance}) |
| 504 | }) | 505 | }) | ||
| 505 | } | 506 | } | ||
M internal/usecase/daemon/render.go +2 -50
| @@ -357,59 +357,11 @@ func (d *Daemon) paint(sess *session, ac *attachedClient, reset bool, lease *att | |||
| 357 | 357 | d.emitFrame(sess, ac, state, composed, &marks) | |
| 358 | 358 | } | |
| 359 | 359 | ||
| 360 | - | type themeStyles struct { | |
| 361 | - | statusBar renderer.Style | |
| 362 | - | accent renderer.Style | |
| 363 | - | border renderer.Style | |
| 364 | - | selection renderer.Style | |
| 365 | - | copyStatus renderer.Style | |
| 366 | - | paletteDesc renderer.Style | |
| 367 | - | ||
| 368 | - | // Top bar tab label segments: name (bold) and parenthesized pane-title | |
| 369 | - | // (muted), one pair per base style. No-ops to statusBar/accent on | |
| 370 | - | // non-truecolor themes. | |
| 371 | - | tabName renderer.Style | |
| 372 | - | tabNameActive renderer.Style | |
| 373 | - | tabTitle renderer.Style | |
| 374 | - | tabTitleActive renderer.Style | |
| 375 | - | ||
| 376 | - | // Session picker row segments. Detail reuses paletteDesc (both are | |
| 377 | - | // themeui.MutedTextStyle(t)); pickerName/pickerSelection* are picker-only. | |
| 378 | - | pickerName renderer.Style | |
| 379 | - | pickerSelectionName renderer.Style | |
| 380 | - | pickerSelectionMuted renderer.Style | |
| 381 | - | pickerSeparator renderer.Style | |
| 382 | - | } | |
| 383 | - | ||
| 384 | - | func newThemeStyles(t themeui.Theme) themeStyles { | |
| 385 | - | statusBar := themeui.StatusBarStyle(t) | |
| 386 | - | accent := themeui.AccentStyle(t) | |
| 387 | - | selection := themeui.SelectionStyle(t) | |
| 388 | - | return themeStyles{ | |
| 389 | - | statusBar: statusBar, | |
| 390 | - | accent: accent, | |
| 391 | - | border: themeui.BorderStyle(t), | |
| 392 | - | selection: selection, | |
| 393 | - | copyStatus: selection, | |
| 394 | - | paletteDesc: themeui.MutedTextStyle(t), | |
| 395 | - | ||
| 396 | - | tabName: themeui.EmphasisStyle(statusBar, t), | |
| 397 | - | tabNameActive: themeui.EmphasisStyle(accent, t), | |
| 398 | - | tabTitle: themeui.MutedVariantStyle(statusBar, t), | |
| 399 | - | tabTitleActive: themeui.MutedVariantStyle(accent, t), | |
| 400 | - | ||
| 401 | - | pickerName: themeui.EmphasisStyle(renderer.DefaultStyle(), t), | |
| 402 | - | pickerSelectionName: themeui.EmphasisStyle(selection, t), | |
| 403 | - | pickerSelectionMuted: themeui.MutedVariantStyle(selection, t), | |
| 404 | - | pickerSeparator: themeui.MutedTextStyle(t), | |
| 405 | - | } | |
| 406 | - | } | |
| 407 | - | ||
| 408 | - | func resolveThemeStyles(styles []themeStyles) themeStyles { | |
| 360 | + | func resolveStyles(styles []themeui.Styles) themeui.Styles { | |
| 409 | 361 | if len(styles) > 0 { | |
| 410 | 362 | return styles[0] | |
| 411 | 363 | } | |
| 412 | - | return newThemeStyles(themeui.Theme{}) | |
| 364 | + | return themeui.NewStyles(themeui.Theme{}) | |
| 413 | 365 | } | |
| 414 | 366 | ||
| 415 | 367 | type barCache struct { | |
| @@ -357,59 +357,11 @@ func (d *Daemon) paint(sess *session, ac *attachedClient, reset bool, lease *att | |||||
| 357 | d.emitFrame(sess, ac, state, composed, &marks) | 357 | d.emitFrame(sess, ac, state, composed, &marks) | ||
| 358 | } | 358 | } | ||
| 359 | 359 | ||||
| 360 | - | type themeStyles struct { | 360 | + | func resolveStyles(styles []themeui.Styles) themeui.Styles { |
| 361 | - | statusBar renderer.Style | |||
| 362 | - | accent renderer.Style | |||
| 363 | - | border renderer.Style | |||
| 364 | - | selection renderer.Style | |||
| 365 | - | copyStatus renderer.Style | |||
| 366 | - | paletteDesc renderer.Style | |||
| 367 | - | ||||
| 368 | - | // Top bar tab label segments: name (bold) and parenthesized pane-title | |||
| 369 | - | // (muted), one pair per base style. No-ops to statusBar/accent on | |||
| 370 | - | // non-truecolor themes. | |||
| 371 | - | tabName renderer.Style | |||
| 372 | - | tabNameActive renderer.Style | |||
| 373 | - | tabTitle renderer.Style | |||
| 374 | - | tabTitleActive renderer.Style | |||
| 375 | - | ||||
| 376 | - | // Session picker row segments. Detail reuses paletteDesc (both are | |||
| 377 | - | // themeui.MutedTextStyle(t)); pickerName/pickerSelection* are picker-only. | |||
| 378 | - | pickerName renderer.Style | |||
| 379 | - | pickerSelectionName renderer.Style | |||
| 380 | - | pickerSelectionMuted renderer.Style | |||
| 381 | - | pickerSeparator renderer.Style | |||
| 382 | - | } | |||
| 383 | - | ||||
| 384 | - | func newThemeStyles(t themeui.Theme) themeStyles { | |||
| 385 | - | statusBar := themeui.StatusBarStyle(t) | |||
| 386 | - | accent := themeui.AccentStyle(t) | |||
| 387 | - | selection := themeui.SelectionStyle(t) | |||
| 388 | - | return themeStyles{ | |||
| 389 | - | statusBar: statusBar, | |||
| 390 | - | accent: accent, | |||
| 391 | - | border: themeui.BorderStyle(t), | |||
| 392 | - | selection: selection, | |||
| 393 | - | copyStatus: selection, | |||
| 394 | - | paletteDesc: themeui.MutedTextStyle(t), | |||
| 395 | - | ||||
| 396 | - | tabName: themeui.EmphasisStyle(statusBar, t), | |||
| 397 | - | tabNameActive: themeui.EmphasisStyle(accent, t), | |||
| 398 | - | tabTitle: themeui.MutedVariantStyle(statusBar, t), | |||
| 399 | - | tabTitleActive: themeui.MutedVariantStyle(accent, t), | |||
| 400 | - | ||||
| 401 | - | pickerName: themeui.EmphasisStyle(renderer.DefaultStyle(), t), | |||
| 402 | - | pickerSelectionName: themeui.EmphasisStyle(selection, t), | |||
| 403 | - | pickerSelectionMuted: themeui.MutedVariantStyle(selection, t), | |||
| 404 | - | pickerSeparator: themeui.MutedTextStyle(t), | |||
| 405 | - | } | |||
| 406 | - | } | |||
| 407 | - | ||||
| 408 | - | func resolveThemeStyles(styles []themeStyles) themeStyles { | |||
| 409 | if len(styles) > 0 { | 361 | if len(styles) > 0 { | ||
| 410 | return styles[0] | 362 | return styles[0] | ||
| 411 | } | 363 | } | ||
| 412 | - | return newThemeStyles(themeui.Theme{}) | 364 | + | return themeui.NewStyles(themeui.Theme{}) |
| 413 | } | 365 | } | ||
| 414 | 366 | ||||
| 415 | type barCache struct { | 367 | type barCache struct { | ||
M internal/usecase/daemon/render_pipeline.go +11 -11
| @@ -77,14 +77,14 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||
| 77 | 77 | copy(frame.Row(y), in.frame.Row(y)) | |
| 78 | 78 | } | |
| 79 | 79 | } | |
| 80 | - | styles := newThemeStyles(state.theme) | |
| 80 | + | styles := themeui.NewStyles(state.theme) | |
| 81 | 81 | defaultDimmer := themeui.NewDimmer(state.theme) | |
| 82 | 82 | inactivePaneDimmer := themeui.NewDimmer(state.theme, themeui.WithForegroundDimming(inactivePaneForegroundDimming)) | |
| 83 | 83 | drawTopBarSnapshot(frame.Row(0), state.bars.status, state.bars.attentionFrame, state.bars.topRight, styles) | |
| 84 | 84 | drawStatusBarState(frame.Row(rows+1), state.bars, styles) | |
| 85 | 85 | content := domain.Rect{Y: 1, Width: width, Height: rows} | |
| 86 | 86 | if state.layout.valid && state.layout.root != nil { | |
| 87 | - | drawDividers(frame, state.layout.root, content, defaultDimmer.Dim(styles.border)) | |
| 87 | + | drawDividers(frame, state.layout.root, content, defaultDimmer.Dim(styles.Border)) | |
| 88 | 88 | } | |
| 89 | 89 | ||
| 90 | 90 | full := state.reset || !in.valid || in.frame.Width != width || in.frame.Height != rows+2 || in.layoutFingerprint != state.layout.fingerprint || in.theme != state.theme || in.floatingVisible != state.floating.visible | |
| @@ -157,10 +157,10 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||
| 157 | 157 | return composedRenderFrame{frame: frame, damage: damage, cursor: cursor, cache: outCache, reset: state.reset || state.overlays.active()} | |
| 158 | 158 | } | |
| 159 | 159 | ||
| 160 | - | func drawCapturedPaneTitleBar(frame renderer.Frame, pl layout.Placement, title string, focused bool, styles themeStyles, dimmer themeui.Dimmer) { | |
| 161 | - | style := styles.border | |
| 160 | + | func drawCapturedPaneTitleBar(frame renderer.Frame, pl layout.Placement, title string, focused bool, styles themeui.Styles, dimmer themeui.Dimmer) { | |
| 161 | + | style := styles.Border | |
| 162 | 162 | if focused { | |
| 163 | - | style = styles.statusBar | |
| 163 | + | style = styles.StatusBar | |
| 164 | 164 | } else { | |
| 165 | 165 | style = dimmer.Dim(style) | |
| 166 | 166 | } | |
| @@ -191,15 +191,15 @@ func captureOverlayLayers(state *capturedRenderState, snap *overlayRenderSnapsho | |||
| 191 | 191 | if snap.copyPane != nil { | |
| 192 | 192 | o.copyPaneID = snap.copyPane.id | |
| 193 | 193 | } | |
| 194 | - | styles := newThemeStyles(state.theme) | |
| 194 | + | styles := themeui.NewStyles(state.theme) | |
| 195 | 195 | size := domain.Size{Cols: state.layout.area.Width, Rows: state.layout.area.Height + 2} | |
| 196 | 196 | if snap.copySearchModel != nil { | |
| 197 | 197 | o.copySearch.modal = copySearchModal | |
| 198 | - | o.copySearch.inner = snap.copySearchModel.Render(rectSize(copySearchModal.Inner(size)), styles.selection) | |
| 198 | + | o.copySearch.inner = snap.copySearchModel.Render(rectSize(copySearchModal.Inner(size)), styles.Selection) | |
| 199 | 199 | } | |
| 200 | 200 | if snap.pickerActive && snap.pickerModel != nil { | |
| 201 | 201 | o.picker.modal = pickerModal | |
| 202 | - | renderStyles := picker.RenderStyles{Selection: styles.selection, SelectionName: styles.pickerSelectionName, SelectionMuted: styles.pickerSelectionMuted, Name: styles.pickerName, Detail: styles.paletteDesc, Base: renderer.DefaultStyle(), Separator: styles.pickerSeparator} | |
| 202 | + | renderStyles := picker.RenderStyles{Selection: styles.Selection, SelectionName: styles.PickerSelectionName, SelectionMuted: styles.PickerSelectionMuted, Name: styles.PickerName, Detail: styles.PaletteDesc, Base: renderer.DefaultStyle(), Separator: styles.PickerSeparator} | |
| 203 | 203 | o.picker.inner = snap.pickerModel.Render(rectSize(pickerModal.Inner(size)), state.preview, renderStyles) | |
| 204 | 204 | } | |
| 205 | 205 | if snap.paletteActive && snap.paletteModel != nil { | |
| @@ -209,11 +209,11 @@ func captureOverlayLayers(state *capturedRenderState, snap *overlayRenderSnapsho | |||
| 209 | 209 | guidance = snap.paletteHints.Feedback | |
| 210 | 210 | } | |
| 211 | 211 | o.paletteGuidance = snap.paletteFeedback | |
| 212 | - | o.palette.inner = snap.paletteModel.Render(rectSize(o.palette.modal.Inner(size)), palette.RenderOptions{Styles: palette.RenderStyles{Selection: styles.selection, Description: styles.paletteDesc}, Guidance: guidance, Feedback: snap.paletteFeedback}) | |
| 212 | + | o.palette.inner = snap.paletteModel.Render(rectSize(o.palette.modal.Inner(size)), palette.RenderOptions{Styles: palette.RenderStyles{Selection: styles.Selection, Description: styles.PaletteDesc}, Guidance: guidance, Feedback: snap.paletteFeedback}) | |
| 213 | 213 | } | |
| 214 | 214 | if snap.promptActive && snap.promptModel != nil { | |
| 215 | 215 | o.prompt.modal = promptModalFor(snap.promptModel.Title()) | |
| 216 | - | o.prompt.inner = snap.promptModel.Render(rectSize(o.prompt.modal.Inner(size)), styles.accent) | |
| 216 | + | o.prompt.inner = snap.promptModel.Render(rectSize(o.prompt.modal.Inner(size)), styles.Accent) | |
| 217 | 217 | } | |
| 218 | 218 | state.cursor.hiddenByOverlay = o.active() | |
| 219 | 219 | } | |
| @@ -248,7 +248,7 @@ func composeCapturedOverlays(state capturedRenderState, frame renderer.Frame, da | |||
| 248 | 248 | if modal.inner.Width == 0 && modal.inner.Height == 0 { | |
| 249 | 249 | continue | |
| 250 | 250 | } | |
| 251 | - | inner := modal.modal.Composite(frame, newThemeStyles(state.theme).border) | |
| 251 | + | inner := modal.modal.Composite(frame, themeui.NewStyles(state.theme).Border) | |
| 252 | 252 | for y := range min(inner.Height, modal.inner.Height) { | |
| 253 | 253 | copy(frame.Row(inner.Y + y)[inner.X:inner.X+min(inner.Width, modal.inner.Width)], modal.inner.Row(y)[:min(inner.Width, modal.inner.Width)]) | |
| 254 | 254 | } | |
| @@ -77,14 +77,14 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||||
| 77 | copy(frame.Row(y), in.frame.Row(y)) | 77 | copy(frame.Row(y), in.frame.Row(y)) | ||
| 78 | } | 78 | } | ||
| 79 | } | 79 | } | ||
| 80 | - | styles := newThemeStyles(state.theme) | 80 | + | styles := themeui.NewStyles(state.theme) |
| 81 | defaultDimmer := themeui.NewDimmer(state.theme) | 81 | defaultDimmer := themeui.NewDimmer(state.theme) | ||
| 82 | inactivePaneDimmer := themeui.NewDimmer(state.theme, themeui.WithForegroundDimming(inactivePaneForegroundDimming)) | 82 | inactivePaneDimmer := themeui.NewDimmer(state.theme, themeui.WithForegroundDimming(inactivePaneForegroundDimming)) | ||
| 83 | drawTopBarSnapshot(frame.Row(0), state.bars.status, state.bars.attentionFrame, state.bars.topRight, styles) | 83 | drawTopBarSnapshot(frame.Row(0), state.bars.status, state.bars.attentionFrame, state.bars.topRight, styles) | ||
| 84 | drawStatusBarState(frame.Row(rows+1), state.bars, styles) | 84 | drawStatusBarState(frame.Row(rows+1), state.bars, styles) | ||
| 85 | content := domain.Rect{Y: 1, Width: width, Height: rows} | 85 | content := domain.Rect{Y: 1, Width: width, Height: rows} | ||
| 86 | if state.layout.valid && state.layout.root != nil { | 86 | if state.layout.valid && state.layout.root != nil { | ||
| 87 | - | drawDividers(frame, state.layout.root, content, defaultDimmer.Dim(styles.border)) | 87 | + | drawDividers(frame, state.layout.root, content, defaultDimmer.Dim(styles.Border)) |
| 88 | } | 88 | } | ||
| 89 | 89 | ||||
| 90 | full := state.reset || !in.valid || in.frame.Width != width || in.frame.Height != rows+2 || in.layoutFingerprint != state.layout.fingerprint || in.theme != state.theme || in.floatingVisible != state.floating.visible | 90 | full := state.reset || !in.valid || in.frame.Width != width || in.frame.Height != rows+2 || in.layoutFingerprint != state.layout.fingerprint || in.theme != state.theme || in.floatingVisible != state.floating.visible | ||
| @@ -157,10 +157,10 @@ func composeFrame(state capturedRenderState, in composeCacheInput, scratchIn ... | |||||
| 157 | return composedRenderFrame{frame: frame, damage: damage, cursor: cursor, cache: outCache, reset: state.reset || state.overlays.active()} | 157 | return composedRenderFrame{frame: frame, damage: damage, cursor: cursor, cache: outCache, reset: state.reset || state.overlays.active()} | ||
| 158 | } | 158 | } | ||
| 159 | 159 | ||||
| 160 | - | func drawCapturedPaneTitleBar(frame renderer.Frame, pl layout.Placement, title string, focused bool, styles themeStyles, dimmer themeui.Dimmer) { | 160 | + | func drawCapturedPaneTitleBar(frame renderer.Frame, pl layout.Placement, title string, focused bool, styles themeui.Styles, dimmer themeui.Dimmer) { |
| 161 | - | style := styles.border | 161 | + | style := styles.Border |
| 162 | if focused { | 162 | if focused { | ||
| 163 | - | style = styles.statusBar | 163 | + | style = styles.StatusBar |
| 164 | } else { | 164 | } else { | ||
| 165 | style = dimmer.Dim(style) | 165 | style = dimmer.Dim(style) | ||
| 166 | } | 166 | } | ||
| @@ -191,15 +191,15 @@ func captureOverlayLayers(state *capturedRenderState, snap *overlayRenderSnapsho | |||||
| 191 | if snap.copyPane != nil { | 191 | if snap.copyPane != nil { | ||
| 192 | o.copyPaneID = snap.copyPane.id | 192 | o.copyPaneID = snap.copyPane.id | ||
| 193 | } | 193 | } | ||
| 194 | - | styles := newThemeStyles(state.theme) | 194 | + | styles := themeui.NewStyles(state.theme) |
| 195 | size := domain.Size{Cols: state.layout.area.Width, Rows: state.layout.area.Height + 2} | 195 | size := domain.Size{Cols: state.layout.area.Width, Rows: state.layout.area.Height + 2} | ||
| 196 | if snap.copySearchModel != nil { | 196 | if snap.copySearchModel != nil { | ||
| 197 | o.copySearch.modal = copySearchModal | 197 | o.copySearch.modal = copySearchModal | ||
| 198 | - | o.copySearch.inner = snap.copySearchModel.Render(rectSize(copySearchModal.Inner(size)), styles.selection) | 198 | + | o.copySearch.inner = snap.copySearchModel.Render(rectSize(copySearchModal.Inner(size)), styles.Selection) |
| 199 | } | 199 | } | ||
| 200 | if snap.pickerActive && snap.pickerModel != nil { | 200 | if snap.pickerActive && snap.pickerModel != nil { | ||
| 201 | o.picker.modal = pickerModal | 201 | o.picker.modal = pickerModal | ||
| 202 | - | renderStyles := picker.RenderStyles{Selection: styles.selection, SelectionName: styles.pickerSelectionName, SelectionMuted: styles.pickerSelectionMuted, Name: styles.pickerName, Detail: styles.paletteDesc, Base: renderer.DefaultStyle(), Separator: styles.pickerSeparator} | 202 | + | renderStyles := picker.RenderStyles{Selection: styles.Selection, SelectionName: styles.PickerSelectionName, SelectionMuted: styles.PickerSelectionMuted, Name: styles.PickerName, Detail: styles.PaletteDesc, Base: renderer.DefaultStyle(), Separator: styles.PickerSeparator} |
| 203 | o.picker.inner = snap.pickerModel.Render(rectSize(pickerModal.Inner(size)), state.preview, renderStyles) | 203 | o.picker.inner = snap.pickerModel.Render(rectSize(pickerModal.Inner(size)), state.preview, renderStyles) | ||
| 204 | } | 204 | } | ||
| 205 | if snap.paletteActive && snap.paletteModel != nil { | 205 | if snap.paletteActive && snap.paletteModel != nil { | ||
| @@ -209,11 +209,11 @@ func captureOverlayLayers(state *capturedRenderState, snap *overlayRenderSnapsho | |||||
| 209 | guidance = snap.paletteHints.Feedback | 209 | guidance = snap.paletteHints.Feedback | ||
| 210 | } | 210 | } | ||
| 211 | o.paletteGuidance = snap.paletteFeedback | 211 | o.paletteGuidance = snap.paletteFeedback | ||
| 212 | - | o.palette.inner = snap.paletteModel.Render(rectSize(o.palette.modal.Inner(size)), palette.RenderOptions{Styles: palette.RenderStyles{Selection: styles.selection, Description: styles.paletteDesc}, Guidance: guidance, Feedback: snap.paletteFeedback}) | 212 | + | o.palette.inner = snap.paletteModel.Render(rectSize(o.palette.modal.Inner(size)), palette.RenderOptions{Styles: palette.RenderStyles{Selection: styles.Selection, Description: styles.PaletteDesc}, Guidance: guidance, Feedback: snap.paletteFeedback}) |
| 213 | } | 213 | } | ||
| 214 | if snap.promptActive && snap.promptModel != nil { | 214 | if snap.promptActive && snap.promptModel != nil { | ||
| 215 | o.prompt.modal = promptModalFor(snap.promptModel.Title()) | 215 | o.prompt.modal = promptModalFor(snap.promptModel.Title()) | ||
| 216 | - | o.prompt.inner = snap.promptModel.Render(rectSize(o.prompt.modal.Inner(size)), styles.accent) | 216 | + | o.prompt.inner = snap.promptModel.Render(rectSize(o.prompt.modal.Inner(size)), styles.Accent) |
| 217 | } | 217 | } | ||
| 218 | state.cursor.hiddenByOverlay = o.active() | 218 | state.cursor.hiddenByOverlay = o.active() | ||
| 219 | } | 219 | } | ||
| @@ -248,7 +248,7 @@ func composeCapturedOverlays(state capturedRenderState, frame renderer.Frame, da | |||||
| 248 | if modal.inner.Width == 0 && modal.inner.Height == 0 { | 248 | if modal.inner.Width == 0 && modal.inner.Height == 0 { | ||
| 249 | continue | 249 | continue | ||
| 250 | } | 250 | } | ||
| 251 | - | inner := modal.modal.Composite(frame, newThemeStyles(state.theme).border) | 251 | + | inner := modal.modal.Composite(frame, themeui.NewStyles(state.theme).Border) |
| 252 | for y := range min(inner.Height, modal.inner.Height) { | 252 | for y := range min(inner.Height, modal.inner.Height) { | ||
| 253 | copy(frame.Row(inner.Y + y)[inner.X:inner.X+min(inner.Width, modal.inner.Width)], modal.inner.Row(y)[:min(inner.Width, modal.inner.Width)]) | 253 | copy(frame.Row(inner.Y + y)[inner.X:inner.X+min(inner.Width, modal.inner.Width)], modal.inner.Row(y)[:min(inner.Width, modal.inner.Width)]) | ||
| 254 | } | 254 | } | ||
M internal/usecase/daemon/status.go +15 -15
| @@ -51,18 +51,18 @@ func pulseStyle(frame int, base renderer.Style) (renderer.Style, bool) { | |||
| 51 | 51 | return style, true | |
| 52 | 52 | } | |
| 53 | 53 | ||
| 54 | - | func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, topRight string, styles themeStyles) { | |
| 54 | + | func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, topRight string, styles themeui.Styles) { | |
| 55 | 55 | clearStatusRow(row) | |
| 56 | 56 | x := 0 | |
| 57 | 57 | labels := fitTabLabels(status.tabs, len(row), topRight) | |
| 58 | 58 | for i, w := range status.tabs { | |
| 59 | - | baseStyle := styles.statusBar | |
| 60 | - | nameStyle := styles.tabName | |
| 61 | - | titleStyle := styles.tabTitle | |
| 59 | + | baseStyle := styles.StatusBar | |
| 60 | + | nameStyle := styles.TabName | |
| 61 | + | titleStyle := styles.TabTitle | |
| 62 | 62 | if w.active { | |
| 63 | - | baseStyle = styles.accent | |
| 64 | - | nameStyle = styles.tabNameActive | |
| 65 | - | titleStyle = styles.tabTitleActive | |
| 63 | + | baseStyle = styles.Accent | |
| 64 | + | nameStyle = styles.TabNameActive | |
| 65 | + | titleStyle = styles.TabTitleActive | |
| 66 | 66 | } | |
| 67 | 67 | label := labels[i] | |
| 68 | 68 | writeStatusText(row, &x, " "+label.text[:label.nameLen], nameStyle) | |
| @@ -73,30 +73,30 @@ func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, t | |||
| 73 | 73 | writeStatusText(row, &x, label.text[label.nameLen:], titleStyle) | |
| 74 | 74 | writeStatusText(row, &x, " ", baseStyle) | |
| 75 | 75 | } | |
| 76 | - | drawRightPlainText(row, topRight, x, styles.statusBar) | |
| 76 | + | drawRightPlainText(row, topRight, x, styles.StatusBar) | |
| 77 | 77 | } | |
| 78 | 78 | ||
| 79 | - | func drawStatusBarState(row []renderer.Cell, state barState, styles themeStyles) { | |
| 79 | + | func drawStatusBarState(row []renderer.Cell, state barState, styles themeui.Styles) { | |
| 80 | 80 | clearStatusRow(row) | |
| 81 | 81 | x := 0 | |
| 82 | 82 | rightText := composeBottomRightText(state.bottomRight, state.copyFeedback) | |
| 83 | - | writeStatusText(row, &x, " "+state.status.session+" ", styles.accent) | |
| 83 | + | writeStatusText(row, &x, " "+state.status.session+" ", styles.Accent) | |
| 84 | 84 | if state.rankedRecent != nil { | |
| 85 | 85 | for _, sess := range fitRankedRecent(state.rankedRecent, len(row), x, rightText) { | |
| 86 | - | style := styles.statusBar // contextual ranks deliberately do not fade. | |
| 86 | + | style := styles.StatusBar // contextual ranks deliberately do not fade. | |
| 87 | 87 | if sess.selected { | |
| 88 | - | style = styles.accent | |
| 88 | + | style = styles.Accent | |
| 89 | 89 | } | |
| 90 | 90 | drawRankedStatusSessionEntry(row, &x, sess, style, state.attentionFrame) | |
| 91 | 91 | } | |
| 92 | 92 | } else { | |
| 93 | 93 | fittedMRU := fitMRU(state.mru, len(row), x, rightText) | |
| 94 | 94 | for i, sess := range fittedMRU { | |
| 95 | - | style := mruStyle(styles.statusBar, state.theme, i, len(fittedMRU)) | |
| 95 | + | style := mruStyle(styles.StatusBar, state.theme, i, len(fittedMRU)) | |
| 96 | 96 | drawStatusSessionEntry(row, &x, sess.name, sess.attention, style, state.attentionFrame) | |
| 97 | 97 | } | |
| 98 | 98 | } | |
| 99 | - | drawRightPlainText(row, rightText, x, styles.statusBar) | |
| 99 | + | drawRightPlainText(row, rightText, x, styles.StatusBar) | |
| 100 | 100 | } | |
| 101 | 101 | ||
| 102 | 102 | type rankedRecent struct { | |
| @@ -170,7 +170,7 @@ type barState struct { | |||
| 170 | 170 | attentionFrame int | |
| 171 | 171 | // theme is the client's terminal theme, if reported. Its zero value | |
| 172 | 172 | // (Theme{}, Known: false) is a valid "no theme" default that resolves to | |
| 173 | - | // the pre-theme fallback styles (see newThemeStyles / theme.usable). | |
| 173 | + | // the pre-theme fallback styles (see themeui.NewStyles / theme.usable). | |
| 174 | 174 | theme themeui.Theme | |
| 175 | 175 | } | |
| 176 | 176 | ||
| @@ -51,18 +51,18 @@ func pulseStyle(frame int, base renderer.Style) (renderer.Style, bool) { | |||||
| 51 | return style, true | 51 | return style, true | ||
| 52 | } | 52 | } | ||
| 53 | 53 | ||||
| 54 | - | func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, topRight string, styles themeStyles) { | 54 | + | func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, topRight string, styles themeui.Styles) { |
| 55 | clearStatusRow(row) | 55 | clearStatusRow(row) | ||
| 56 | x := 0 | 56 | x := 0 | ||
| 57 | labels := fitTabLabels(status.tabs, len(row), topRight) | 57 | labels := fitTabLabels(status.tabs, len(row), topRight) | ||
| 58 | for i, w := range status.tabs { | 58 | for i, w := range status.tabs { | ||
| 59 | - | baseStyle := styles.statusBar | 59 | + | baseStyle := styles.StatusBar |
| 60 | - | nameStyle := styles.tabName | 60 | + | nameStyle := styles.TabName |
| 61 | - | titleStyle := styles.tabTitle | 61 | + | titleStyle := styles.TabTitle |
| 62 | if w.active { | 62 | if w.active { | ||
| 63 | - | baseStyle = styles.accent | 63 | + | baseStyle = styles.Accent |
| 64 | - | nameStyle = styles.tabNameActive | 64 | + | nameStyle = styles.TabNameActive |
| 65 | - | titleStyle = styles.tabTitleActive | 65 | + | titleStyle = styles.TabTitleActive |
| 66 | } | 66 | } | ||
| 67 | label := labels[i] | 67 | label := labels[i] | ||
| 68 | writeStatusText(row, &x, " "+label.text[:label.nameLen], nameStyle) | 68 | writeStatusText(row, &x, " "+label.text[:label.nameLen], nameStyle) | ||
| @@ -73,30 +73,30 @@ func drawTopBarSnapshot(row []renderer.Cell, status statusSnapshot, frame int, t | |||||
| 73 | writeStatusText(row, &x, label.text[label.nameLen:], titleStyle) | 73 | writeStatusText(row, &x, label.text[label.nameLen:], titleStyle) | ||
| 74 | writeStatusText(row, &x, " ", baseStyle) | 74 | writeStatusText(row, &x, " ", baseStyle) | ||
| 75 | } | 75 | } | ||
| 76 | - | drawRightPlainText(row, topRight, x, styles.statusBar) | 76 | + | drawRightPlainText(row, topRight, x, styles.StatusBar) |
| 77 | } | 77 | } | ||
| 78 | 78 | ||||
| 79 | - | func drawStatusBarState(row []renderer.Cell, state barState, styles themeStyles) { | 79 | + | func drawStatusBarState(row []renderer.Cell, state barState, styles themeui.Styles) { |
| 80 | clearStatusRow(row) | 80 | clearStatusRow(row) | ||
| 81 | x := 0 | 81 | x := 0 | ||
| 82 | rightText := composeBottomRightText(state.bottomRight, state.copyFeedback) | 82 | rightText := composeBottomRightText(state.bottomRight, state.copyFeedback) | ||
| 83 | - | writeStatusText(row, &x, " "+state.status.session+" ", styles.accent) | 83 | + | writeStatusText(row, &x, " "+state.status.session+" ", styles.Accent) |
| 84 | if state.rankedRecent != nil { | 84 | if state.rankedRecent != nil { | ||
| 85 | for _, sess := range fitRankedRecent(state.rankedRecent, len(row), x, rightText) { | 85 | for _, sess := range fitRankedRecent(state.rankedRecent, len(row), x, rightText) { | ||
| 86 | - | style := styles.statusBar // contextual ranks deliberately do not fade. | 86 | + | style := styles.StatusBar // contextual ranks deliberately do not fade. |
| 87 | if sess.selected { | 87 | if sess.selected { | ||
| 88 | - | style = styles.accent | 88 | + | style = styles.Accent |
| 89 | } | 89 | } | ||
| 90 | drawRankedStatusSessionEntry(row, &x, sess, style, state.attentionFrame) | 90 | drawRankedStatusSessionEntry(row, &x, sess, style, state.attentionFrame) | ||
| 91 | } | 91 | } | ||
| 92 | } else { | 92 | } else { | ||
| 93 | fittedMRU := fitMRU(state.mru, len(row), x, rightText) | 93 | fittedMRU := fitMRU(state.mru, len(row), x, rightText) | ||
| 94 | for i, sess := range fittedMRU { | 94 | for i, sess := range fittedMRU { | ||
| 95 | - | style := mruStyle(styles.statusBar, state.theme, i, len(fittedMRU)) | 95 | + | style := mruStyle(styles.StatusBar, state.theme, i, len(fittedMRU)) |
| 96 | drawStatusSessionEntry(row, &x, sess.name, sess.attention, style, state.attentionFrame) | 96 | drawStatusSessionEntry(row, &x, sess.name, sess.attention, style, state.attentionFrame) | ||
| 97 | } | 97 | } | ||
| 98 | } | 98 | } | ||
| 99 | - | drawRightPlainText(row, rightText, x, styles.statusBar) | 99 | + | drawRightPlainText(row, rightText, x, styles.StatusBar) |
| 100 | } | 100 | } | ||
| 101 | 101 | ||||
| 102 | type rankedRecent struct { | 102 | type rankedRecent struct { | ||
| @@ -170,7 +170,7 @@ type barState struct { | |||||
| 170 | attentionFrame int | 170 | attentionFrame int | ||
| 171 | // theme is the client's terminal theme, if reported. Its zero value | 171 | // theme is the client's terminal theme, if reported. Its zero value | ||
| 172 | // (Theme{}, Known: false) is a valid "no theme" default that resolves to | 172 | // (Theme{}, Known: false) is a valid "no theme" default that resolves to | ||
| 173 | - | // the pre-theme fallback styles (see newThemeStyles / theme.usable). | 173 | + | // the pre-theme fallback styles (see themeui.NewStyles / theme.usable). |
| 174 | theme themeui.Theme | 174 | theme themeui.Theme | ||
| 175 | } | 175 | } | ||
| 176 | 176 | ||||
M internal/usecase/daemon/status_test.go +42 -42
| @@ -267,9 +267,9 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 267 | 267 | Known: true, | |
| 268 | 268 | TrueColor: true, | |
| 269 | 269 | } | |
| 270 | - | styles := newThemeStyles(theme) | |
| 271 | - | require.False(t, styles.tabTitle.Equal(styles.statusBar), "sanity: muted title style must differ from the base style on a truecolor theme") | |
| 272 | - | require.False(t, styles.tabTitleActive.Equal(styles.accent), "sanity: muted active-title style must differ from the base accent style") | |
| 270 | + | styles := themeui.NewStyles(theme) | |
| 271 | + | require.False(t, styles.TabTitle.Equal(styles.StatusBar), "sanity: muted title style must differ from the base style on a truecolor theme") | |
| 272 | + | require.False(t, styles.TabTitleActive.Equal(styles.Accent), "sanity: muted active-title style must differ from the base accent style") | |
| 273 | 273 | ||
| 274 | 274 | labels := fitTabLabels(tabs, rowLen, "") | |
| 275 | 275 | ||
| @@ -286,17 +286,17 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 286 | 286 | nameEnd := x + 1 + labels[0].nameLen | |
| 287 | 287 | for i := x; i < nameEnd; i++ { | |
| 288 | 288 | require.True(t, row[i].Style.Bold, "cell %d: name segment must be bold", i) | |
| 289 | - | require.True(t, row[i].Style.Equal(styles.tabName), "cell %d: name segment style mismatch", i) | |
| 289 | + | require.True(t, row[i].Style.Equal(styles.TabName), "cell %d: name segment style mismatch", i) | |
| 290 | 290 | } | |
| 291 | 291 | x = nameEnd | |
| 292 | 292 | titleLen := len(labels[0].text) - labels[0].nameLen | |
| 293 | 293 | titleEnd := x + titleLen | |
| 294 | 294 | for i := x; i < titleEnd; i++ { | |
| 295 | 295 | require.False(t, row[i].Style.Bold, "cell %d: title segment must not be bold", i) | |
| 296 | - | require.True(t, row[i].Style.Equal(styles.tabTitle), "cell %d: title segment style mismatch", i) | |
| 296 | + | require.True(t, row[i].Style.Equal(styles.TabTitle), "cell %d: title segment style mismatch", i) | |
| 297 | 297 | } | |
| 298 | 298 | x = titleEnd | |
| 299 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 0 trailing space must keep the base style", x) | |
| 299 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 0 trailing space must keep the base style", x) | |
| 300 | 300 | x++ | |
| 301 | 301 | ||
| 302 | 302 | // tab 1: active, attention, no title: name, attention space+bell (both | |
| @@ -304,16 +304,16 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 304 | 304 | nameEnd = x + 1 + labels[1].nameLen | |
| 305 | 305 | for i := x; i < nameEnd; i++ { | |
| 306 | 306 | require.True(t, row[i].Style.Bold, "cell %d: active name segment must be bold", i) | |
| 307 | - | require.True(t, row[i].Style.Equal(styles.tabNameActive), "cell %d: active name segment style mismatch", i) | |
| 307 | + | require.True(t, row[i].Style.Equal(styles.TabNameActive), "cell %d: active name segment style mismatch", i) | |
| 308 | 308 | } | |
| 309 | 309 | require.Equal(t, len(labels[1].text), labels[1].nameLen, "tab 1 has no pane title, so its whole label is the name segment") | |
| 310 | 310 | x = nameEnd | |
| 311 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 attention leading space must keep the active base style", x) | |
| 311 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 attention leading space must keep the active base style", x) | |
| 312 | 312 | x++ | |
| 313 | 313 | tab1Bell := x | |
| 314 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 blank bell must keep the active base style, not DefaultStyle", x) | |
| 314 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 blank bell must keep the active base style, not DefaultStyle", x) | |
| 315 | 315 | x++ | |
| 316 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 trailing space must keep the active base style", x) | |
| 316 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 trailing space must keep the active base style", x) | |
| 317 | 317 | x++ | |
| 318 | 318 | ||
| 319 | 319 | // tab 2: inactive, attention, with title: name, attention space+bell | |
| @@ -321,22 +321,22 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 321 | 321 | nameEnd = x + 1 + labels[2].nameLen | |
| 322 | 322 | for i := x; i < nameEnd; i++ { | |
| 323 | 323 | require.True(t, row[i].Style.Bold, "cell %d: tab 2 name segment must be bold", i) | |
| 324 | - | require.True(t, row[i].Style.Equal(styles.tabName), "cell %d: tab 2 name segment style mismatch", i) | |
| 324 | + | require.True(t, row[i].Style.Equal(styles.TabName), "cell %d: tab 2 name segment style mismatch", i) | |
| 325 | 325 | } | |
| 326 | 326 | x = nameEnd | |
| 327 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 attention leading space must keep the base style", x) | |
| 327 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 attention leading space must keep the base style", x) | |
| 328 | 328 | x++ | |
| 329 | 329 | tab2Bell := x | |
| 330 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 blank bell must keep the base style, not DefaultStyle", x) | |
| 330 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 blank bell must keep the base style, not DefaultStyle", x) | |
| 331 | 331 | x++ | |
| 332 | 332 | titleLen = len(labels[2].text) - labels[2].nameLen | |
| 333 | 333 | titleEnd = x + titleLen | |
| 334 | 334 | for i := x; i < titleEnd; i++ { | |
| 335 | 335 | require.False(t, row[i].Style.Bold, "cell %d: tab 2 title segment must not be bold", i) | |
| 336 | - | require.True(t, row[i].Style.Equal(styles.tabTitle), "cell %d: tab 2 title segment style mismatch", i) | |
| 336 | + | require.True(t, row[i].Style.Equal(styles.TabTitle), "cell %d: tab 2 title segment style mismatch", i) | |
| 337 | 337 | } | |
| 338 | 338 | x = titleEnd | |
| 339 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 trailing space must keep the base style", x) | |
| 339 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 trailing space must keep the base style", x) | |
| 340 | 340 | ||
| 341 | 341 | // On a visible pulse frame, the bell glyph itself must keep the same | |
| 342 | 342 | // background as its tab's base style. | |
| @@ -344,18 +344,18 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 344 | 344 | drawTopBarSnapshot(visibleRow, status, 1, "", styles) | |
| 345 | 345 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab1Bell].Rune) | |
| 346 | 346 | require.True(t, visibleRow[tab1Bell].Style.HasBackgroundRGB) | |
| 347 | - | require.Equal(t, styles.accent.BackgroundRGB, visibleRow[tab1Bell].Style.BackgroundRGB, "tab 1's visible bell must keep the active base background") | |
| 347 | + | require.Equal(t, styles.Accent.BackgroundRGB, visibleRow[tab1Bell].Style.BackgroundRGB, "tab 1's visible bell must keep the active base background") | |
| 348 | 348 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab2Bell].Rune) | |
| 349 | 349 | require.True(t, visibleRow[tab2Bell].Style.HasBackgroundRGB) | |
| 350 | - | require.Equal(t, styles.statusBar.BackgroundRGB, visibleRow[tab2Bell].Style.BackgroundRGB, "tab 2's visible bell must keep the plain base background") | |
| 350 | + | require.Equal(t, styles.StatusBar.BackgroundRGB, visibleRow[tab2Bell].Style.BackgroundRGB, "tab 2's visible bell must keep the plain base background") | |
| 351 | 351 | }) | |
| 352 | 352 | ||
| 353 | 353 | t.Run("non-usable theme matches the pre-change fallback styles", func(t *testing.T) { | |
| 354 | - | styles := resolveThemeStyles(nil) | |
| 355 | - | require.True(t, styles.tabName.Equal(styles.statusBar)) | |
| 356 | - | require.True(t, styles.tabTitle.Equal(styles.statusBar)) | |
| 357 | - | require.True(t, styles.tabNameActive.Equal(styles.accent)) | |
| 358 | - | require.True(t, styles.tabTitleActive.Equal(styles.accent)) | |
| 354 | + | styles := resolveStyles(nil) | |
| 355 | + | require.True(t, styles.TabName.Equal(styles.StatusBar)) | |
| 356 | + | require.True(t, styles.TabTitle.Equal(styles.StatusBar)) | |
| 357 | + | require.True(t, styles.TabNameActive.Equal(styles.Accent)) | |
| 358 | + | require.True(t, styles.TabTitleActive.Equal(styles.Accent)) | |
| 359 | 359 | ||
| 360 | 360 | row := make([]renderer.Cell, rowLen) | |
| 361 | 361 | drawTopBarSnapshot(row, status, 0, "", styles) | |
| @@ -371,15 +371,15 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||
| 371 | 371 | require.Equal(t, tab0+tab1+tab2+strings.Repeat(" ", rowLen-len(tab0)-len(tab1)-len(tab2)), rowText(row)) | |
| 372 | 372 | ||
| 373 | 373 | for i, c := range row[:len(tab0)] { | |
| 374 | - | require.True(t, c.Style.Equal(styles.statusBar), "cell %d should use the plain base style", i) | |
| 374 | + | require.True(t, c.Style.Equal(styles.StatusBar), "cell %d should use the plain base style", i) | |
| 375 | 375 | } | |
| 376 | 376 | tab1Start := len(tab0) | |
| 377 | 377 | for i := tab1Start; i < tab1Start+len(tab1); i++ { | |
| 378 | - | require.True(t, row[i].Style.Equal(styles.accent), "cell %d should use the plain accent style, including the blank bell cell", i) | |
| 378 | + | require.True(t, row[i].Style.Equal(styles.Accent), "cell %d should use the plain accent style, including the blank bell cell", i) | |
| 379 | 379 | } | |
| 380 | 380 | tab2Start := tab1Start + len(tab1) | |
| 381 | 381 | for i := tab2Start; i < tab2Start+len(tab2); i++ { | |
| 382 | - | require.True(t, row[i].Style.Equal(styles.statusBar), "cell %d should use the plain base style, including the blank bell cell", i) | |
| 382 | + | require.True(t, row[i].Style.Equal(styles.StatusBar), "cell %d should use the plain base style, including the blank bell cell", i) | |
| 383 | 383 | } | |
| 384 | 384 | }) | |
| 385 | 385 | } | |
| @@ -847,7 +847,7 @@ func TestTopBarRightAnchor(t *testing.T) { | |||
| 847 | 847 | t.Run(tt.name, func(t *testing.T) { | |
| 848 | 848 | row := make([]renderer.Cell, tt.width) | |
| 849 | 849 | ||
| 850 | - | drawTopBarSnapshot(row, tt.status, 0, tt.topRight, resolveThemeStyles(nil)) | |
| 850 | + | drawTopBarSnapshot(row, tt.status, 0, tt.topRight, resolveStyles(nil)) | |
| 851 | 851 | ||
| 852 | 852 | require.Equal(t, tt.want, rowText(row)) | |
| 853 | 853 | if tt.continuationCell > 0 { | |
| @@ -1093,7 +1093,7 @@ func TestCapturePrimaryRenderStatePreservesContextualMRUModeThroughScratchReuse( | |||
| 1093 | 1093 | draw := func(state capturedRenderState) string { | |
| 1094 | 1094 | t.Helper() | |
| 1095 | 1095 | row := make([]renderer.Cell, 32) | |
| 1096 | - | drawStatusBarState(row, state.bars, resolveThemeStyles(nil)) | |
| 1096 | + | drawStatusBarState(row, state.bars, resolveStyles(nil)) | |
| 1097 | 1097 | return rowText(row) | |
| 1098 | 1098 | } | |
| 1099 | 1099 | normal := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "vty"}, {name: "misc"}}} | |
| @@ -1139,7 +1139,7 @@ func TestStatusBarRendersMRUNamesAndInlineBell(t *testing.T) { | |||
| 1139 | 1139 | } | |
| 1140 | 1140 | row := make([]renderer.Cell, 24) | |
| 1141 | 1141 | ||
| 1142 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | |
| 1142 | + | drawStatusBarState(row, state, resolveStyles(nil)) | |
| 1143 | 1143 | ||
| 1144 | 1144 | require.Equal(t, " cur fresh tmp ", rowText(row)) | |
| 1145 | 1145 | for _, c := range row { | |
| @@ -1153,19 +1153,19 @@ func TestStatusBarRendersMRUNamesAndInlineBell(t *testing.T) { | |||
| 1153 | 1153 | ||
| 1154 | 1154 | func TestStatusBarCurrentSessionUsesAccentStyle(t *testing.T) { | |
| 1155 | 1155 | theme := themeui.Theme{Foreground: renderer.RGB{R: 220, G: 220, B: 220}, Background: renderer.RGB{R: 10, G: 10, B: 10}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | |
| 1156 | - | styles := newThemeStyles(theme) | |
| 1156 | + | styles := themeui.NewStyles(theme) | |
| 1157 | 1157 | row := make([]renderer.Cell, 16) | |
| 1158 | 1158 | ||
| 1159 | 1159 | drawStatusBarState(row, barState{status: statusSnapshot{session: "cur"}, theme: theme}, styles) | |
| 1160 | 1160 | ||
| 1161 | 1161 | for _, idx := range []int{0, 1, 2, 3, 4} { | |
| 1162 | - | require.True(t, row[idx].Style.Equal(styles.accent), "cell %d should use accent style", idx) | |
| 1162 | + | require.True(t, row[idx].Style.Equal(styles.Accent), "cell %d should use accent style", idx) | |
| 1163 | 1163 | } | |
| 1164 | - | require.NotEqual(t, styles.statusBar.BackgroundRGB, styles.accent.BackgroundRGB) | |
| 1164 | + | require.NotEqual(t, styles.StatusBar.BackgroundRGB, styles.Accent.BackgroundRGB) | |
| 1165 | 1165 | } | |
| 1166 | 1166 | ||
| 1167 | 1167 | func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testing.T) { | |
| 1168 | - | styles := resolveThemeStyles(nil) | |
| 1168 | + | styles := resolveStyles(nil) | |
| 1169 | 1169 | row := make([]renderer.Cell, 18) | |
| 1170 | 1170 | state := barState{ | |
| 1171 | 1171 | status: statusSnapshot{session: "cur"}, | |
| @@ -1190,8 +1190,8 @@ func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testi | |||
| 1190 | 1190 | if cell.Rune == '2' { | |
| 1191 | 1191 | found = true | |
| 1192 | 1192 | require.True(t, cell.Style.Bold) | |
| 1193 | - | require.Equal(t, styles.accent.Foreground, cell.Style.Foreground) | |
| 1194 | - | require.Equal(t, styles.accent.Background, cell.Style.Background) | |
| 1193 | + | require.Equal(t, styles.Accent.Foreground, cell.Style.Foreground) | |
| 1194 | + | require.Equal(t, styles.Accent.Background, cell.Style.Background) | |
| 1195 | 1195 | break | |
| 1196 | 1196 | } | |
| 1197 | 1197 | } | |
| @@ -1200,7 +1200,7 @@ func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testi | |||
| 1200 | 1200 | ||
| 1201 | 1201 | func TestStatusBarMRUGradientTruecolorAndPlainFallback(t *testing.T) { | |
| 1202 | 1202 | theme := themeui.Theme{Foreground: renderer.RGB{R: 200, G: 200, B: 200}, Background: renderer.RGB{R: 20, G: 20, B: 20}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | |
| 1203 | - | styles := newThemeStyles(theme) | |
| 1203 | + | styles := themeui.NewStyles(theme) | |
| 1204 | 1204 | state := barState{status: statusSnapshot{session: "c"}, theme: theme, mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | |
| 1205 | 1205 | row := make([]renderer.Cell, 16) | |
| 1206 | 1206 | ||
| @@ -1209,14 +1209,14 @@ func TestStatusBarMRUGradientTruecolorAndPlainFallback(t *testing.T) { | |||
| 1209 | 1209 | firstFG := row[4].Style.ForegroundRGB.R | |
| 1210 | 1210 | secondFG := row[7].Style.ForegroundRGB.R | |
| 1211 | 1211 | thirdFG := row[10].Style.ForegroundRGB.R | |
| 1212 | - | require.Equal(t, styles.statusBar.ForegroundRGB.R, firstFG) | |
| 1212 | + | require.Equal(t, styles.StatusBar.ForegroundRGB.R, firstFG) | |
| 1213 | 1213 | require.Greater(t, firstFG, secondFG) | |
| 1214 | 1214 | require.Greater(t, secondFG, thirdFG) | |
| 1215 | 1215 | ||
| 1216 | 1216 | firstBG := row[4].Style.BackgroundRGB.R | |
| 1217 | 1217 | secondBG := row[7].Style.BackgroundRGB.R | |
| 1218 | 1218 | thirdBG := row[10].Style.BackgroundRGB.R | |
| 1219 | - | require.Equal(t, styles.statusBar.BackgroundRGB.R, firstBG) | |
| 1219 | + | require.Equal(t, styles.StatusBar.BackgroundRGB.R, firstBG) | |
| 1220 | 1220 | require.Greater(t, firstBG, secondBG) | |
| 1221 | 1221 | require.Greater(t, secondBG, thirdBG) | |
| 1222 | 1222 | ||
| @@ -1229,7 +1229,7 @@ func TestStatusBarNarrowRowsDropWholeOldestMRUEntries(t *testing.T) { | |||
| 1229 | 1229 | state := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "fresh"}, {name: "middle"}, {name: "old"}}} | |
| 1230 | 1230 | row := make([]renderer.Cell, 21) | |
| 1231 | 1231 | ||
| 1232 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | |
| 1232 | + | drawStatusBarState(row, state, resolveStyles(nil)) | |
| 1233 | 1233 | ||
| 1234 | 1234 | text := rowText(row) | |
| 1235 | 1235 | require.Contains(t, text, "fresh") | |
| @@ -1273,7 +1273,7 @@ func TestStatusBarMRUWidthAwareBudget(t *testing.T) { | |||
| 1273 | 1273 | t.Run(tt.name, func(t *testing.T) { | |
| 1274 | 1274 | row := make([]renderer.Cell, tt.cols) | |
| 1275 | 1275 | ||
| 1276 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | |
| 1276 | + | drawStatusBarState(row, state, resolveStyles(nil)) | |
| 1277 | 1277 | ||
| 1278 | 1278 | text := rowText(row) | |
| 1279 | 1279 | for _, want := range tt.wantShown { | |
| @@ -1346,7 +1346,7 @@ func TestStatusBarBottomRightAndMRUFitting(t *testing.T) { | |||
| 1346 | 1346 | t.Run(tt.name, func(t *testing.T) { | |
| 1347 | 1347 | row := make([]renderer.Cell, tt.width) | |
| 1348 | 1348 | ||
| 1349 | - | drawStatusBarState(row, tt.state, resolveThemeStyles(nil)) | |
| 1349 | + | drawStatusBarState(row, tt.state, resolveStyles(nil)) | |
| 1350 | 1350 | ||
| 1351 | 1351 | text := rowText(row) | |
| 1352 | 1352 | if tt.want != "" { | |
| @@ -1372,7 +1372,7 @@ func TestStatusBarCopyFeedbackFullyRenderedAlongsideMRU(t *testing.T) { | |||
| 1372 | 1372 | state := barState{status: statusSnapshot{session: "cur"}, copyFeedback: "copied", mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | |
| 1373 | 1373 | row := make([]renderer.Cell, 20) | |
| 1374 | 1374 | ||
| 1375 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | |
| 1375 | + | drawStatusBarState(row, state, resolveStyles(nil)) | |
| 1376 | 1376 | ||
| 1377 | 1377 | text := rowText(row) | |
| 1378 | 1378 | require.Contains(t, text, " a") | |
| @@ -1395,7 +1395,7 @@ func TestStatusBarCopyFeedbackBoundaryWidths(t *testing.T) { | |||
| 1395 | 1395 | t.Run(tt.name, func(t *testing.T) { | |
| 1396 | 1396 | row := make([]renderer.Cell, tt.cols) | |
| 1397 | 1397 | ||
| 1398 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | |
| 1398 | + | drawStatusBarState(row, state, resolveStyles(nil)) | |
| 1399 | 1399 | ||
| 1400 | 1400 | require.Equal(t, tt.want, rowText(row)) | |
| 1401 | 1401 | }) | |
| @@ -267,9 +267,9 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 267 | Known: true, | 267 | Known: true, | ||
| 268 | TrueColor: true, | 268 | TrueColor: true, | ||
| 269 | } | 269 | } | ||
| 270 | - | styles := newThemeStyles(theme) | 270 | + | styles := themeui.NewStyles(theme) |
| 271 | - | require.False(t, styles.tabTitle.Equal(styles.statusBar), "sanity: muted title style must differ from the base style on a truecolor theme") | 271 | + | require.False(t, styles.TabTitle.Equal(styles.StatusBar), "sanity: muted title style must differ from the base style on a truecolor theme") |
| 272 | - | require.False(t, styles.tabTitleActive.Equal(styles.accent), "sanity: muted active-title style must differ from the base accent style") | 272 | + | require.False(t, styles.TabTitleActive.Equal(styles.Accent), "sanity: muted active-title style must differ from the base accent style") |
| 273 | 273 | ||||
| 274 | labels := fitTabLabels(tabs, rowLen, "") | 274 | labels := fitTabLabels(tabs, rowLen, "") | ||
| 275 | 275 | ||||
| @@ -286,17 +286,17 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 286 | nameEnd := x + 1 + labels[0].nameLen | 286 | nameEnd := x + 1 + labels[0].nameLen | ||
| 287 | for i := x; i < nameEnd; i++ { | 287 | for i := x; i < nameEnd; i++ { | ||
| 288 | require.True(t, row[i].Style.Bold, "cell %d: name segment must be bold", i) | 288 | require.True(t, row[i].Style.Bold, "cell %d: name segment must be bold", i) | ||
| 289 | - | require.True(t, row[i].Style.Equal(styles.tabName), "cell %d: name segment style mismatch", i) | 289 | + | require.True(t, row[i].Style.Equal(styles.TabName), "cell %d: name segment style mismatch", i) |
| 290 | } | 290 | } | ||
| 291 | x = nameEnd | 291 | x = nameEnd | ||
| 292 | titleLen := len(labels[0].text) - labels[0].nameLen | 292 | titleLen := len(labels[0].text) - labels[0].nameLen | ||
| 293 | titleEnd := x + titleLen | 293 | titleEnd := x + titleLen | ||
| 294 | for i := x; i < titleEnd; i++ { | 294 | for i := x; i < titleEnd; i++ { | ||
| 295 | require.False(t, row[i].Style.Bold, "cell %d: title segment must not be bold", i) | 295 | require.False(t, row[i].Style.Bold, "cell %d: title segment must not be bold", i) | ||
| 296 | - | require.True(t, row[i].Style.Equal(styles.tabTitle), "cell %d: title segment style mismatch", i) | 296 | + | require.True(t, row[i].Style.Equal(styles.TabTitle), "cell %d: title segment style mismatch", i) |
| 297 | } | 297 | } | ||
| 298 | x = titleEnd | 298 | x = titleEnd | ||
| 299 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 0 trailing space must keep the base style", x) | 299 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 0 trailing space must keep the base style", x) |
| 300 | x++ | 300 | x++ | ||
| 301 | 301 | ||||
| 302 | // tab 1: active, attention, no title: name, attention space+bell (both | 302 | // tab 1: active, attention, no title: name, attention space+bell (both | ||
| @@ -304,16 +304,16 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 304 | nameEnd = x + 1 + labels[1].nameLen | 304 | nameEnd = x + 1 + labels[1].nameLen | ||
| 305 | for i := x; i < nameEnd; i++ { | 305 | for i := x; i < nameEnd; i++ { | ||
| 306 | require.True(t, row[i].Style.Bold, "cell %d: active name segment must be bold", i) | 306 | require.True(t, row[i].Style.Bold, "cell %d: active name segment must be bold", i) | ||
| 307 | - | require.True(t, row[i].Style.Equal(styles.tabNameActive), "cell %d: active name segment style mismatch", i) | 307 | + | require.True(t, row[i].Style.Equal(styles.TabNameActive), "cell %d: active name segment style mismatch", i) |
| 308 | } | 308 | } | ||
| 309 | require.Equal(t, len(labels[1].text), labels[1].nameLen, "tab 1 has no pane title, so its whole label is the name segment") | 309 | require.Equal(t, len(labels[1].text), labels[1].nameLen, "tab 1 has no pane title, so its whole label is the name segment") | ||
| 310 | x = nameEnd | 310 | x = nameEnd | ||
| 311 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 attention leading space must keep the active base style", x) | 311 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 attention leading space must keep the active base style", x) |
| 312 | x++ | 312 | x++ | ||
| 313 | tab1Bell := x | 313 | tab1Bell := x | ||
| 314 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 blank bell must keep the active base style, not DefaultStyle", x) | 314 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 blank bell must keep the active base style, not DefaultStyle", x) |
| 315 | x++ | 315 | x++ | ||
| 316 | - | require.True(t, row[x].Style.Equal(styles.accent), "cell %d: tab 1 trailing space must keep the active base style", x) | 316 | + | require.True(t, row[x].Style.Equal(styles.Accent), "cell %d: tab 1 trailing space must keep the active base style", x) |
| 317 | x++ | 317 | x++ | ||
| 318 | 318 | ||||
| 319 | // tab 2: inactive, attention, with title: name, attention space+bell | 319 | // tab 2: inactive, attention, with title: name, attention space+bell | ||
| @@ -321,22 +321,22 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 321 | nameEnd = x + 1 + labels[2].nameLen | 321 | nameEnd = x + 1 + labels[2].nameLen | ||
| 322 | for i := x; i < nameEnd; i++ { | 322 | for i := x; i < nameEnd; i++ { | ||
| 323 | require.True(t, row[i].Style.Bold, "cell %d: tab 2 name segment must be bold", i) | 323 | require.True(t, row[i].Style.Bold, "cell %d: tab 2 name segment must be bold", i) | ||
| 324 | - | require.True(t, row[i].Style.Equal(styles.tabName), "cell %d: tab 2 name segment style mismatch", i) | 324 | + | require.True(t, row[i].Style.Equal(styles.TabName), "cell %d: tab 2 name segment style mismatch", i) |
| 325 | } | 325 | } | ||
| 326 | x = nameEnd | 326 | x = nameEnd | ||
| 327 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 attention leading space must keep the base style", x) | 327 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 attention leading space must keep the base style", x) |
| 328 | x++ | 328 | x++ | ||
| 329 | tab2Bell := x | 329 | tab2Bell := x | ||
| 330 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 blank bell must keep the base style, not DefaultStyle", x) | 330 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 blank bell must keep the base style, not DefaultStyle", x) |
| 331 | x++ | 331 | x++ | ||
| 332 | titleLen = len(labels[2].text) - labels[2].nameLen | 332 | titleLen = len(labels[2].text) - labels[2].nameLen | ||
| 333 | titleEnd = x + titleLen | 333 | titleEnd = x + titleLen | ||
| 334 | for i := x; i < titleEnd; i++ { | 334 | for i := x; i < titleEnd; i++ { | ||
| 335 | require.False(t, row[i].Style.Bold, "cell %d: tab 2 title segment must not be bold", i) | 335 | require.False(t, row[i].Style.Bold, "cell %d: tab 2 title segment must not be bold", i) | ||
| 336 | - | require.True(t, row[i].Style.Equal(styles.tabTitle), "cell %d: tab 2 title segment style mismatch", i) | 336 | + | require.True(t, row[i].Style.Equal(styles.TabTitle), "cell %d: tab 2 title segment style mismatch", i) |
| 337 | } | 337 | } | ||
| 338 | x = titleEnd | 338 | x = titleEnd | ||
| 339 | - | require.True(t, row[x].Style.Equal(styles.statusBar), "cell %d: tab 2 trailing space must keep the base style", x) | 339 | + | require.True(t, row[x].Style.Equal(styles.StatusBar), "cell %d: tab 2 trailing space must keep the base style", x) |
| 340 | 340 | ||||
| 341 | // On a visible pulse frame, the bell glyph itself must keep the same | 341 | // On a visible pulse frame, the bell glyph itself must keep the same | ||
| 342 | // background as its tab's base style. | 342 | // background as its tab's base style. | ||
| @@ -344,18 +344,18 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 344 | drawTopBarSnapshot(visibleRow, status, 1, "", styles) | 344 | drawTopBarSnapshot(visibleRow, status, 1, "", styles) | ||
| 345 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab1Bell].Rune) | 345 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab1Bell].Rune) | ||
| 346 | require.True(t, visibleRow[tab1Bell].Style.HasBackgroundRGB) | 346 | require.True(t, visibleRow[tab1Bell].Style.HasBackgroundRGB) | ||
| 347 | - | require.Equal(t, styles.accent.BackgroundRGB, visibleRow[tab1Bell].Style.BackgroundRGB, "tab 1's visible bell must keep the active base background") | 347 | + | require.Equal(t, styles.Accent.BackgroundRGB, visibleRow[tab1Bell].Style.BackgroundRGB, "tab 1's visible bell must keep the active base background") |
| 348 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab2Bell].Rune) | 348 | require.Equal(t, rune(ui.AttentionGlyph), visibleRow[tab2Bell].Rune) | ||
| 349 | require.True(t, visibleRow[tab2Bell].Style.HasBackgroundRGB) | 349 | require.True(t, visibleRow[tab2Bell].Style.HasBackgroundRGB) | ||
| 350 | - | require.Equal(t, styles.statusBar.BackgroundRGB, visibleRow[tab2Bell].Style.BackgroundRGB, "tab 2's visible bell must keep the plain base background") | 350 | + | require.Equal(t, styles.StatusBar.BackgroundRGB, visibleRow[tab2Bell].Style.BackgroundRGB, "tab 2's visible bell must keep the plain base background") |
| 351 | }) | 351 | }) | ||
| 352 | 352 | ||||
| 353 | t.Run("non-usable theme matches the pre-change fallback styles", func(t *testing.T) { | 353 | t.Run("non-usable theme matches the pre-change fallback styles", func(t *testing.T) { | ||
| 354 | - | styles := resolveThemeStyles(nil) | 354 | + | styles := resolveStyles(nil) |
| 355 | - | require.True(t, styles.tabName.Equal(styles.statusBar)) | 355 | + | require.True(t, styles.TabName.Equal(styles.StatusBar)) |
| 356 | - | require.True(t, styles.tabTitle.Equal(styles.statusBar)) | 356 | + | require.True(t, styles.TabTitle.Equal(styles.StatusBar)) |
| 357 | - | require.True(t, styles.tabNameActive.Equal(styles.accent)) | 357 | + | require.True(t, styles.TabNameActive.Equal(styles.Accent)) |
| 358 | - | require.True(t, styles.tabTitleActive.Equal(styles.accent)) | 358 | + | require.True(t, styles.TabTitleActive.Equal(styles.Accent)) |
| 359 | 359 | ||||
| 360 | row := make([]renderer.Cell, rowLen) | 360 | row := make([]renderer.Cell, rowLen) | ||
| 361 | drawTopBarSnapshot(row, status, 0, "", styles) | 361 | drawTopBarSnapshot(row, status, 0, "", styles) | ||
| @@ -371,15 +371,15 @@ func TestDrawTopBarSnapshotStylesTabNameAndTitle(t *testing.T) { | |||||
| 371 | require.Equal(t, tab0+tab1+tab2+strings.Repeat(" ", rowLen-len(tab0)-len(tab1)-len(tab2)), rowText(row)) | 371 | require.Equal(t, tab0+tab1+tab2+strings.Repeat(" ", rowLen-len(tab0)-len(tab1)-len(tab2)), rowText(row)) | ||
| 372 | 372 | ||||
| 373 | for i, c := range row[:len(tab0)] { | 373 | for i, c := range row[:len(tab0)] { | ||
| 374 | - | require.True(t, c.Style.Equal(styles.statusBar), "cell %d should use the plain base style", i) | 374 | + | require.True(t, c.Style.Equal(styles.StatusBar), "cell %d should use the plain base style", i) |
| 375 | } | 375 | } | ||
| 376 | tab1Start := len(tab0) | 376 | tab1Start := len(tab0) | ||
| 377 | for i := tab1Start; i < tab1Start+len(tab1); i++ { | 377 | for i := tab1Start; i < tab1Start+len(tab1); i++ { | ||
| 378 | - | require.True(t, row[i].Style.Equal(styles.accent), "cell %d should use the plain accent style, including the blank bell cell", i) | 378 | + | require.True(t, row[i].Style.Equal(styles.Accent), "cell %d should use the plain accent style, including the blank bell cell", i) |
| 379 | } | 379 | } | ||
| 380 | tab2Start := tab1Start + len(tab1) | 380 | tab2Start := tab1Start + len(tab1) | ||
| 381 | for i := tab2Start; i < tab2Start+len(tab2); i++ { | 381 | for i := tab2Start; i < tab2Start+len(tab2); i++ { | ||
| 382 | - | require.True(t, row[i].Style.Equal(styles.statusBar), "cell %d should use the plain base style, including the blank bell cell", i) | 382 | + | require.True(t, row[i].Style.Equal(styles.StatusBar), "cell %d should use the plain base style, including the blank bell cell", i) |
| 383 | } | 383 | } | ||
| 384 | }) | 384 | }) | ||
| 385 | } | 385 | } | ||
| @@ -847,7 +847,7 @@ func TestTopBarRightAnchor(t *testing.T) { | |||||
| 847 | t.Run(tt.name, func(t *testing.T) { | 847 | t.Run(tt.name, func(t *testing.T) { | ||
| 848 | row := make([]renderer.Cell, tt.width) | 848 | row := make([]renderer.Cell, tt.width) | ||
| 849 | 849 | ||||
| 850 | - | drawTopBarSnapshot(row, tt.status, 0, tt.topRight, resolveThemeStyles(nil)) | 850 | + | drawTopBarSnapshot(row, tt.status, 0, tt.topRight, resolveStyles(nil)) |
| 851 | 851 | ||||
| 852 | require.Equal(t, tt.want, rowText(row)) | 852 | require.Equal(t, tt.want, rowText(row)) | ||
| 853 | if tt.continuationCell > 0 { | 853 | if tt.continuationCell > 0 { | ||
| @@ -1093,7 +1093,7 @@ func TestCapturePrimaryRenderStatePreservesContextualMRUModeThroughScratchReuse( | |||||
| 1093 | draw := func(state capturedRenderState) string { | 1093 | draw := func(state capturedRenderState) string { | ||
| 1094 | t.Helper() | 1094 | t.Helper() | ||
| 1095 | row := make([]renderer.Cell, 32) | 1095 | row := make([]renderer.Cell, 32) | ||
| 1096 | - | drawStatusBarState(row, state.bars, resolveThemeStyles(nil)) | 1096 | + | drawStatusBarState(row, state.bars, resolveStyles(nil)) |
| 1097 | return rowText(row) | 1097 | return rowText(row) | ||
| 1098 | } | 1098 | } | ||
| 1099 | normal := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "vty"}, {name: "misc"}}} | 1099 | normal := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "vty"}, {name: "misc"}}} | ||
| @@ -1139,7 +1139,7 @@ func TestStatusBarRendersMRUNamesAndInlineBell(t *testing.T) { | |||||
| 1139 | } | 1139 | } | ||
| 1140 | row := make([]renderer.Cell, 24) | 1140 | row := make([]renderer.Cell, 24) | ||
| 1141 | 1141 | ||||
| 1142 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | 1142 | + | drawStatusBarState(row, state, resolveStyles(nil)) |
| 1143 | 1143 | ||||
| 1144 | require.Equal(t, " cur fresh tmp ", rowText(row)) | 1144 | require.Equal(t, " cur fresh tmp ", rowText(row)) | ||
| 1145 | for _, c := range row { | 1145 | for _, c := range row { | ||
| @@ -1153,19 +1153,19 @@ func TestStatusBarRendersMRUNamesAndInlineBell(t *testing.T) { | |||||
| 1153 | 1153 | ||||
| 1154 | func TestStatusBarCurrentSessionUsesAccentStyle(t *testing.T) { | 1154 | func TestStatusBarCurrentSessionUsesAccentStyle(t *testing.T) { | ||
| 1155 | theme := themeui.Theme{Foreground: renderer.RGB{R: 220, G: 220, B: 220}, Background: renderer.RGB{R: 10, G: 10, B: 10}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | 1155 | theme := themeui.Theme{Foreground: renderer.RGB{R: 220, G: 220, B: 220}, Background: renderer.RGB{R: 10, G: 10, B: 10}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | ||
| 1156 | - | styles := newThemeStyles(theme) | 1156 | + | styles := themeui.NewStyles(theme) |
| 1157 | row := make([]renderer.Cell, 16) | 1157 | row := make([]renderer.Cell, 16) | ||
| 1158 | 1158 | ||||
| 1159 | drawStatusBarState(row, barState{status: statusSnapshot{session: "cur"}, theme: theme}, styles) | 1159 | drawStatusBarState(row, barState{status: statusSnapshot{session: "cur"}, theme: theme}, styles) | ||
| 1160 | 1160 | ||||
| 1161 | for _, idx := range []int{0, 1, 2, 3, 4} { | 1161 | for _, idx := range []int{0, 1, 2, 3, 4} { | ||
| 1162 | - | require.True(t, row[idx].Style.Equal(styles.accent), "cell %d should use accent style", idx) | 1162 | + | require.True(t, row[idx].Style.Equal(styles.Accent), "cell %d should use accent style", idx) |
| 1163 | } | 1163 | } | ||
| 1164 | - | require.NotEqual(t, styles.statusBar.BackgroundRGB, styles.accent.BackgroundRGB) | 1164 | + | require.NotEqual(t, styles.StatusBar.BackgroundRGB, styles.Accent.BackgroundRGB) |
| 1165 | } | 1165 | } | ||
| 1166 | 1166 | ||||
| 1167 | func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testing.T) { | 1167 | func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testing.T) { | ||
| 1168 | - | styles := resolveThemeStyles(nil) | 1168 | + | styles := resolveStyles(nil) |
| 1169 | row := make([]renderer.Cell, 18) | 1169 | row := make([]renderer.Cell, 18) | ||
| 1170 | state := barState{ | 1170 | state := barState{ | ||
| 1171 | status: statusSnapshot{session: "cur"}, | 1171 | status: statusSnapshot{session: "cur"}, | ||
| @@ -1190,8 +1190,8 @@ func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testi | |||||
| 1190 | if cell.Rune == '2' { | 1190 | if cell.Rune == '2' { | ||
| 1191 | found = true | 1191 | found = true | ||
| 1192 | require.True(t, cell.Style.Bold) | 1192 | require.True(t, cell.Style.Bold) | ||
| 1193 | - | require.Equal(t, styles.accent.Foreground, cell.Style.Foreground) | 1193 | + | require.Equal(t, styles.Accent.Foreground, cell.Style.Foreground) |
| 1194 | - | require.Equal(t, styles.accent.Background, cell.Style.Background) | 1194 | + | require.Equal(t, styles.Accent.Background, cell.Style.Background) |
| 1195 | break | 1195 | break | ||
| 1196 | } | 1196 | } | ||
| 1197 | } | 1197 | } | ||
| @@ -1200,7 +1200,7 @@ func TestStatusBarContextualRanksPreserveOriginalRanksAndSelectedAccent(t *testi | |||||
| 1200 | 1200 | ||||
| 1201 | func TestStatusBarMRUGradientTruecolorAndPlainFallback(t *testing.T) { | 1201 | func TestStatusBarMRUGradientTruecolorAndPlainFallback(t *testing.T) { | ||
| 1202 | theme := themeui.Theme{Foreground: renderer.RGB{R: 200, G: 200, B: 200}, Background: renderer.RGB{R: 20, G: 20, B: 20}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | 1202 | theme := themeui.Theme{Foreground: renderer.RGB{R: 200, G: 200, B: 200}, Background: renderer.RGB{R: 20, G: 20, B: 20}, HasFG: true, HasBG: true, TrueColor: true, Known: true} | ||
| 1203 | - | styles := newThemeStyles(theme) | 1203 | + | styles := themeui.NewStyles(theme) |
| 1204 | state := barState{status: statusSnapshot{session: "c"}, theme: theme, mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | 1204 | state := barState{status: statusSnapshot{session: "c"}, theme: theme, mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | ||
| 1205 | row := make([]renderer.Cell, 16) | 1205 | row := make([]renderer.Cell, 16) | ||
| 1206 | 1206 | ||||
| @@ -1209,14 +1209,14 @@ func TestStatusBarMRUGradientTruecolorAndPlainFallback(t *testing.T) { | |||||
| 1209 | firstFG := row[4].Style.ForegroundRGB.R | 1209 | firstFG := row[4].Style.ForegroundRGB.R | ||
| 1210 | secondFG := row[7].Style.ForegroundRGB.R | 1210 | secondFG := row[7].Style.ForegroundRGB.R | ||
| 1211 | thirdFG := row[10].Style.ForegroundRGB.R | 1211 | thirdFG := row[10].Style.ForegroundRGB.R | ||
| 1212 | - | require.Equal(t, styles.statusBar.ForegroundRGB.R, firstFG) | 1212 | + | require.Equal(t, styles.StatusBar.ForegroundRGB.R, firstFG) |
| 1213 | require.Greater(t, firstFG, secondFG) | 1213 | require.Greater(t, firstFG, secondFG) | ||
| 1214 | require.Greater(t, secondFG, thirdFG) | 1214 | require.Greater(t, secondFG, thirdFG) | ||
| 1215 | 1215 | ||||
| 1216 | firstBG := row[4].Style.BackgroundRGB.R | 1216 | firstBG := row[4].Style.BackgroundRGB.R | ||
| 1217 | secondBG := row[7].Style.BackgroundRGB.R | 1217 | secondBG := row[7].Style.BackgroundRGB.R | ||
| 1218 | thirdBG := row[10].Style.BackgroundRGB.R | 1218 | thirdBG := row[10].Style.BackgroundRGB.R | ||
| 1219 | - | require.Equal(t, styles.statusBar.BackgroundRGB.R, firstBG) | 1219 | + | require.Equal(t, styles.StatusBar.BackgroundRGB.R, firstBG) |
| 1220 | require.Greater(t, firstBG, secondBG) | 1220 | require.Greater(t, firstBG, secondBG) | ||
| 1221 | require.Greater(t, secondBG, thirdBG) | 1221 | require.Greater(t, secondBG, thirdBG) | ||
| 1222 | 1222 | ||||
| @@ -1229,7 +1229,7 @@ func TestStatusBarNarrowRowsDropWholeOldestMRUEntries(t *testing.T) { | |||||
| 1229 | state := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "fresh"}, {name: "middle"}, {name: "old"}}} | 1229 | state := barState{status: statusSnapshot{session: "cur"}, mru: []recentSession{{name: "fresh"}, {name: "middle"}, {name: "old"}}} | ||
| 1230 | row := make([]renderer.Cell, 21) | 1230 | row := make([]renderer.Cell, 21) | ||
| 1231 | 1231 | ||||
| 1232 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | 1232 | + | drawStatusBarState(row, state, resolveStyles(nil)) |
| 1233 | 1233 | ||||
| 1234 | text := rowText(row) | 1234 | text := rowText(row) | ||
| 1235 | require.Contains(t, text, "fresh") | 1235 | require.Contains(t, text, "fresh") | ||
| @@ -1273,7 +1273,7 @@ func TestStatusBarMRUWidthAwareBudget(t *testing.T) { | |||||
| 1273 | t.Run(tt.name, func(t *testing.T) { | 1273 | t.Run(tt.name, func(t *testing.T) { | ||
| 1274 | row := make([]renderer.Cell, tt.cols) | 1274 | row := make([]renderer.Cell, tt.cols) | ||
| 1275 | 1275 | ||||
| 1276 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | 1276 | + | drawStatusBarState(row, state, resolveStyles(nil)) |
| 1277 | 1277 | ||||
| 1278 | text := rowText(row) | 1278 | text := rowText(row) | ||
| 1279 | for _, want := range tt.wantShown { | 1279 | for _, want := range tt.wantShown { | ||
| @@ -1346,7 +1346,7 @@ func TestStatusBarBottomRightAndMRUFitting(t *testing.T) { | |||||
| 1346 | t.Run(tt.name, func(t *testing.T) { | 1346 | t.Run(tt.name, func(t *testing.T) { | ||
| 1347 | row := make([]renderer.Cell, tt.width) | 1347 | row := make([]renderer.Cell, tt.width) | ||
| 1348 | 1348 | ||||
| 1349 | - | drawStatusBarState(row, tt.state, resolveThemeStyles(nil)) | 1349 | + | drawStatusBarState(row, tt.state, resolveStyles(nil)) |
| 1350 | 1350 | ||||
| 1351 | text := rowText(row) | 1351 | text := rowText(row) | ||
| 1352 | if tt.want != "" { | 1352 | if tt.want != "" { | ||
| @@ -1372,7 +1372,7 @@ func TestStatusBarCopyFeedbackFullyRenderedAlongsideMRU(t *testing.T) { | |||||
| 1372 | state := barState{status: statusSnapshot{session: "cur"}, copyFeedback: "copied", mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | 1372 | state := barState{status: statusSnapshot{session: "cur"}, copyFeedback: "copied", mru: []recentSession{{name: "a"}, {name: "b"}, {name: "c"}}} | ||
| 1373 | row := make([]renderer.Cell, 20) | 1373 | row := make([]renderer.Cell, 20) | ||
| 1374 | 1374 | ||||
| 1375 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | 1375 | + | drawStatusBarState(row, state, resolveStyles(nil)) |
| 1376 | 1376 | ||||
| 1377 | text := rowText(row) | 1377 | text := rowText(row) | ||
| 1378 | require.Contains(t, text, " a") | 1378 | require.Contains(t, text, " a") | ||
| @@ -1395,7 +1395,7 @@ func TestStatusBarCopyFeedbackBoundaryWidths(t *testing.T) { | |||||
| 1395 | t.Run(tt.name, func(t *testing.T) { | 1395 | t.Run(tt.name, func(t *testing.T) { | ||
| 1396 | row := make([]renderer.Cell, tt.cols) | 1396 | row := make([]renderer.Cell, tt.cols) | ||
| 1397 | 1397 | ||||
| 1398 | - | drawStatusBarState(row, state, resolveThemeStyles(nil)) | 1398 | + | drawStatusBarState(row, state, resolveStyles(nil)) |
| 1399 | 1399 | ||||
| 1400 | require.Equal(t, tt.want, rowText(row)) | 1400 | require.Equal(t, tt.want, rowText(row)) | ||
| 1401 | }) | 1401 | }) | ||
A internal/usecase/theme/styles.go +48 -0
| @@ -0,0 +1,48 @@ | |||
| 1 | + | package theme | |
| 2 | + | ||
| 3 | + | import "github.com/bnema/vev/pkg/renderer" | |
| 4 | + | ||
| 5 | + | // Styles is the resolved set of terminal chrome styles for a client theme. | |
| 6 | + | type Styles struct { | |
| 7 | + | StatusBar renderer.Style | |
| 8 | + | Accent renderer.Style | |
| 9 | + | Border renderer.Style | |
| 10 | + | Selection renderer.Style | |
| 11 | + | CopyStatus renderer.Style | |
| 12 | + | PaletteDesc renderer.Style | |
| 13 | + | ||
| 14 | + | TabName renderer.Style | |
| 15 | + | TabNameActive renderer.Style | |
| 16 | + | TabTitle renderer.Style | |
| 17 | + | TabTitleActive renderer.Style | |
| 18 | + | ||
| 19 | + | PickerName renderer.Style | |
| 20 | + | PickerSelectionName renderer.Style | |
| 21 | + | PickerSelectionMuted renderer.Style | |
| 22 | + | PickerSeparator renderer.Style | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // NewStyles composes the terminal chrome styles from the semantic theme styles. | |
| 26 | + | func NewStyles(t Theme) Styles { | |
| 27 | + | statusBar := StatusBarStyle(t) | |
| 28 | + | accent := AccentStyle(t) | |
| 29 | + | selection := SelectionStyle(t) | |
| 30 | + | return Styles{ | |
| 31 | + | StatusBar: statusBar, | |
| 32 | + | Accent: accent, | |
| 33 | + | Border: BorderStyle(t), | |
| 34 | + | Selection: selection, | |
| 35 | + | CopyStatus: selection, | |
| 36 | + | PaletteDesc: MutedTextStyle(t), | |
| 37 | + | ||
| 38 | + | TabName: EmphasisStyle(statusBar, t), | |
| 39 | + | TabNameActive: EmphasisStyle(accent, t), | |
| 40 | + | TabTitle: MutedVariantStyle(statusBar, t), | |
| 41 | + | TabTitleActive: MutedVariantStyle(accent, t), | |
| 42 | + | ||
| 43 | + | PickerName: EmphasisStyle(renderer.DefaultStyle(), t), | |
| 44 | + | PickerSelectionName: EmphasisStyle(selection, t), | |
| 45 | + | PickerSelectionMuted: MutedVariantStyle(selection, t), | |
| 46 | + | PickerSeparator: MutedTextStyle(t), | |
| 47 | + | } | |
| 48 | + | } | |
| @@ -0,0 +1,48 @@ | |||||
| 1 | + | package theme | |||
| 2 | + | ||||
| 3 | + | import "github.com/bnema/vev/pkg/renderer" | |||
| 4 | + | ||||
| 5 | + | // Styles is the resolved set of terminal chrome styles for a client theme. | |||
| 6 | + | type Styles struct { | |||
| 7 | + | StatusBar renderer.Style | |||
| 8 | + | Accent renderer.Style | |||
| 9 | + | Border renderer.Style | |||
| 10 | + | Selection renderer.Style | |||
| 11 | + | CopyStatus renderer.Style | |||
| 12 | + | PaletteDesc renderer.Style | |||
| 13 | + | ||||
| 14 | + | TabName renderer.Style | |||
| 15 | + | TabNameActive renderer.Style | |||
| 16 | + | TabTitle renderer.Style | |||
| 17 | + | TabTitleActive renderer.Style | |||
| 18 | + | ||||
| 19 | + | PickerName renderer.Style | |||
| 20 | + | PickerSelectionName renderer.Style | |||
| 21 | + | PickerSelectionMuted renderer.Style | |||
| 22 | + | PickerSeparator renderer.Style | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | // NewStyles composes the terminal chrome styles from the semantic theme styles. | |||
| 26 | + | func NewStyles(t Theme) Styles { | |||
| 27 | + | statusBar := StatusBarStyle(t) | |||
| 28 | + | accent := AccentStyle(t) | |||
| 29 | + | selection := SelectionStyle(t) | |||
| 30 | + | return Styles{ | |||
| 31 | + | StatusBar: statusBar, | |||
| 32 | + | Accent: accent, | |||
| 33 | + | Border: BorderStyle(t), | |||
| 34 | + | Selection: selection, | |||
| 35 | + | CopyStatus: selection, | |||
| 36 | + | PaletteDesc: MutedTextStyle(t), | |||
| 37 | + | ||||
| 38 | + | TabName: EmphasisStyle(statusBar, t), | |||
| 39 | + | TabNameActive: EmphasisStyle(accent, t), | |||
| 40 | + | TabTitle: MutedVariantStyle(statusBar, t), | |||
| 41 | + | TabTitleActive: MutedVariantStyle(accent, t), | |||
| 42 | + | ||||
| 43 | + | PickerName: EmphasisStyle(renderer.DefaultStyle(), t), | |||
| 44 | + | PickerSelectionName: EmphasisStyle(selection, t), | |||
| 45 | + | PickerSelectionMuted: MutedVariantStyle(selection, t), | |||
| 46 | + | PickerSeparator: MutedTextStyle(t), | |||
| 47 | + | } | |||
| 48 | + | } | |||
A internal/usecase/theme/styles_test.go +39 -0
| @@ -0,0 +1,39 @@ | |||
| 1 | + | package theme | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/vev/pkg/renderer" | |
| 7 | + | "github.com/stretchr/testify/require" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | func TestNewStylesComposesSemanticStyles(t *testing.T) { | |
| 11 | + | theme := Theme{ | |
| 12 | + | Foreground: renderer.RGB{R: 0xc8, G: 0xc8, B: 0xc8}, | |
| 13 | + | Background: renderer.RGB{R: 0x0a, G: 0x14, B: 0x1e}, | |
| 14 | + | HasFG: true, | |
| 15 | + | HasBG: true, | |
| 16 | + | Known: true, | |
| 17 | + | TrueColor: true, | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | styles := NewStyles(theme) | |
| 21 | + | statusBar := StatusBarStyle(theme) | |
| 22 | + | accent := AccentStyle(theme) | |
| 23 | + | selection := SelectionStyle(theme) | |
| 24 | + | ||
| 25 | + | require.True(t, styles.StatusBar.Equal(statusBar)) | |
| 26 | + | require.True(t, styles.Accent.Equal(accent)) | |
| 27 | + | require.True(t, styles.Border.Equal(BorderStyle(theme))) | |
| 28 | + | require.True(t, styles.Selection.Equal(selection)) | |
| 29 | + | require.True(t, styles.CopyStatus.Equal(selection)) | |
| 30 | + | require.True(t, styles.PaletteDesc.Equal(MutedTextStyle(theme))) | |
| 31 | + | require.True(t, styles.TabName.Equal(EmphasisStyle(statusBar, theme))) | |
| 32 | + | require.True(t, styles.TabNameActive.Equal(EmphasisStyle(accent, theme))) | |
| 33 | + | require.True(t, styles.TabTitle.Equal(MutedVariantStyle(statusBar, theme))) | |
| 34 | + | require.True(t, styles.TabTitleActive.Equal(MutedVariantStyle(accent, theme))) | |
| 35 | + | require.True(t, styles.PickerName.Equal(EmphasisStyle(renderer.DefaultStyle(), theme))) | |
| 36 | + | require.True(t, styles.PickerSelectionName.Equal(EmphasisStyle(selection, theme))) | |
| 37 | + | require.True(t, styles.PickerSelectionMuted.Equal(MutedVariantStyle(selection, theme))) | |
| 38 | + | require.True(t, styles.PickerSeparator.Equal(MutedTextStyle(theme))) | |
| 39 | + | } | |
| @@ -0,0 +1,39 @@ | |||||
| 1 | + | package theme | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/vev/pkg/renderer" | |||
| 7 | + | "github.com/stretchr/testify/require" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | func TestNewStylesComposesSemanticStyles(t *testing.T) { | |||
| 11 | + | theme := Theme{ | |||
| 12 | + | Foreground: renderer.RGB{R: 0xc8, G: 0xc8, B: 0xc8}, | |||
| 13 | + | Background: renderer.RGB{R: 0x0a, G: 0x14, B: 0x1e}, | |||
| 14 | + | HasFG: true, | |||
| 15 | + | HasBG: true, | |||
| 16 | + | Known: true, | |||
| 17 | + | TrueColor: true, | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | styles := NewStyles(theme) | |||
| 21 | + | statusBar := StatusBarStyle(theme) | |||
| 22 | + | accent := AccentStyle(theme) | |||
| 23 | + | selection := SelectionStyle(theme) | |||
| 24 | + | ||||
| 25 | + | require.True(t, styles.StatusBar.Equal(statusBar)) | |||
| 26 | + | require.True(t, styles.Accent.Equal(accent)) | |||
| 27 | + | require.True(t, styles.Border.Equal(BorderStyle(theme))) | |||
| 28 | + | require.True(t, styles.Selection.Equal(selection)) | |||
| 29 | + | require.True(t, styles.CopyStatus.Equal(selection)) | |||
| 30 | + | require.True(t, styles.PaletteDesc.Equal(MutedTextStyle(theme))) | |||
| 31 | + | require.True(t, styles.TabName.Equal(EmphasisStyle(statusBar, theme))) | |||
| 32 | + | require.True(t, styles.TabNameActive.Equal(EmphasisStyle(accent, theme))) | |||
| 33 | + | require.True(t, styles.TabTitle.Equal(MutedVariantStyle(statusBar, theme))) | |||
| 34 | + | require.True(t, styles.TabTitleActive.Equal(MutedVariantStyle(accent, theme))) | |||
| 35 | + | require.True(t, styles.PickerName.Equal(EmphasisStyle(renderer.DefaultStyle(), theme))) | |||
| 36 | + | require.True(t, styles.PickerSelectionName.Equal(EmphasisStyle(selection, theme))) | |||
| 37 | + | require.True(t, styles.PickerSelectionMuted.Equal(MutedVariantStyle(selection, theme))) | |||
| 38 | + | require.True(t, styles.PickerSeparator.Equal(MutedTextStyle(theme))) | |||
| 39 | + | } | |||