2026-07-11 18:13:03 +03:00
|
|
|
//! AI Chat panel — AI assistant for image editing.
|
|
|
|
|
//! Uses ThemeColors for consistent styling.
|
|
|
|
|
|
|
|
|
|
use crate::app::Message;
|
|
|
|
|
use crate::panels::styles;
|
|
|
|
|
use crate::theme::ThemeColors;
|
|
|
|
|
use iced::widget::{button, column, container, horizontal_rule, row, scrollable, text, text_input};
|
|
|
|
|
use iced::{Element, Length};
|
|
|
|
|
|
|
|
|
|
/// Build the AI chat panel.
|
|
|
|
|
pub fn view(input: &str, messages: &[(String, String)], colors: ThemeColors) -> Element<'static, Message> {
|
|
|
|
|
let provider_row = row![
|
|
|
|
|
text("Provider:").size(10),
|
|
|
|
|
button(text("Ollama").size(10)).on_press(Message::NoOp).padding([4, 8]),
|
|
|
|
|
button(text("Claude").size(10)).on_press(Message::NoOp).padding([4, 8]),
|
|
|
|
|
button(text("OpenAI").size(10)).on_press(Message::NoOp).padding([4, 8]),
|
|
|
|
|
]
|
|
|
|
|
.spacing(4);
|
|
|
|
|
|
|
|
|
|
let mut msg_list = column![].spacing(4);
|
|
|
|
|
|
|
|
|
|
if messages.is_empty() {
|
|
|
|
|
let welcome = container(
|
|
|
|
|
column![
|
2026-07-14 14:14:10 +03:00
|
|
|
text("AI Assistant").size(12),
|
2026-07-11 18:13:03 +03:00
|
|
|
text("Ask me anything about image editing!").size(11),
|
|
|
|
|
text("").size(8),
|
|
|
|
|
text("I can help with:").size(10),
|
|
|
|
|
text(" - Creating and editing layers").size(10),
|
|
|
|
|
text(" - Applying filters and effects").size(10),
|
|
|
|
|
text(" - Drawing shapes and text").size(10),
|
|
|
|
|
text(" - Color adjustments").size(10),
|
|
|
|
|
]
|
|
|
|
|
.spacing(2)
|
|
|
|
|
)
|
|
|
|
|
.padding(12)
|
|
|
|
|
.style(move |_theme| iced::widget::container::Style {
|
|
|
|
|
background: Some(iced::Background::Color(colors.bg_hover)),
|
|
|
|
|
border: iced::Border::default().rounded(8),
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
msg_list = msg_list.push(welcome);
|
|
|
|
|
} else {
|
|
|
|
|
for (role, content) in messages {
|
|
|
|
|
let (label, color) = if role == "user" {
|
|
|
|
|
("You", colors.accent)
|
|
|
|
|
} else {
|
|
|
|
|
("AI", iced::Color::from_rgb(0.4, 0.9, 0.4))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let content_owned = content.clone();
|
|
|
|
|
let msg_text = text(content_owned).size(11);
|
|
|
|
|
let msg_text = msg_text.style(move |_theme: &iced::Theme| iced::widget::text::Style {
|
|
|
|
|
color: Some(color),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let bubble = container(
|
2026-07-14 14:14:10 +03:00
|
|
|
column![text(label).size(10), msg_text].spacing(2)
|
2026-07-11 18:13:03 +03:00
|
|
|
)
|
|
|
|
|
.width(Length::Fill)
|
|
|
|
|
.padding(8)
|
|
|
|
|
.style(move |_theme| iced::widget::container::Style {
|
|
|
|
|
background: Some(iced::Background::Color(colors.bg_hover)),
|
|
|
|
|
border: iced::Border::default().rounded(6),
|
|
|
|
|
..Default::default()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
msg_list = msg_list.push(bubble);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let input_owned = input.to_string();
|
|
|
|
|
let input_field = text_input("Ask the AI assistant...", &input_owned)
|
|
|
|
|
.on_input(|s| Message::AiChatInput(s))
|
|
|
|
|
.width(Length::Fill);
|
|
|
|
|
|
|
|
|
|
let send_btn = button(text("Send").size(11)).on_press(Message::AiChatSend).padding([6, 12]);
|
|
|
|
|
let clear_btn = button(text("Clear").size(11)).on_press(Message::AiChatClear).padding([6, 12]);
|
|
|
|
|
let input_row = row![input_field, send_btn, clear_btn].spacing(4);
|
|
|
|
|
|
|
|
|
|
let panel = column![
|
2026-07-14 14:14:10 +03:00
|
|
|
text("AI Assistant").size(13),
|
2026-07-11 18:13:03 +03:00
|
|
|
provider_row,
|
|
|
|
|
horizontal_rule(1),
|
|
|
|
|
scrollable(msg_list).height(Length::Fill),
|
|
|
|
|
horizontal_rule(1),
|
|
|
|
|
input_row,
|
|
|
|
|
]
|
|
|
|
|
.spacing(4)
|
|
|
|
|
.padding(8);
|
|
|
|
|
|
|
|
|
|
container(panel)
|
|
|
|
|
.width(Length::Fill)
|
|
|
|
|
.height(Length::Fill)
|
|
|
|
|
.style(move |_theme| styles::panel_background(colors))
|
|
|
|
|
.into()
|
|
|
|
|
}
|