Skip to content

Commit 77122e0

Browse files
fix(tui): reuse focus styles and dropdown rendering
Remove source-quality regressions without changing the banner green palette or its accessible focus backing. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent 9796db1 commit 77122e0

4 files changed

Lines changed: 52 additions & 173 deletions

File tree

‎src/cortex-tui/src/lock_boards.rs‎

Lines changed: 19 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
99
use cortex_core::markdown::{TableBuilder, render_table};
1010
use cortex_core::style::{
11-
ACCENT, DIFF_ADD, ERROR, HAIRLINE, PANEL_BG, SELECTION_BG, SUCCESS, SURFACE_2, TEXT, TEXT_DIM,
12-
THINKING, USER_TURN_BG, WARNING,
11+
ACCENT, CortexStyle, DIFF_ADD, ERROR, HAIRLINE, PANEL_BG, SELECTION_BG, SUCCESS, SURFACE_2,
12+
TEXT, TEXT_DIM, THINKING, USER_TURN_BG, WARNING,
1313
};
1414
use ratatui::buffer::Buffer;
1515
use ratatui::layout::Rect;
@@ -250,10 +250,7 @@ fn paint_composer(area: Rect, buf: &mut Buffer, y: u16, composer: Composer<'_>)
250250
}
251251
let prefix = if i == 0 { "> " } else { " " };
252252
let prefix_style = if i == 0 {
253-
Style::default()
254-
.fg(ACCENT)
255-
.add_modifier(Modifier::BOLD)
256-
.bg(cortex_core::style::TEXT)
253+
CortexStyle::selected().add_modifier(Modifier::BOLD)
257254
} else {
258255
Style::default().fg(TEXT)
259256
};
@@ -459,7 +456,7 @@ fn picker_option(
459456
}
460457
}
461458
let marker_style = if selected {
462-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT)
459+
CortexStyle::selected()
463460
} else {
464461
Style::default().fg(TEXT_DIM)
465462
};
@@ -471,10 +468,7 @@ fn picker_option(
471468
}
472469
let indent = (x - area.x) as usize;
473470
let label_style = if selected {
474-
Style::default()
475-
.fg(ACCENT)
476-
.add_modifier(Modifier::BOLD)
477-
.bg(cortex_core::style::TEXT)
471+
CortexStyle::selected().add_modifier(Modifier::BOLD)
478472
} else {
479473
Style::default().fg(TEXT)
480474
};
@@ -926,11 +920,8 @@ fn board_palette(area: Rect, buf: &mut Buffer) {
926920
let (marker, marker_style, cmd_style) = if selected {
927921
(
928922
"> ",
929-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
930-
Style::default()
931-
.fg(ACCENT)
932-
.add_modifier(Modifier::BOLD)
933-
.bg(cortex_core::style::TEXT),
923+
CortexStyle::selected(),
924+
CortexStyle::selected().add_modifier(Modifier::BOLD),
934925
)
935926
} else {
936927
(
@@ -1446,7 +1437,7 @@ fn board_permission(area: Rect, buf: &mut Buffer) {
14461437
fill_row(buf, area, y, SELECTION_BG);
14471438
}
14481439
let style = if *selected {
1449-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT)
1440+
CortexStyle::selected()
14501441
} else {
14511442
Style::default().fg(TEXT)
14521443
};
@@ -1549,10 +1540,7 @@ fn board_plan(area: Rect, buf: &mut Buffer) {
15491540
area.x + 4,
15501541
y,
15511542
part,
1552-
Style::default()
1553-
.fg(ACCENT)
1554-
.add_modifier(Modifier::BOLD)
1555-
.bg(cortex_core::style::TEXT),
1543+
CortexStyle::selected().add_modifier(Modifier::BOLD),
15561544
);
15571545
}
15581546
}
@@ -2222,10 +2210,7 @@ fn board_files(area: Rect, buf: &mut Buffer) {
22222210
}
22232211
let mut x = area.x;
22242212
let (marker, marker_style) = if selected {
2225-
(
2226-
"> ",
2227-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
2228-
)
2213+
("> ", CortexStyle::selected())
22292214
} else {
22302215
("· ", Style::default().fg(TEXT_DIM))
22312216
};
@@ -2415,10 +2400,7 @@ fn board_jobs(area: Rect, buf: &mut Buffer) {
24152400
Color::Reset
24162401
};
24172402
let (marker, marker_style) = if job.selected {
2418-
(
2419-
"> ",
2420-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
2421-
)
2403+
("> ", CortexStyle::selected())
24222404
} else {
24232405
("· ", Style::default().fg(TEXT_DIM))
24242406
};
@@ -2430,10 +2412,7 @@ fn board_jobs(area: Rect, buf: &mut Buffer) {
24302412
Style::default().fg(job.icon_color).bg(bg),
24312413
);
24322414
let title_style = if job.selected {
2433-
Style::default()
2434-
.fg(ACCENT)
2435-
.add_modifier(Modifier::BOLD)
2436-
.bg(cortex_core::style::TEXT)
2415+
CortexStyle::selected().add_modifier(Modifier::BOLD)
24372416
} else {
24382417
Style::default().fg(TEXT)
24392418
};
@@ -2857,20 +2836,12 @@ fn board_config(area: Rect, buf: &mut Buffer) {
28572836
let value_fit = first_fitting_line(value, w.saturating_sub(label.chars().count() + 4));
28582837
if *selected {
28592838
fill_row(buf, area, y, SELECTION_BG);
2860-
buf.set_string(
2861-
area.x,
2862-
y,
2863-
"> ",
2864-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
2865-
);
2839+
buf.set_string(area.x, y, "> ", CortexStyle::selected());
28662840
buf.set_string(
28672841
area.x + 2,
28682842
y,
28692843
&label,
2870-
Style::default()
2871-
.fg(ACCENT)
2872-
.add_modifier(Modifier::BOLD)
2873-
.bg(cortex_core::style::TEXT),
2844+
CortexStyle::selected().add_modifier(Modifier::BOLD),
28742845
);
28752846
// The selected value keeps its column gap and never ends on a
28762847
// dangling `·` when the `⏎ edit` affordance takes the right edge.
@@ -3199,10 +3170,7 @@ fn board_question(area: Rect, buf: &mut Buffer) {
31993170
fill_row(buf, area, y, SELECTION_BG);
32003171
}
32013172
let style = if *selected {
3202-
Style::default()
3203-
.fg(ACCENT)
3204-
.add_modifier(Modifier::BOLD)
3205-
.bg(cortex_core::style::TEXT)
3173+
CortexStyle::selected().add_modifier(Modifier::BOLD)
32063174
} else {
32073175
Style::default().fg(TEXT)
32083176
};
@@ -3243,20 +3211,12 @@ fn board_skills(area: Rect, buf: &mut Buffer) {
32433211
let desc_fit = ellipsis_fit(desc, w.saturating_sub(cmd.len() + 4));
32443212
if selected {
32453213
fill_row(buf, area, y, SELECTION_BG);
3246-
buf.set_string(
3247-
area.x,
3248-
y,
3249-
"> ",
3250-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
3251-
);
3214+
buf.set_string(area.x, y, "> ", CortexStyle::selected());
32523215
buf.set_string(
32533216
area.x + 2,
32543217
y,
32553218
cmd,
3256-
Style::default()
3257-
.fg(ACCENT)
3258-
.add_modifier(Modifier::BOLD)
3259-
.bg(cortex_core::style::TEXT),
3219+
CortexStyle::selected().add_modifier(Modifier::BOLD),
32603220
);
32613221
buf.set_string(
32623222
area.x + 2 + cmd.len() as u16 + 2,
@@ -3892,19 +3852,13 @@ fn board_multi_diff(area: Rect, buf: &mut Buffer) {
38923852
}
38933853
};
38943854
let (marker, marker_style) = if selected {
3895-
(
3896-
"> ",
3897-
Style::default().fg(ACCENT).bg(cortex_core::style::TEXT),
3898-
)
3855+
("> ", CortexStyle::selected())
38993856
} else {
39003857
("· ", Style::default().fg(TEXT_DIM))
39013858
};
39023859
buf.set_string(area.x, y, marker, marker_style);
39033860
let path_style = if selected {
3904-
Style::default()
3905-
.fg(ACCENT)
3906-
.add_modifier(Modifier::BOLD)
3907-
.bg(cortex_core::style::TEXT)
3861+
CortexStyle::selected().add_modifier(Modifier::BOLD)
39083862
} else {
39093863
Style::default().fg(TEXT)
39103864
};

‎src/cortex-tui/src/lock_proof.rs‎

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ mod tests {
823823
frame.ansi.contains(SELECTION_WASH),
824824
"{id} selection bar must be #262626"
825825
);
826-
let row = (0..40u16)
826+
(0..40u16)
827827
.find(|y| {
828828
(0..120u16).any(|x| {
829829
frame.buffer[(x, *y)].symbol() == ">"
@@ -833,14 +833,6 @@ mod tests {
833833
})
834834
})
835835
.unwrap_or_else(|| panic!("{id} has no banner green `>` row:\n{}", frame.plain));
836-
// The focused row carries the selection bar and a banner green caret.
837-
let has_bar =
838-
(0..120u16).any(|x| frame.buffer[(x, row)].style().bg == Some(SELECTION_BG));
839-
assert!(
840-
has_bar,
841-
"{id} selection bar missing:\n{}",
842-
row_text(&frame.buffer, row)
843-
);
844836
}
845837
}
846838

@@ -1007,12 +999,7 @@ mod tests {
1007999

10081000
#[test]
10091001
fn banned_colors_never_painted() {
1010-
// The interim banner green highlight is gone with the mint one: no scene
1011-
// paints banner green, the old `#221A38` retired violet wash, the mint pair, the old
1012-
// brand green, or the navy wash — the host terminal owns the
1013-
// background and the banner green lives on the focused selection alone.
1014-
// Mint `#00F5D4` / `#1A3330` never painted. Selection bar `#221A38`
1015-
// is allowed; inverted accent as a background is not (checked above).
1002+
// Retired mint, brand green, and navy colors must never paint.
10161003
const BANNED: [&str; 4] = ["0;245;212", "26;51;48", "0;255;163", "10;22;40"];
10171004
for id in lock_scene_ids() {
10181005
for size in SIZES {

‎src/cortex-tui/src/widgets/autocomplete.rs‎

Lines changed: 10 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
//! frame.render_widget(popup, area);
1717
//! ```
1818
19+
use super::scrollable_dropdown::ScrollableDropdown;
1920
use crate::app::{AutocompleteItem, AutocompleteState, AutocompleteTrigger};
20-
use cortex_core::style::{ACCENT, HAIRLINE, SELECTION_BG, SURFACE_1, TEXT, TEXT_DIM, TEXT_MUTED};
21+
use cortex_core::style::{HAIRLINE, SURFACE_1, TEXT, TEXT_MUTED};
2122
use ratatui::prelude::*;
2223
use ratatui::widgets::{
2324
Block, Borders, Clear, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget,
@@ -101,90 +102,6 @@ impl<'a> AutocompletePopup<'a> {
101102
(width, height)
102103
}
103104

104-
/// Renders a single item.
105-
fn render_item(
106-
&self,
107-
item: &AutocompleteItem,
108-
is_selected: bool,
109-
area: Rect,
110-
buf: &mut Buffer,
111-
) {
112-
// Background
113-
let bg = if is_selected { SELECTION_BG } else { SURFACE_1 };
114-
for x in area.x..area.x + area.width {
115-
if let Some(cell) = buf.cell_mut((x, area.y)) {
116-
cell.set_bg(bg);
117-
}
118-
}
119-
120-
let mut x = area.x + 1;
121-
122-
// Icon
123-
if item.icon != '\0' {
124-
let icon_style = Style::default().fg(TEXT_DIM).bg(bg);
125-
if let Some(cell) = buf.cell_mut((x, area.y)) {
126-
cell.set_char(item.icon).set_style(icon_style);
127-
}
128-
x += 2;
129-
}
130-
131-
// Label
132-
// The selected label is the banner green accent on the gray bar.
133-
let label_style = if is_selected {
134-
Style::default()
135-
.fg(ACCENT)
136-
.add_modifier(Modifier::BOLD)
137-
.bg(cortex_core::style::TEXT)
138-
} else {
139-
Style::default().fg(TEXT).bg(bg)
140-
};
141-
142-
for ch in item.label.chars() {
143-
if x >= area.x + area.width - 1 {
144-
break;
145-
}
146-
if let Some(cell) = buf.cell_mut((x, area.y)) {
147-
cell.set_char(ch).set_style(label_style);
148-
}
149-
x += 1;
150-
}
151-
152-
// Description (if there's room)
153-
if !item.description.is_empty() && x < area.x + area.width - 5 {
154-
// Add separator
155-
let sep_style = Style::default().fg(TEXT_MUTED).bg(bg);
156-
for ch in " - ".chars() {
157-
if x >= area.x + area.width - 1 {
158-
break;
159-
}
160-
if let Some(cell) = buf.cell_mut((x, area.y)) {
161-
cell.set_char(ch).set_style(sep_style);
162-
}
163-
x += 1;
164-
}
165-
166-
// Description text
167-
let desc_style = Style::default().fg(TEXT_DIM).bg(bg);
168-
for ch in item.description.chars() {
169-
if x >= area.x + area.width - 1 {
170-
break;
171-
}
172-
if let Some(cell) = buf.cell_mut((x, area.y)) {
173-
cell.set_char(ch).set_style(desc_style);
174-
}
175-
x += 1;
176-
}
177-
}
178-
179-
// Selection indicator
180-
if is_selected {
181-
let indicator_style = Style::default().fg(ACCENT).bg(cortex_core::style::TEXT);
182-
if let Some(cell) = buf.cell_mut((area.x, area.y)) {
183-
cell.set_char('>').set_style(indicator_style);
184-
}
185-
}
186-
}
187-
188105
/// Gets the title based on trigger type.
189106
fn get_title(&self) -> &'static str {
190107
match self.state.trigger {
@@ -253,7 +170,14 @@ impl Widget for AutocompletePopup<'_> {
253170
};
254171

255172
let is_selected = self.state.scroll_offset + i == self.state.selected;
256-
self.render_item(item, is_selected, item_area, buf);
173+
ScrollableDropdown::render_item(
174+
&item.label,
175+
&item.description,
176+
item.icon,
177+
is_selected,
178+
item_area,
179+
buf,
180+
);
257181
}
258182

259183
// Render scrollbar if needed

0 commit comments

Comments
 (0)