fix(ui): mark sidebar focus with an accent colour, not bold (#25)

The focused sidebar panel border (ADR-0046 DC3) was bright `fg` plus
`Modifier::BOLD`. Bold box-drawing glyphs render as broken/gapped
line-art in the asciinema cast player and are fragile in some terminals.

`panel_border_style` now marks focus with a non-bold accent colour
(`theme.mode_simple`, blue); the unfocused border stays muted. Bold is
untouched on text spans (titles, key hints) — the constraint is
specifically that box-drawing borders carry no bold attribute.

Pure style change: the Tier-2 snapshots are text-only so none needed
re-accepting; the Tier-1 assertion was updated and a render-level test
now checks the rendered border cells carry the accent and no bold.

ADR-0046 Amendment 1.
This commit is contained in:
claude@clouddev1
2026-06-12 15:01:26 +00:00
parent 3d4a0fd45e
commit fde50ce3bf
3 changed files with 95 additions and 9 deletions
+70 -8
View File
@@ -275,13 +275,15 @@ fn render_nav_sidebar_overlay(app: &mut App, theme: &Theme, frame: &mut Frame<'_
render_relationships_panel(app, theme, frame, parts[1]);
}
/// Border style for a sidebar panel: an accented, bold border when it
/// holds navigation focus (ADR-0046 DC3), the muted border otherwise.
/// Border style for a sidebar panel: a non-bold **accent colour**
/// border when it holds navigation focus (ADR-0046 DC3, refined by
/// Amendment 1 / issue #25), the muted border otherwise. The focus
/// cue is the accent hue, NOT `Modifier::BOLD` — bold box-drawing
/// glyphs render as broken/gapped line-art in the asciinema player
/// and are fragile in some terminals.
fn panel_border_style(theme: &Theme, focused: bool) -> Style {
if focused {
Style::default()
.fg(theme.fg)
.add_modifier(Modifier::BOLD)
Style::default().fg(theme.mode_simple)
} else {
Style::default().fg(theme.border)
}
@@ -3027,16 +3029,76 @@ mod tests {
#[test]
fn focused_panel_gets_an_accent_border() {
// ADR-0046 DC3: the focused sidebar panel is accent-bordered.
// ADR-0046 DC3 (Amendment 1, issue #25): the focused sidebar
// panel is marked by a non-bold accent COLOUR, not bold. Bold
// box-drawing glyphs render as broken/gapped line-art in the
// asciinema player (and are fragile in some terminals), so the
// focus cue is the accent hue against the muted unfocused
// border — never a `Modifier::BOLD` on the border.
let theme = Theme::dark();
let focused = panel_border_style(&theme, true);
let normal = panel_border_style(&theme, false);
assert_eq!(focused.fg, Some(theme.fg));
assert!(focused.add_modifier.contains(Modifier::BOLD));
assert_eq!(focused.fg, Some(theme.mode_simple));
assert!(
!focused.add_modifier.contains(Modifier::BOLD),
"the focused border must NOT be bold (issue #25)",
);
assert_eq!(normal.fg, Some(theme.border));
assert!(!normal.add_modifier.contains(Modifier::BOLD));
}
#[test]
fn focused_panel_border_cells_are_accent_colour_not_bold() {
// Full-stack guard for issue #25: the accent colour (and the
// absence of bold) must reach the actual rendered border cells,
// not just `panel_border_style` in isolation. With the Tables
// panel focused, its box-drawing border cells carry
// `theme.mode_simple` and never `Modifier::BOLD`; with no panel
// focused, no border cell wears the accent colour.
const BOX_DRAWING: &[char] = &['╭', '╮', '╰', '╯', '─', '│'];
let is_border = |sym: &str| sym.chars().all(|c| BOX_DRAWING.contains(&c));
let theme = Theme::dark();
let mut app = App::new();
app.tables = vec!["Customers".to_string(), "Orders".to_string()];
app.nav_focus = NavFocus::SidebarTables;
let buf = render_to_buffer(&mut app, &theme, 110, 24);
let mut accent_border_cells = 0;
for y in 0..buf.area.height {
for x in 0..buf.area.width {
let cell = &buf[(x, y)];
if is_border(cell.symbol()) && cell.fg == theme.mode_simple {
accent_border_cells += 1;
assert!(
!cell.modifier.contains(Modifier::BOLD),
"focused border cell at ({x},{y}) must not be bold (issue #25)",
);
}
}
}
assert!(
accent_border_cells > 0,
"the focused Tables panel must render accent-coloured border cells",
);
// With nothing focused (Input), no border cell wears the accent.
let mut app2 = App::new();
app2.tables = vec!["Customers".to_string()];
app2.nav_focus = NavFocus::Input;
let buf2 = render_to_buffer(&mut app2, &theme, 110, 24);
for y in 0..buf2.area.height {
for x in 0..buf2.area.width {
let cell = &buf2[(x, y)];
if is_border(cell.symbol()) {
assert_ne!(
cell.fg, theme.mode_simple,
"no border cell may wear the focus accent when nothing is focused (at {x},{y})",
);
}
}
}
}
#[test]
fn focused_tables_panel_scrolls_and_clamps() {
// ADR-0046 DC3: more tables than fit → a large offset reveals the