Add Iced panel adapter and AI chat/script panels

- Implemented a theme system in `iced-panel-adapter` for consistent styling across panels.
- Created `iced-panel-ai-chat` for an AI assistant interface, including message handling and UI components.
- Developed `iced-panel-script` for a DSL editor with execution capabilities, allowing procedural drawing commands.
- Introduced message types for interaction in both chat and script panels.
- Added color and theme management for improved visual consistency.
This commit is contained in:
2026-07-11 18:13:03 +03:00
parent 0d1b0eae4c
commit f4ad22b55b
113 changed files with 8604 additions and 141 deletions
@@ -0,0 +1,98 @@
//! 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![
text("AI Assistant").size(12).font(iced::Font::MONOSPACE),
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(
column![text(label).size(10).font(iced::Font::MONOSPACE), msg_text].spacing(2)
)
.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![
text("AI Assistant").size(13).font(iced::Font::MONOSPACE),
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()
}