show prompt responses in dialog

This commit is contained in:
2026-02-07 21:56:47 +02:00
parent 71d957882c
commit 16ab8e4207
4 changed files with 60 additions and 18 deletions

View File

@@ -58,6 +58,7 @@ async fn main() {
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.invoke_handler(tauri::generate_handler![toggle_popup, prompt_llm])
.setup(|app| {
/* Auto-hide popup when focus is lost
if let Some(window) = app.get_webview_window("popup") {
let w = window.clone();
window.on_window_event(move |event| {
@@ -68,6 +69,8 @@ async fn main() {
}
})
}
*/
// Global shortcut to pull up the popup
let shortcut = Shortcut::new(Some(Modifiers::META), Code::Space);
app.global_shortcut()
.on_shortcut(shortcut, move |app, _shortcut, event| {

View File

@@ -22,8 +22,8 @@
"label": "popup",
"title": "AI Quick Action",
"url": "/popup",
"width": 600,
"height": 260,
"width": 800,
"height": 400,
"decorations": false,
"transparent": true,
"alwaysOnTop": true,

View File

@@ -3,12 +3,19 @@ use leptos::{ev::keydown, html::Input, prelude::*};
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use wasm_bindgen_futures::spawn_local;
#[derive(Clone, Debug)]
struct Message {
id: usize,
text: String,
is_user: bool,
}
#[component]
pub fn Popup() -> impl IntoView {
// Prompt signals and and action
let prompt_input_ref = NodeRef::<Input>::new();
let (prompt_text, set_prompt_text) = signal(String::new());
let (prompt_result, set_prompt_result) = signal(String::new());
let (messages, set_messages) = signal(Vec::<Message>::new());
// Action that calls the promp daemon
let prompt_action = Action::new_local(|prompt: &String| {
let prompt = prompt.clone();
@@ -25,7 +32,13 @@ pub fn Popup() -> impl IntoView {
// Update the model response div with the prompt result
Effect::new(move |_| {
if let Some(result) = prompt_action.value().get() {
set_prompt_result.set(result)
set_messages.update(|previous| {
previous.push(Message {
id: previous.len(),
text: result,
is_user: false,
});
});
}
});
// Clear the propt text-input when the window loses focus (and is hidden)
@@ -64,15 +77,26 @@ pub fn Popup() -> impl IntoView {
on:input=move |ev| set_prompt_text.set(event_target_value(&ev))
on:keydown=move |ev| {
if ev.key() == "Enter" {
set_messages.update(|previous| {
previous.push(Message {
id: previous.len(),
text: prompt_text.get(),
is_user: true,
});
});
prompt_action.dispatch(prompt_text.get());
//set_prompt_result.update(|s| *s = prompt_action.value());
set_prompt_text.update(|s| *s = "".to_string());
}
}
prop:value=prompt_text
/>
<div class="response-area">
<p>{move || prompt_result.get()}</p>
<For each=move || messages.get()
key=|msg| msg.id
let(msg)
>
<div class=if msg.is_user {"msg msg-user"} else {"msg msg-model"}>{msg.text}</div>
</For>
</div>
</main>
}

View File

@@ -1,23 +1,38 @@
body {
background-color: transparent !important;
margin: 0;
color: #f0f0f0;
background-color: transparent !important;
margin: 0;
color: #f0f0f0;
}
.window-shell {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 15px;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 15px;
}
.opaque-bg {
background-color: #212121;
background-color: #212121;
}
.rounded-container {
background-color: #212121;
border-radius: 15px;
background-color: #212121;
border-radius: 15px;
}
.response-area {
width: 100%;
height: 300px;
overflow-y: scroll;
}
.msg {
margin: 1px;
border: solid 1px #808080;
}
.msg-user {
text-align: end;
}