use ratatui::prelude::*;
use unicode_width::UnicodeWidthStr;
use super::super::super::types::InlineMessageKind;
use super::super::{Session, text_utils};
use crate::config::constants::ui;
impl Session {
#[allow(dead_code)]
pub(super) fn message_divider_line(
&self,
width: usize,
kind: InlineMessageKind,
) -> Line<'static> {
if width == 0 {
return Line::default();
}
let content = ui::INLINE_USER_MESSAGE_DIVIDER_SYMBOL.repeat(width);
let style = self.message_divider_style(kind);
Line::from(content).style(style)
}
#[allow(dead_code)]
pub(super) fn message_divider_style(&self, kind: InlineMessageKind) -> Style {
self.styles.message_divider_style(kind)
}
#[allow(dead_code)]
pub(super) fn justify_wrapped_lines(
&self,
lines: Vec<Line<'static>>,
max_width: usize,
kind: InlineMessageKind,
) -> Vec<Line<'static>> {
if max_width == 0 {
return lines;
}
let total = lines.len();
let mut justified = Vec::with_capacity(total);
let mut in_fenced_block = false;
for (index, line) in lines.into_iter().enumerate() {
let is_last = index + 1 == total;
let line_text_storage: std::borrow::Cow<'_, str> = if line.spans.len() == 1 {
std::borrow::Cow::Borrowed(&*line.spans[0].content)
} else {
std::borrow::Cow::Owned(
line.spans
.iter()
.map(|span| &*span.content)
.collect::<String>(),
)
};
let line_text: &str = &line_text_storage;
let trimmed_start = line_text.trim_start();
let mut next_in_fenced_block = in_fenced_block;
let is_fence_line =
trimmed_start.starts_with("```") || trimmed_start.starts_with("~~~");
if is_fence_line {
next_in_fenced_block = !in_fenced_block;
}
let todo_state = text_utils::detect_todo_state(line_text);
let processed_line = if self.is_diff_line(&line) {
self.pad_diff_line(&line, max_width)
} else if todo_state == text_utils::TodoState::Completed
&& self.appearance.dim_completed_todos
{
self.apply_completed_todo_style(&line)
} else if kind == InlineMessageKind::Agent
&& !in_fenced_block
&& !is_fence_line
&& self.should_justify_message_line(&line, max_width, is_last)
{
self.justify_message_line(&line, max_width)
} else {
line
};
justified.push(processed_line);
in_fenced_block = next_in_fenced_block;
}
justified
}
fn apply_completed_todo_style(&self, line: &Line<'static>) -> Line<'static> {
let dimmed_spans: Vec<Span<'static>> = line
.spans
.iter()
.map(|span| {
Span::styled(
span.content.clone(),
span.style
.add_modifier(Modifier::DIM)
.add_modifier(Modifier::CROSSED_OUT),
)
})
.collect();
Line::from(dimmed_spans)
}
#[allow(dead_code)]
pub(super) fn should_justify_message_line(
&self,
line: &Line<'static>,
max_width: usize,
is_last: bool,
) -> bool {
if is_last || max_width == 0 {
return false;
}
if line.spans.len() != 1 {
return false;
}
let text: &str = &line.spans[0].content;
if text.trim().is_empty() {
return false;
}
if text.starts_with(char::is_whitespace) {
return false;
}
let trimmed = text.trim();
if trimmed.starts_with(['-', '*', '`', '>', '#']) {
return false;
}
if trimmed.contains("```") {
return false;
}
let width = UnicodeWidthStr::width(trimmed);
if width >= max_width || width < max_width / 2 {
return false;
}
text_utils::justify_plain_text(text, max_width).is_some()
}
#[allow(dead_code)]
pub(super) fn justify_message_line(
&self,
line: &Line<'static>,
max_width: usize,
) -> Line<'static> {
let span = &line.spans[0];
if let Some(justified) = text_utils::justify_plain_text(&span.content, max_width) {
Line::from(justified).style(span.style)
} else {
line.clone()
}
}
#[allow(dead_code)]
pub(super) fn is_diff_line(&self, line: &Line<'static>) -> bool {
if line.spans.is_empty() {
return false;
}
let has_bg_color = line.spans.iter().any(|span| span.style.bg.is_some());
if !has_bg_color {
return false;
}
let first_span_char = line.spans[0].content.chars().next();
matches!(first_span_char, Some('+') | Some('-') | Some(' '))
}
#[allow(dead_code)]
pub(super) fn pad_diff_line(&self, line: &Line<'static>, max_width: usize) -> Line<'static> {
if max_width == 0 || line.spans.is_empty() {
return line.clone();
}
let line_width: usize = line
.spans
.iter()
.map(|s| {
s.content
.chars()
.map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
.sum::<usize>()
})
.sum();
let padding_needed = max_width.saturating_sub(line_width);
if padding_needed == 0 {
return line.clone();
}
let padding_style = line
.spans
.iter()
.find_map(|span| span.style.bg)
.map(|bg| Style::default().bg(bg))
.unwrap_or_default();
let mut new_spans = Vec::with_capacity(line.spans.len() + 1);
new_spans.extend(line.spans.iter().cloned());
new_spans.push(Span::styled(" ".repeat(padding_needed), padding_style));
Line::from(new_spans)
}
}