Compare commits

..

2 Commits

Author SHA1 Message Date
564df43910 Merge pull request 'fix: always reserve space for selection marker to prevent text shift' (#23) from refactor into main
Reviewed-on: #23
2026-02-24 12:59:04 +00:00
Mikhail Kilin
a095fe277b fix: always reserve space for selection marker to prevent text shift
Some checks failed
ci/woodpecker/pr/check Pipeline failed
Render "  " (2 spaces) for unselected messages instead of nothing,
so text stays aligned when navigating with the ▶ selection indicator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:49:08 +03:00
22 changed files with 54 additions and 58 deletions

View File

@@ -221,9 +221,9 @@ pub fn render_message_bubble(
let mut lines = Vec::new();
let is_selected = selected_msg_id == Some(msg.id());
// Маркер выбора
let selection_marker = if is_selected { "" } else { "" };
let marker_len = selection_marker.chars().count();
// Маркер выбора (всегда резервируем место для ▶, чтобы текст не сдвигался)
let selection_marker = if is_selected { "" } else { " " };
let marker_len = 2;
// Цвет сообщения
let msg_color = if is_selected {
@@ -306,16 +306,16 @@ pub fn render_message_bubble(
let full_len = line_len + time_mark_len + marker_len;
let padding = content_width.saturating_sub(full_len + 1);
let mut line_spans = vec![Span::raw(" ".repeat(padding))];
if is_selected && i == 0 {
// Одна строка — маркер на ней
if i == 0 {
// Первая (или единственная) строка — маркер
line_spans.push(Span::styled(
selection_marker,
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
));
} else if is_selected {
// Последняя строка multi-line — пробелы вместо маркера
} else {
// Остальные строки multi-line — пробелы вместо маркера
line_spans.push(Span::raw(" ".repeat(marker_len)));
}
line_spans.extend(formatted_spans);
@@ -327,14 +327,14 @@ pub fn render_message_bubble(
} else {
let padding = content_width.saturating_sub(line_len + marker_len + 1);
let mut line_spans = vec![Span::raw(" ".repeat(padding))];
if i == 0 && is_selected {
if i == 0 {
line_spans.push(Span::styled(
selection_marker,
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
));
} else if is_selected {
} else {
// Средние строки multi-line — пробелы вместо маркера
line_spans.push(Span::raw(" ".repeat(marker_len)));
}
@@ -364,14 +364,12 @@ pub fn render_message_bubble(
if i == 0 {
let mut line_spans = vec![];
if is_selected {
line_spans.push(Span::styled(
selection_marker,
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
));
}
line_spans
.push(Span::styled(format!(" {}", time_str), Style::default().fg(Color::Gray)));
line_spans.push(Span::raw(" "));
@@ -548,8 +546,8 @@ pub fn render_album_bubble(
let is_selected = messages.iter().any(|m| selected_msg_id == Some(m.id()));
let is_outgoing = messages.first().is_some_and(|m| m.is_outgoing());
// Selection marker
let selection_marker = if is_selected { "" } else { "" };
// Selection marker (всегда резервируем место)
let selection_marker = if is_selected { "" } else { " " };
// Фильтруем фото
let photos: Vec<&MessageInfo> = messages.iter().filter(|m| m.has_photo()).collect();
@@ -567,15 +565,13 @@ pub fn render_album_bubble(
let cols = photo_count.min(ALBUM_GRID_MAX_COLS);
let rows = photo_count.div_ceil(cols);
// Добавляем маркер выбора на первую строку
if is_selected {
// Добавляем маркер выбора на первую строку (всегда — для постоянного отступа)
lines.push(Line::from(vec![Span::styled(
selection_marker,
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)]));
}
let grid_start_line = lines.len();