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>
This commit is contained in:
Mikhail Kilin
2026-02-24 15:49:08 +03:00
parent dfd4184039
commit a095fe277b
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(
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),
)]));
}
// Добавляем маркер выбора на первую строку (всегда — для постоянного отступа)
lines.push(Line::from(vec![Span::styled(
selection_marker,
Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
)]));
let grid_start_line = lines.len();

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Mom ──────────────── │
│ (14:33) What do you think about this?
(14:33) What do you think about this? │
│ │
│ │
│ │

View File

@@ -9,9 +9,9 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Alice ──────────────── │
│ (14:33) 📷 [Фото]
│ (14:33) Caption for album
│ (14:33) 📷 [Фото]
(14:33) 📷 [Фото] │
(14:33) Caption for album │
(14:33) 📷 [Фото] │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Alice ──────────────── │
│ (14:33) 📷 [Фото]
(14:33) 📷 [Фото] │
│▶ (14:33) 📷 [Фото] │
│ │
│ │

View File

@@ -9,10 +9,10 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Alice ──────────────── │
│ (14:33) Regular message before
│ (14:33) 📷 [Фото]
│ (14:33) Album caption
│ (14:33) Regular message after
(14:33) Regular message before │
(14:33) 📷 [Фото] │
(14:33) Album caption │
(14:33) Regular message after │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Message from the past
(14:33) Message from the past │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33 ✎) Edited text
(14:33 ✎) Edited text │
│ │
│ │
│ │

View File

@@ -10,7 +10,7 @@ expression: output
│ │
│User ──────────────── │
│↪ Переслано от Alice │
│ (14:33) Forwarded content
(14:33) Forwarded content │
│ │
│ │
│ │

View File

@@ -9,9 +9,9 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) This is a very long message that should wrap across multiple lines
│ when rendered in the terminal UI. Let's make it even longer to
│ ensure we test the wrapping behavior properly.
(14:33) This is a very long message that should wrap across multiple lines │
when rendered in the terminal UI. Let's make it even longer to │
ensure we test the wrapping behavior properly. │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) **bold** *italic* `code`
(14:33) **bold** *italic* `code` │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Check [this](https://example.com) and @username
(14:33) Check [this](https://example.com) and @username │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Spoiler: ||hidden text||
(14:33) Spoiler: ||hidden text|| │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) [Фото]
(14:33) [Фото] │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Popular message
(14:33) Popular message │
│[👍 ] 5 👎 3 │
│ │
│ │

View File

@@ -10,7 +10,7 @@ expression: output
│ │
│User ──────────────── │
│┌ Mom: Original message text │
│ (14:33) This is a reply
(14:33) This is a reply │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Selected message
(14:33) Selected message │
│ │
│ │
│ │

View File

@@ -9,11 +9,11 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Alice ──────────────── │
│ (14:33) First message
│ (14:33) Second message
(14:33) First message │
(14:33) Second message │
│ │
│Bob ──────────────── │
│ (14:33) Third message
(14:33) Third message │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│Mom ──────────────── │
│ (14:33) Hello there!
(14:33) Hello there! │
│ │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Great!
(14:33) Great! │
│[👍 ] │
│ │
│ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) React to this
(14:33) React to this │
│ │
│ ┌ Выбери реакцию ────────────────────────────────┐ │
│ │ │ │

View File

@@ -9,7 +9,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) React to this
(14:33) React to this │
│ │
│ ┌ Выбери реакцию ────────────────────────────────┐ │
│ │ │ │

View File

@@ -10,7 +10,7 @@ expression: output
│ ──────── 02.01.2022 ──────── │
│ │
│User ──────────────── │
│ (14:33) Regular message
(14:33) Regular message │
│ │
│ │
│ │