From b5ae7c855020616330edb490cd4d3ef83379a2c7 Mon Sep 17 00:00:00 2001 From: Jarno Date: Mon, 2 Feb 2026 20:34:32 +0200 Subject: [PATCH] Prompt popup implementation --- frontend/src-tauri/src/main.rs | 24 ++++++----- frontend/src-tauri/tauri.conf.json | 2 +- frontend/src/app.rs | 23 +++------- frontend/src/bridge.rs | 6 +++ frontend/src/main.rs | 2 + frontend/src/popup.rs | 69 ++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 frontend/src/bridge.rs create mode 100644 frontend/src/popup.rs diff --git a/frontend/src-tauri/src/main.rs b/frontend/src-tauri/src/main.rs index c2d4d4d..60a6025 100644 --- a/frontend/src-tauri/src/main.rs +++ b/frontend/src-tauri/src/main.rs @@ -3,10 +3,10 @@ use tokio::sync::Mutex; -use tauri::{Manager, State, WindowEvent}; +use tauri::{Emitter, Manager, State, WindowEvent}; use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, Modifiers, Shortcut, ShortcutState}; -use shared::ai::{PromptRequest, ai_daemon_client::AiDaemonClient}; +use shared::ai::{ai_daemon_client::AiDaemonClient, PromptRequest}; struct AppState { grpc_client: Mutex>, @@ -22,6 +22,7 @@ fn toggle_popup(app_handle: tauri::AppHandle) { } else { window.show().unwrap(); window.set_focus().unwrap(); + let _ = window.emit("window-focused", ()); } } None => { @@ -32,7 +33,8 @@ fn toggle_popup(app_handle: tauri::AppHandle) { #[tauri::command] async fn prompt_llm(state: State<'_, AppState>, prompt: String) -> Result { - let mut client= state.grpc_client.lock().await; + println!(">>>> {}", prompt); + let mut client = state.grpc_client.lock().await; let request = tonic::Request::new(PromptRequest { prompt }); match client.prompt(request).await { Ok(response) => Ok(response.into_inner().response), @@ -42,7 +44,6 @@ async fn prompt_llm(state: State<'_, AppState>, prompt: String) -> Result JsValue; -} #[component] pub fn App() -> impl IntoView { @@ -33,7 +28,9 @@ fn Dashboard() -> impl IntoView { }; let prompt = |_ev: leptos::ev::MouseEvent| { spawn_local(async { - let prompt = serde_wasm_bindgen::to_value(&serde_json::json!({"prompt": "jee juu juu"})).unwrap(); + let prompt = + serde_wasm_bindgen::to_value(&serde_json::json!({"prompt": "jee juu juu"})) + .unwrap(); invoke("prompt_llm", prompt).await; }); }; @@ -45,13 +42,3 @@ fn Dashboard() -> impl IntoView { } } - -#[component] -fn Popup() -> impl IntoView { - view! { -
-

"AI quick action"

- -
- } -} diff --git a/frontend/src/bridge.rs b/frontend/src/bridge.rs new file mode 100644 index 0000000..fb0a08b --- /dev/null +++ b/frontend/src/bridge.rs @@ -0,0 +1,6 @@ +use wasm_bindgen::prelude::*; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])] + pub async fn invoke(cmd: &str, args: JsValue) -> JsValue; +} diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 4fcc0ff..4c167f4 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,4 +1,6 @@ mod app; +mod bridge; +mod popup; use app::*; use leptos::prelude::*; diff --git a/frontend/src/popup.rs b/frontend/src/popup.rs new file mode 100644 index 0000000..7ad1edd --- /dev/null +++ b/frontend/src/popup.rs @@ -0,0 +1,69 @@ +use crate::bridge::invoke; +use leptos::{html::Input, prelude::*}; +use wasm_bindgen::{prelude::Closure, JsCast}; + +#[component] +pub fn Popup() -> impl IntoView { + let prompt_input_ref = NodeRef::::new(); + let (prompt_text, set_prompt_text) = signal(String::new()); + let (prompt_result, set_prompt_result) = signal(String::new()); + + let prompt_action = Action::new_local(|prompt: &String| { + let prompt = prompt.clone(); + async move { + let response = invoke( + "prompt_llm", + serde_wasm_bindgen::to_value(&serde_json::json!({"prompt": prompt})).unwrap(), + ) + .await; + let result: String = serde_wasm_bindgen::from_value(response).unwrap(); + result + } + }); + + Effect::new(move |_| { + if let Some(result) = prompt_action.value().get() { + set_prompt_result.set(result) + } + }); + + Effect::new(move |_| { + let Some(input_el) = prompt_input_ref.get() else { + return; + }; + let handle_focus = Closure::::new(move || { + let _ = input_el.focus(); + set_prompt_text.update(|s| *s = "".to_string()); + }); + + window() + .add_event_listener_with_callback("focus", handle_focus.as_ref().unchecked_ref()) + .unwrap(); + + handle_focus.forget(); + }); + + view! { +
+

"AI quick action"

+ +
+

{move || prompt_result.get()}

+
+
+ } +}