From 2b022c31cb086f898691f1ab9747bd3cb2d72a9a Mon Sep 17 00:00:00 2001 From: Dmytro Kondakov Date: Tue, 22 Apr 2025 20:56:08 +0200 Subject: [PATCH] feat(image-viewer): enhance window focus and visibility handling in ImageViewer component --- .DS_Store | Bin 0 -> 6148 bytes .cursor/rules/frontend-rules.mdc | 38 +++++++++++ frontend/bun.lockb | Bin 164224 -> 164224 bytes frontend/src-tauri/capabilities/default.json | 2 + frontend/src-tauri/src/shortcut.rs | 68 ++++++++----------- frontend/src/components/ImageViewer.tsx | 15 +++- 6 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 .DS_Store create mode 100644 .cursor/rules/frontend-rules.mdc diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..2a559d693a35f56dbe5d52e55f9c002ced87b135 GIT binary patch literal 6148 zcmeHKyG{c^3>-s>lW0;>?l0sIR#EtZ{6LBT4Z28CpuURl;?pvIC?dMhAkm<)WY4bG zv!|QldkDIf);fE17dQs7q#c<-eR4~dFWKnh5Kivs?AXmrP3I3>oXgCRx$;)3Zg zu49%UHct?H;grY>&5}w?s?~^LNoT%QT`!yxlMbul!|KUa6N<&td4G#?SWi@x0#e{w zf&1Jpz5hSaf0+NTN!m#PDe$ipu*GJ(S@V^ux6WS9du^ld=w9(T=(C fcKjGcS=W5c^IkY52A%n!6ZJFTy2zx!Un_71&fOKH literal 0 HcmV?d00001 diff --git a/.cursor/rules/frontend-rules.mdc b/.cursor/rules/frontend-rules.mdc new file mode 100644 index 0000000..74dba6a --- /dev/null +++ b/.cursor/rules/frontend-rules.mdc @@ -0,0 +1,38 @@ +--- +description: +globs: +alwaysApply: true +--- +You are an expert AI programming assistant focused on producing clean, readable TypeScript and Rust code for modern cross-platform desktop apps. + +Use these rules for any code under /frontend folder. + +You always use the latest versions of Tauri, Rust, SolidJS, and you're fluent in their latest features, best practices, and patterns. + +You give accurate, thoughtful answers and think like a real dev—step-by-step. + +Follow the user’s specs exactly. If a specs folder exists, check it before coding. + +Begin with a detailed pseudo-code plan and confirm it with the user before writing actual code. + +Write correct, complete, idiomatic, secure, performant, and bug-free code. + +Prioritize readability unless performance is explicitly required. + +Fully implement all requested features—no TODOs, stubs, or placeholders. + +Use TypeScript's type system thoroughly for clarity and safety. + +Style with TailwindCSS using utility-first principles. + +Use Kobalte components effectively, building with Solid’s reactive model in mind. + +Offload performance-heavy logic to Rust and ensure smooth integration with Tauri. + +Guarantee tight coordination between SolidJS, Tauri, and Rust for a polished desktop UX. + +When needed, provide bash scripts to generate config files or folder structures. + +Be concise—cut the fluff. + +If there's no solid answer, say so. If you're unsure, don't guess—own it. \ No newline at end of file diff --git a/frontend/bun.lockb b/frontend/bun.lockb index cc853681272c8d1a330e034733613b373cf4cfce..85f294736a9c124c5e8388508bb92cca2c593fb6 100755 GIT binary patch delta 25 fcmZo@=4t? str, unexpected_type => panic!( @@ -38,39 +37,36 @@ pub fn enable_shortcut(app: &App) { unexpected_type ), }; - let stored_shortcut = stored_shortcut_str + stored_shortcut_str .parse::() - .expect("Stored shortcut string should be valid"); - _register_shortcut_upon_start(app, stored_shortcut); // Register stored shortcut + .expect("Stored shortcut string should be valid") } else { - // Use default shortcut if none is stored store.set( HAYSTACK_GLOBAL_SHORTCUT, JsonValue::String(DEFAULT_SHORTCUT.to_string()), ); - let default_shortcut = DEFAULT_SHORTCUT + DEFAULT_SHORTCUT .parse::() - .expect("Default shortcut should be valid"); - _register_shortcut_upon_start(app, default_shortcut); // Register default shortcut - } + .expect("Default shortcut should be valid") + }; + + register_shortcut_upon_start(app, shortcut); } /// Get the current stored shortcut as a string #[tauri::command] pub fn get_current_shortcut(app: AppHandle) -> Result { - let shortcut = _get_shortcut(&app); - Ok(shortcut) + Ok(get_shortcut_from_store(&app)) } /// Unregister the current shortcut in Tauri #[tauri::command] pub fn unregister_shortcut(app: AppHandle) { - let shortcut_str = _get_shortcut(&app); + let shortcut_str = get_shortcut_from_store(&app); let shortcut = shortcut_str .parse::() .expect("Stored shortcut string should be valid"); - // Unregister the shortcut app.global_shortcut() .unregister(shortcut) .expect("Failed to unregister shortcut") @@ -96,28 +92,31 @@ pub fn change_shortcut( store.set(HAYSTACK_GLOBAL_SHORTCUT, JsonValue::String(key)); // Register the new shortcut - _register_shortcut(&app, shortcut); + register_shortcut(&app, shortcut); Ok(()) } +/// Helper function to handle window visibility toggle +fn handle_window_visibility(app: &AppHandle, window: &tauri::WebviewWindow) { + if window.is_visible().unwrap() { + window.hide().unwrap(); + } else { + window.show().unwrap(); + window.set_focus().unwrap(); + app.emit("focus-search", ()).unwrap(); + } +} + /// Helper function to register a shortcut, primarily for updating shortcuts -fn _register_shortcut(app: &AppHandle, shortcut: Shortcut) { +fn register_shortcut(app: &AppHandle, shortcut: Shortcut) { let main_window = app.get_webview_window("main").unwrap(); - // Register global shortcut and define its behavior + app.global_shortcut() .on_shortcut(shortcut, move |app, scut, event| { if scut == &shortcut { if let ShortcutState::Pressed = event.state() { - // Toggle window visibility - if main_window.is_visible().unwrap() { - main_window.hide().unwrap(); // Hide window - } else { - main_window.show().unwrap(); // Show window - main_window.set_focus().unwrap(); // Focus window - // Emit focus-search event - app.emit("focus-search", ()).unwrap(); - } + handle_window_visibility(app, &main_window); } } }) @@ -126,38 +125,29 @@ fn _register_shortcut(app: &AppHandle, shortcut: Shortcut) { } /// Helper function to register shortcuts during application startup -fn _register_shortcut_upon_start(app: &App, shortcut: Shortcut) { +fn register_shortcut_upon_start(app: &App, shortcut: Shortcut) { let window = app .get_webview_window("main") .expect("webview to be defined"); - // Initialize global shortcut and set its handler app.handle() .plugin( tauri_plugin_global_shortcut::Builder::new() .with_handler(move |app, scut, event| { if scut == &shortcut { if let ShortcutState::Pressed = event.state() { - // Toggle window visibility - if window.is_visible().unwrap() { - window.hide().unwrap(); // Hide window - } else { - window.show().unwrap(); // Show window - window.set_focus().unwrap(); // Focus window - // Emit focus-search event - app.emit("focus-search", ()).unwrap(); - } + handle_window_visibility(app, &window); } } }) .build(), ) .unwrap(); - app.global_shortcut().register(shortcut).unwrap(); // Register global shortcut + app.global_shortcut().register(shortcut).unwrap(); } /// Retrieve the stored global shortcut as a string -pub fn _get_shortcut(app: &AppHandle) -> String { +fn get_shortcut_from_store(app: &AppHandle) -> String { let store = app .get_store(HAYSTACK_TAURI_STORE) .expect("Store should already be loaded or created"); diff --git a/frontend/src/components/ImageViewer.tsx b/frontend/src/components/ImageViewer.tsx index 8d8dbfc..65e491d 100644 --- a/frontend/src/components/ImageViewer.tsx +++ b/frontend/src/components/ImageViewer.tsx @@ -1,7 +1,13 @@ import { listen } from "@tauri-apps/api/event"; -import { createEffect } from "solid-js"; + +import { getCurrentWindow } from "@tauri-apps/api/window"; +import { createEffect, createSignal } from "solid-js"; import { sendImage } from "../network"; +// TODO: This component should focus the window and show preview of screenshot, +// before we send it to backend, potentially we could draw and annotate +// OR we kill this and do stuff siltently +// anyhow keeping it like this for now export function ImageViewer() { // const [latestImage, setLatestImage] = createSignal(null); @@ -11,10 +17,15 @@ export function ImageViewer() { console.log("Received processed PNG", event); const base64Data = event.payload as string; + const appWindow = getCurrentWindow(); + + appWindow.show(); + appWindow.setFocus(); + // setLatestImage(`data:image/png;base64,${base64Data}`); + const result = await sendImage("test-image.png", base64Data); - window.location.reload(); console.log("DBG: ", result); });