Enhance TUI and core functionality: add header rendering, improve message formatting, and refine provider/model handling logic. Update dependencies.
This commit is contained in:
@@ -15,7 +15,7 @@ impl MessageFormatter {
|
||||
Self {
|
||||
wrap_width: wrap_width.max(20),
|
||||
show_role_labels,
|
||||
preserve_empty_lines: true,
|
||||
preserve_empty_lines: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,36 +25,51 @@ impl MessageFormatter {
|
||||
self
|
||||
}
|
||||
|
||||
/// Render a message to a list of visual lines ready for display
|
||||
pub fn format_message(&self, message: &Message) -> Vec<String> {
|
||||
let mut lines = Vec::new();
|
||||
/// Update the wrap width
|
||||
pub fn set_wrap_width(&mut self, width: usize) {
|
||||
self.wrap_width = width.max(20);
|
||||
}
|
||||
|
||||
pub fn format_message(&self, message: &Message) -> Vec<String> {
|
||||
// 1) Normalize line breaks to '\n' (handles CR, NEL, LS, PS)
|
||||
let normalized: String = message
|
||||
.content
|
||||
.chars()
|
||||
.map(|ch| match ch {
|
||||
'\r' | '\u{0085}' | '\u{2028}' | '\u{2029}' => '\n',
|
||||
_ => ch,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// 2) Collapse: remove whitespace-only lines; keep exactly one '\n' between content lines
|
||||
let mut content = normalized
|
||||
.split('\n')
|
||||
.map(|l| l.trim_end()) // trim trailing spaces per line
|
||||
.filter(|l| !l.trim().is_empty()) // drop blank/whitespace-only lines
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.trim() // trim leading/trailing whitespace
|
||||
.to_string();
|
||||
|
||||
let mut content = message.content.trim_end().to_string();
|
||||
if content.is_empty() && self.preserve_empty_lines {
|
||||
content.push(' ');
|
||||
}
|
||||
|
||||
// 3) Wrap
|
||||
let options = Options::new(self.wrap_width)
|
||||
.break_words(true)
|
||||
.word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
|
||||
|
||||
let wrapped = wrap(&content, &options);
|
||||
// 4) Post: rtrim each visual line; drop any whitespace-only lines
|
||||
let mut lines: Vec<String> = wrap(&content, &options)
|
||||
.into_iter()
|
||||
.map(|s| s.trim_end().to_string())
|
||||
.filter(|s| !s.trim().is_empty())
|
||||
.collect();
|
||||
|
||||
if self.show_role_labels {
|
||||
let label = format!("{}:", message.role.to_string().to_uppercase());
|
||||
if let Some(first) = wrapped.first() {
|
||||
lines.push(format!("{label} {first}"));
|
||||
for line in wrapped.iter().skip(1) {
|
||||
lines.push(format!("{:width$} {line}", "", width = label.len()));
|
||||
}
|
||||
} else {
|
||||
lines.push(label);
|
||||
}
|
||||
} else {
|
||||
for line in wrapped {
|
||||
lines.push(line.into_owned());
|
||||
}
|
||||
}
|
||||
// 5) Belt & suspenders: remove leading/trailing blanks if any survived
|
||||
while lines.first().map_or(false, |s| s.trim().is_empty()) { lines.remove(0); }
|
||||
while lines.last().map_or(false, |s| s.trim().is_empty()) { lines.pop(); }
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
@@ -87,6 +87,11 @@ impl SessionController {
|
||||
&self.formatter
|
||||
}
|
||||
|
||||
/// Update the wrap width of the message formatter
|
||||
pub fn set_formatter_wrap_width(&mut self, width: usize) {
|
||||
self.formatter.set_wrap_width(width);
|
||||
}
|
||||
|
||||
/// Access configuration
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
|
||||
Reference in New Issue
Block a user