feat(iced): implement AI script generator with working LLM calls
This commit is contained in:
@@ -163,6 +163,20 @@ pub struct HcieIcedApp {
|
||||
pub script_error_line: Option<usize>,
|
||||
/// Whether a script is currently executing.
|
||||
pub script_is_running: bool,
|
||||
/// AI script generator — natural language prompt input.
|
||||
pub ai_script_prompt: String,
|
||||
/// AI script generator — generated DSL script output.
|
||||
pub ai_script_output: String,
|
||||
/// AI script generator — last error message (if any).
|
||||
pub ai_script_error: Option<String>,
|
||||
/// AI script generator — whether a generation request is in progress.
|
||||
pub ai_script_is_running: bool,
|
||||
/// AI script generator — selected provider type ("ollama", "claude", "openai").
|
||||
pub ai_script_provider: String,
|
||||
/// AI script generator — model name for the selected provider.
|
||||
pub ai_script_model: String,
|
||||
/// AI script generator — base URL for the selected provider.
|
||||
pub ai_script_url: String,
|
||||
}
|
||||
|
||||
/// A recently opened file entry.
|
||||
@@ -438,6 +452,17 @@ pub enum Message {
|
||||
AiChatStreamChunk(String),
|
||||
AiChatStreamFinished(Result<String, String>),
|
||||
|
||||
// ── AI Script Generator ─────────────────────────────
|
||||
AiScriptPromptChanged(String),
|
||||
AiScriptProviderChanged(String),
|
||||
AiScriptModelChanged(String),
|
||||
AiScriptUrlChanged(String),
|
||||
AiScriptGenerate,
|
||||
AiScriptRun,
|
||||
AiScriptCopy,
|
||||
AiScriptClear,
|
||||
AiScriptResult(Result<String, String>),
|
||||
|
||||
// ── Clipboard ───────────────────────────────────────
|
||||
CopyImage,
|
||||
PasteImage,
|
||||
@@ -642,6 +667,13 @@ impl HcieIcedApp {
|
||||
script_error: None,
|
||||
script_error_line: None,
|
||||
script_is_running: false,
|
||||
ai_script_prompt: String::new(),
|
||||
ai_script_output: String::new(),
|
||||
ai_script_error: None,
|
||||
ai_script_is_running: false,
|
||||
ai_script_provider: "ollama".to_string(),
|
||||
ai_script_model: "qwen2.5:7b".to_string(),
|
||||
ai_script_url: "http://localhost:11434".to_string(),
|
||||
};
|
||||
|
||||
// Apply saved settings to tool state
|
||||
@@ -2452,6 +2484,104 @@ impl HcieIcedApp {
|
||||
}
|
||||
}
|
||||
|
||||
// ── AI Script Generator ───────────────────────
|
||||
Message::AiScriptPromptChanged(text) => {
|
||||
self.ai_script_prompt = text;
|
||||
}
|
||||
Message::AiScriptProviderChanged(provider) => {
|
||||
self.ai_script_provider = provider.clone();
|
||||
// Set sensible defaults when switching providers
|
||||
match provider.as_str() {
|
||||
"ollama" => {
|
||||
self.ai_script_url = "http://localhost:11434".to_string();
|
||||
self.ai_script_model = "qwen2.5:7b".to_string();
|
||||
}
|
||||
"claude" => {
|
||||
self.ai_script_url = "https://api.anthropic.com".to_string();
|
||||
self.ai_script_model = "claude-sonnet-4-20250514".to_string();
|
||||
}
|
||||
"openai" => {
|
||||
self.ai_script_url = "https://api.openai.com/v1".to_string();
|
||||
self.ai_script_model = "gpt-4o".to_string();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Message::AiScriptModelChanged(model) => {
|
||||
self.ai_script_model = model;
|
||||
}
|
||||
Message::AiScriptUrlChanged(url) => {
|
||||
self.ai_script_url = url;
|
||||
}
|
||||
Message::AiScriptGenerate => {
|
||||
if self.ai_script_prompt.trim().is_empty() || self.ai_script_is_running {
|
||||
return Task::none();
|
||||
}
|
||||
self.ai_script_is_running = true;
|
||||
self.ai_script_error = None;
|
||||
self.ai_script_output.clear();
|
||||
|
||||
let prompt = self.ai_script_prompt.clone();
|
||||
let provider_type = self.ai_script_provider.clone();
|
||||
let provider = crate::ai_script::Provider {
|
||||
base_url: self.ai_script_url.clone(),
|
||||
model: self.ai_script_model.clone(),
|
||||
};
|
||||
|
||||
return Task::perform(
|
||||
async move {
|
||||
crate::ai_script::generate_script(&provider_type, &provider, &prompt).await
|
||||
},
|
||||
Message::AiScriptResult,
|
||||
);
|
||||
}
|
||||
Message::AiScriptRun => {
|
||||
if self.ai_script_output.is_empty() {
|
||||
return Task::none();
|
||||
}
|
||||
match crate::script::parser::parse_dsl_script(&self.ai_script_output) {
|
||||
Ok(actions) => {
|
||||
self.ai_script_error = None;
|
||||
crate::script::executor::execute_actions(
|
||||
&mut self.documents[self.active_doc].engine,
|
||||
&actions,
|
||||
);
|
||||
self.documents[self.active_doc].modified = true;
|
||||
self.refresh_composite_if_needed();
|
||||
return Task::perform(async {}, |_| Message::CompositeRefresh);
|
||||
}
|
||||
Err(e) => {
|
||||
self.ai_script_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::AiScriptCopy => {
|
||||
if !self.ai_script_output.is_empty() {
|
||||
// Copy script to the script editor panel
|
||||
self.script_text = self.ai_script_output.clone();
|
||||
self.script_content = iced::widget::text_editor::Content::with_text(
|
||||
&self.ai_script_output,
|
||||
);
|
||||
}
|
||||
}
|
||||
Message::AiScriptClear => {
|
||||
self.ai_script_prompt.clear();
|
||||
self.ai_script_output.clear();
|
||||
self.ai_script_error = None;
|
||||
self.ai_script_is_running = false;
|
||||
}
|
||||
Message::AiScriptResult(result) => {
|
||||
self.ai_script_is_running = false;
|
||||
match result {
|
||||
Ok(script) => {
|
||||
self.ai_script_output = script;
|
||||
}
|
||||
Err(e) => {
|
||||
self.ai_script_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Clipboard ─────────────────────────────────
|
||||
Message::CopyImage => {
|
||||
let doc = &self.documents[self.active_doc];
|
||||
@@ -3064,6 +3194,7 @@ impl HcieIcedApp {
|
||||
(7, 7) => self.dock.reopen_pane(PaneType::Geometry),
|
||||
(7, 8) => self.dock.reopen_pane(PaneType::Script),
|
||||
(7, 9) => self.dock.reopen_pane(PaneType::LayerDetails),
|
||||
(7, 10) => self.dock.reopen_pane(PaneType::AiScript),
|
||||
|
||||
// ══════════════════════════════════════════
|
||||
// More menu (8) — Photopea-style
|
||||
|
||||
Reference in New Issue
Block a user