feat: finalized support for chat

This commit is contained in:
Georg Bauer
2026-07-24 20:14:26 +02:00
parent 3154f57a2f
commit 6560f20129
9 changed files with 356 additions and 40 deletions

View File

@@ -1,4 +1,6 @@
use super::{ActiveDownload, App, Message, ModelDownload, ModelOperation, models_path};
use super::{
ActiveDownload, App, Message, ModelDownload, ModelOperation, chat_scroll_id, models_path,
};
use crate::database::{ProjectWithSessions, Session};
use crate::model::{
self, DownloadPhase, MODEL_CHOICES, ManagedArtifact, ManagedArtifactState, ModelChoice,
@@ -6,8 +8,8 @@ use crate::model::{
use crate::settings::{GIB, REASONING_MODES};
use iced::theme::{Palette, palette};
use iced::widget::{
Button, Space, Svg, button, checkbox, column, container, horizontal_rule, opaque, pick_list,
progress_bar, row, scrollable, stack, svg, text, text_input,
Button, Space, Svg, button, checkbox, column, container, horizontal_rule, markdown, opaque,
pick_list, progress_bar, row, scrollable, stack, svg, text, text_input,
};
use iced::{Alignment, Background, Border, Color, Element, Length, Theme, window};
use std::path::Path;
@@ -234,6 +236,7 @@ impl App {
.spacing(8),
);
} else {
let markdown_style = markdown::Style::from_palette(app_theme().palette());
for (index, message) in self.conversation.iter().enumerate() {
let label = if message.user { "You" } else { "DS4" };
let active = self.generating && index + 1 == self.conversation.len();
@@ -267,20 +270,32 @@ impl App {
}
}
if !message.content.is_empty() {
let content = if message.reasoning.is_some() {
message.content.trim_start()
if message.user || message.markdown.is_empty() {
let content = if message.reasoning.is_some() {
message.content.trim_start()
} else {
&message.content
};
body = body.push(text(content).size(14));
} else {
&message.content
};
body = body.push(text(content).size(14));
body = body.push(
markdown::view(
&message.markdown,
markdown::Settings::with_text_size(14),
markdown_style,
)
.map(Message::OpenLink),
);
}
} else if active && message.reasoning.is_none() {
body = body.push(text("Loading model…").size(14));
}
let user = message.user;
messages = messages.push(
container(body)
.padding(14)
.width(Length::Fill)
.style(overview_style),
.style(move |theme| chat_message_style(theme, user)),
);
}
}
@@ -304,7 +319,9 @@ impl App {
self.context_used.min(self.context_limit) as f32 / self.context_limit as f32
};
let conversation = column![
scrollable(messages).height(Length::Fill),
scrollable(messages)
.id(chat_scroll_id())
.height(Length::Fill),
container(
column![
composer,
@@ -312,10 +329,14 @@ impl App {
row![
icon(ICON_PAPERCLIP, 19),
text(format!(
"{} / {} tokens ({:.0}%)",
"{} / {} tokens ({:.0}%){}",
self.context_used,
self.context_limit,
context_fraction * 100.0
context_fraction * 100.0,
self.tokens_per_second.map_or_else(
|| "— tok/s".to_owned(),
|speed| format!("{speed:.1} tok/s")
)
))
.size(11)
.color(muted_text()),
@@ -1180,6 +1201,21 @@ fn overview_style(_: &Theme) -> container::Style {
}
}
fn chat_message_style(theme: &Theme, user: bool) -> container::Style {
if !user {
return overview_style(theme);
}
container::Style {
background: Some(Background::Color(Color::from_rgb8(27, 34, 44))),
border: Border {
color: Color::from_rgb8(54, 68, 88),
width: 1.0,
radius: 14.0.into(),
},
..container::Style::default()
}
}
fn muted_text() -> Color {
Color::from_rgb8(174, 174, 178)
}
@@ -1207,6 +1243,10 @@ mod tests {
let palette = theme.extended_palette();
assert_eq!(palette.background.weak.color, Color::from_rgb8(38, 38, 40));
assert_eq!(palette.secondary.base.color, Color::from_rgb8(47, 47, 50));
assert_ne!(
chat_message_style(&theme, true).background,
chat_message_style(&theme, false).background
);
}
#[test]