diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..2a559d6 Binary files /dev/null and b/.DS_Store differ 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 cc85368..85f2947 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/src-tauri/capabilities/default.json b/frontend/src-tauri/capabilities/default.json index 6c95be9..d8c4686 100644 --- a/frontend/src-tauri/capabilities/default.json +++ b/frontend/src-tauri/capabilities/default.json @@ -13,6 +13,8 @@ "global-shortcut:allow-unregister", "global-shortcut:allow-unregister-all", "http:default", + "core:window:allow-show", + "core:window:allow-set-focus", { "identifier": "http:default", "allow": [ diff --git a/frontend/src-tauri/src/shortcut.rs b/frontend/src-tauri/src/shortcut.rs index b853778..003d105 100644 --- a/frontend/src-tauri/src/shortcut.rs +++ b/frontend/src-tauri/src/shortcut.rs @@ -29,8 +29,7 @@ pub fn enable_shortcut(app: &App) { .store(HAYSTACK_TAURI_STORE) .expect("Creating the store should not fail"); - // Use stored shortcut if it exists - if let Some(stored_shortcut) = store.get(HAYSTACK_GLOBAL_SHORTCUT) { + let shortcut = if let Some(stored_shortcut) = store.get(HAYSTACK_GLOBAL_SHORTCUT) { let stored_shortcut_str = match stored_shortcut { JsonValue::String(str) => 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); });