v0.7.0 4c16dd8

styles.go RawHistoryFolder

1package theme
2
3import (
4	"math"
5
6	renderer "github.com/bnema/vev-vt"
7	"github.com/bnema/vev/internal/domain"
8)
9
10const accentContrastMin = 3.0
11
12// RelativeLuminance returns a color's WCAG 2.x relative luminance.
13func RelativeLuminance(color renderer.RGB) float64 {
14	linearize := func(channel uint8) float64 {
15		srgb := float64(channel) / 255
16		if srgb <= 0.04045 {
17			return srgb / 12.92
18		}
19		return math.Pow((srgb+0.055)/1.055, 2.4)
20	}
21
22	return 0.2126*linearize(color.R) +
23		0.7152*linearize(color.G) +
24		0.0722*linearize(color.B)
25}
26
27// ContrastRatio returns the WCAG 2.x contrast ratio between two colors.
28func ContrastRatio(a, b renderer.RGB) float64 {
29	aLuminance := RelativeLuminance(a)
30	bLuminance := RelativeLuminance(b)
31	lighter := math.Max(aLuminance, bLuminance)
32	darker := math.Min(aLuminance, bLuminance)
33	return (lighter + 0.05) / (darker + 0.05)
34}
35
36// PulseColor derives the attention pulse color at intensity while preserving
37// the base style's remaining attributes.
38func PulseColor(base renderer.Style, intensity float64) renderer.Style {
39	style := base
40	style.Bold = true
41	if base.HasForegroundRGB && base.HasBackgroundRGB {
42		style.ForegroundRGB = Blend(base.BackgroundRGB, base.ForegroundRGB, intensity)
43	} else {
44		style.HasForegroundRGB = false
45		style.Foreground = 244 + int(intensity*11)
46	}
47	return style
48}
49
50// PulseAttribute is PulseColor for reduced-color attachments: the glyph's
51// on/off beat already pulses, so the visible beat is just bold. Grey indexes
52// would quantize to arbitrary slots or vanish.
53func PulseAttribute(base renderer.Style) renderer.Style {
54	base.Bold = true
55	return base
56}
57
58// MRUFade fades an MRU style by position, reaching 60 percent of the terminal
59// background color at the oldest entry.
60func MRUFade(base renderer.Style, t Theme, i, count int) renderer.Style {
61	if count <= 1 || !base.HasForegroundRGB || !base.HasBackgroundRGB || !t.HasBG {
62		return base
63	}
64	amount := (float64(i) / float64(count-1)) * 0.6
65	base.ForegroundRGB = Blend(base.ForegroundRGB, t.Background, amount)
66	base.BackgroundRGB = Blend(base.BackgroundRGB, t.Background, amount)
67	return base
68}
69
70// Styles is the complete semantic set of terminal chrome styles. The legacy
71// aliases remain until daemon renderers consume the semantic fields directly.
72type Styles struct {
73	// Reduced marks the ANSI16/monochrome policy: no RGB anywhere, and the
74	// attention pulse uses bold instead of a grey ramp (see ResolveForColor).
75	Reduced bool
76
77	// mruStyles are all position/count combinations that status composition
78	// can display. They keep OKLab interpolation out of render paths.
79	mruStyles [9][9]renderer.Style
80
81	SurfaceBar      renderer.Style
82	SurfaceInactive renderer.Style
83	SurfaceRecent   renderer.Style
84	SurfaceActive   renderer.Style
85	BorderMuted     renderer.Style
86	BorderActive    renderer.Style
87	// BorderWarn is the amber-hued warn-severity border. Unlike every other
88	// role in this camaïeu palette it does not blend toward the resolved
89	// accent color: it keeps the accent's lightness/chroma band but shifts
90	// hue to a fixed amber target (see warnHueDegrees), so Warn reads as
91	// amber and stays distinct from Error regardless of the accent hue.
92	BorderWarn renderer.Style
93	// NeutralBorder is the non-accent structural border for pane dividers and
94	// unfocused pane title bars. It is resolved with the rest of the immutable
95	// chrome snapshot so composition never derives theme colors.
96	NeutralBorder renderer.Style
97
98	TabInactive      renderer.Style
99	TabInactiveTitle renderer.Style
100	TabActive        renderer.Style
101	TabActiveTitle   renderer.Style
102	MRURecent        renderer.Style
103
104	PickerBase      renderer.Style
105	PickerSelection renderer.Style
106	// HintKey is the keycap badge style for key hints: an accent background
107	// over " key " padding, distinct from PickerSelection's full-line emphasis.
108	HintKey           renderer.Style
109	PickerDescription renderer.Style
110	PickerSeparator   renderer.Style
111	PromptBase        renderer.Style
112	CopyStatus        renderer.Style
113	SearchSelection   renderer.Style
114
115	// Compatibility aliases for pre-cache render paths.
116	StatusBar            renderer.Style
117	Accent               renderer.Style
118	Border               renderer.Style
119	Selection            renderer.Style
120	PaletteDesc          renderer.Style
121	TabName              renderer.Style
122	TabNameActive        renderer.Style
123	TabTitle             renderer.Style
124	TabTitleActive       renderer.Style
125	PickerName           renderer.Style
126	PickerSelectionName  renderer.Style
127	PickerSelectionMuted renderer.Style
128}
129
130// NewStyles applies the default automatic policy. New callers that own a
131// policy should use Resolve and retain its complete immutable result.
132func NewStyles(t Theme) Styles {
133	return Resolve(t, domain.ThemeAccent{Mode: domain.ThemeAccentAuto}).Styles
134}
135
136// MRUStyle returns the position-dependent recent-session surface. Color
137// interpolation stays owned by theme; renderers consume only this cached
138// semantic style snapshot.
139func (s Styles) MRUStyle(index, count int) renderer.Style {
140	if count <= 0 || count > len(s.mruStyles) {
141		return s.MRURecent
142	}
143	if index < 0 {
144		index = 0
145	}
146	if index >= count {
147		index = count - 1
148	}
149	return s.mruStyles[count-1][index]
150}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close