Files
hcie-rust-v3.05/hcie-iced-app/crates/hcie-iced-gui/src/panels/ai_script_panel.rs
T

147 lines
5.9 KiB
Rust
Raw Normal View History

//! AI Script panel — AI-powered procedural drawing script generation.
//!
//! Uses an LLM to generate HCIE-Script DSL commands from natural language
//! descriptions. Supports Ollama, Claude, and OpenAI providers. The generated
//! script can be executed directly or copied to the script editor panel.
use crate::app::{HcieIcedApp, Message};
use iced::widget::{button, column, container, horizontal_rule, row, scrollable, text, text_input};
use iced::{Element, Length};
/// Build the AI script panel view from application state.
///
/// Displays provider selection, URL/model inputs, a prompt text area,
/// action buttons (Generate, Clear, Run, Copy), and the generated script
/// output with error display.
pub fn view(app: &HcieIcedApp) -> Element<'static, Message> {
let colors = app.theme_state.colors();
// ── Provider selector row ────────────────────────────
let provider_label = text("Provider").size(10);
let ollama_btn = button(text("Ollama").size(10))
.on_press(Message::AiScriptProviderChanged("ollama".to_string()))
.padding([4, 8]);
let claude_btn = button(text("Claude").size(10))
.on_press(Message::AiScriptProviderChanged("claude".to_string()))
.padding([4, 8]);
let openai_btn = button(text("OpenAI").size(10))
.on_press(Message::AiScriptProviderChanged("openai".to_string()))
.padding([4, 8]);
let provider_selector = row![ollama_btn, claude_btn, openai_btn].spacing(4);
// ── URL and model inputs ─────────────────────────────
let url_input = text_input("Base URL", &app.ai_script_url)
.width(Length::Fill)
.on_input(Message::AiScriptUrlChanged);
let model_input = text_input("Model", &app.ai_script_model)
.width(Length::Fill)
.on_input(Message::AiScriptModelChanged);
// ── Prompt input ─────────────────────────────────────
let prompt_input = text_input("Describe what to draw...", &app.ai_script_prompt)
.width(Length::Fill)
.on_input(Message::AiScriptPromptChanged);
// ── Action buttons ───────────────────────────────────
let generate_btn = if app.ai_script_is_running {
button(text("Generating...").size(11)).padding([6, 12])
} else {
button(text("Generate Script").size(11))
.on_press(Message::AiScriptGenerate)
.padding([6, 12])
};
let run_btn = if app.ai_script_output.is_empty() || app.ai_script_is_running {
button(text("Run Script").size(11)).padding([6, 12])
} else {
button(text("Run Script").size(11))
.on_press(Message::AiScriptRun)
.padding([6, 12])
};
let copy_btn = if app.ai_script_output.is_empty() {
button(text("To Editor").size(11)).padding([6, 12])
} else {
button(text("To Editor").size(11))
.on_press(Message::AiScriptCopy)
.padding([6, 12])
};
let copy_output_btn = if app.ai_script_output.is_empty() {
button(text("Copy Output").size(11)).padding([6, 12])
} else {
button(text("Copy Output").size(11))
.on_press(Message::AiScriptCopyOutput)
.padding([6, 12])
};
let clear_btn = button(text("Clear").size(11))
.on_press(Message::AiScriptClear)
.padding([6, 12]);
// ── Script output area ───────────────────────────────
let script_content = if app.ai_script_output.is_empty() {
text("Generated script will appear here...").size(10)
} else {
text(app.ai_script_output.clone()).size(10)
};
let script_area = container(scrollable(script_content).height(Length::Fill))
.padding(8)
.style(move |_theme| iced::widget::container::Style {
background: Some(iced::Background::Color(colors.bg_app)),
border: iced::Border::default().rounded(4).color(colors.border_high),
..Default::default()
});
// ── Error display ────────────────────────────────────
let error_section: Element<'static, Message> = if let Some(ref err) = app.ai_script_error {
text(format!("Error: {}", err))
.size(10)
.color(colors.danger)
.into()
} else {
app.ai_script_notice.as_ref().map_or_else(
|| column![].into(),
|notice| {
text(notice.clone())
.size(10)
.color(colors.accent_green)
.into()
},
)
};
// ── Assemble panel ───────────────────────────────────
let panel = column![
text("AI Script Generator").size(13),
horizontal_rule(1),
provider_label,
provider_selector,
url_input,
model_input,
text("Model discovery is unavailable; enter the exact provider model ID.")
.size(9)
.color(colors.text_disabled),
horizontal_rule(1),
prompt_input,
row![generate_btn, clear_btn].spacing(4),
horizontal_rule(1),
script_area,
error_section,
horizontal_rule(1),
row![run_btn, copy_btn, copy_output_btn].spacing(4),
]
.spacing(4)
.padding(8);
container(panel)
.width(Length::Fill)
.height(Length::Fill)
.style(move |_theme| iced::widget::container::Style {
background: Some(iced::Background::Color(colors.bg_panel)),
border: iced::Border::default().color(colors.border_high).width(1),
..Default::default()
})
.into()
}