diff --git a/app.vue b/app.vue index 41027d2..fa71e44 100644 --- a/app.vue +++ b/app.vue @@ -33,10 +33,15 @@ @click="selectItem(groupIndex, index)" :ref="el => { if (isSelected(groupIndex, index)) selectedElement = el as HTMLElement }"> - Favicon + Favicon @@ -49,8 +54,8 @@ Image - YouTube Thumbnail + YouTube Thumbnail {{ selectedItem?.content || '' }} @@ -95,6 +100,9 @@ const selectedItemIndex: Ref = ref(0); const selectedElement: Ref = ref(null); const searchInput: Ref = ref(null); const os: Ref = ref(''); +const imageLoadError = ref(false); +const imageLoading = ref(true); +const imageUrls: Ref> = shallowRef({}); const groupedHistory: ComputedRef = computed(() => { const now = new Date(); @@ -205,7 +213,7 @@ const selectItem = (groupIndex: number, itemIndex: number): void => { const pasteSelectedItem = async (): Promise => { if (!selectedItem.value) return; - + let content = selectedItem.value.content; let contentType: String = selectedItem.value.content_type; if (contentType === 'image') { @@ -217,9 +225,9 @@ const pasteSelectedItem = async (): Promise => { } } await hideApp(); - await invoke("write_and_paste", { - content, - contentType + await invoke("write_and_paste", { + content, + contentType }); }; @@ -255,52 +263,74 @@ const getFaviconFromDb = (favicon: string): string => { const getImageDimensions = (path: string): Promise => { return new Promise(async (resolve) => { const img = new Image(); - img.onload = () => resolve(`${img.width}x${img.height}`); - img.onerror = () => resolve('0x0'); - if (path.includes('AppData\\Roaming\\net.pandadev.qopy\\images\\')) { - const filename = path.split('\\').pop(); - try { - const imageData = await invoke("read_image", { filename: filename }); - const blob = new Blob([imageData], { type: 'image/png' }); - img.src = URL.createObjectURL(blob); - } catch (error) { - console.error('Error reading image file:', error); - resolve('0x0'); - } - } else { - img.src = `data:image/png;base64,${path}`; + img.onload = () => { + imageLoadError.value = false; + imageLoading.value = false; + resolve(`${img.width}x${img.height}`); + }; + img.onerror = (e) => { + console.error('Error loading image:', e); + imageLoadError.value = true; + imageLoading.value = false; + resolve('0x0'); + }; + + try { + imageLoading.value = true; + const dataUrl = await getImageUrl(path); + img.src = dataUrl; + } catch (error) { + console.error('Error getting image URL:', error); + imageLoadError.value = true; + imageLoading.value = false; + resolve('0x0'); } }); }; -const imageUrls: Ref> = shallowRef({}); +const getImageUrl = async (path: string): Promise => { + const isWindows = path.includes('\\'); + const separator = isWindows ? '\\' : '/'; + const filename = path.split(separator).pop(); + + try { + imageLoading.value = true; + const base64 = await invoke("read_image", { filename }); + console.log('Image data received, length:', base64.length); + if (!base64 || base64.length === 0) { + throw new Error('Received empty image data'); + } + + const dataUrl = `data:image/png;base64,${base64}`; + + console.log('Data URL preview:', dataUrl.substring(0, 50) + '...'); + + imageLoadError.value = false; + imageLoading.value = false; + return dataUrl; + } catch (error) { + console.error('Error reading image file:', error); + imageLoadError.value = true; + imageLoading.value = false; + return ''; + } +}; const getComputedImageUrl = (item: HistoryItem): string => { if (!imageUrls.value[item.id]) { imageUrls.value[item.id] = ''; - getImageUrl(item.content).then(url => { - imageUrls.value = { ...imageUrls.value, [item.id]: url }; - }); + getImageUrl(item.content) + .then(url => { + imageUrls.value = { ...imageUrls.value, [item.id]: url }; + }) + .catch(error => { + console.error('Failed to get image URL:', error); + imageUrls.value = { ...imageUrls.value, [item.id]: '' }; + }); } return imageUrls.value[item.id] || ''; }; -const getImageUrl = async (path: string): Promise => { - if (path.includes('AppData\\Roaming\\net.pandadev.qopy\\images\\')) { - const filename = path.split('\\').pop(); - try { - const imageData = await invoke("read_image", { filename: filename }); - const blob = new Blob([imageData], { type: 'image/png' }); - return URL.createObjectURL(blob); - } catch (error) { - console.error('Error reading image file:', error); - return ''; - } - } else { - return `data:image/png;base64,${path}`; - } -}; - const loadHistoryChunk = async (): Promise => { if (!db.value || isLoading) return; @@ -328,6 +358,7 @@ const loadHistoryChunk = async (): Promise => { const processedChunk = await Promise.all(results.map(async item => { if (item.content_type === 'image') { const dimensions = await getImageDimensions(item.content); + getComputedImageUrl(item); return { ...item, dimensions }; } return item; @@ -340,10 +371,10 @@ const loadHistoryChunk = async (): Promise => { const handleScroll = (): void => { if (!resultsContainer.value) return; - + const { viewport } = resultsContainer.value?.osInstance().elements() ?? {}; const { scrollTop = 0, scrollHeight = 0, clientHeight = 0 } = viewport ?? {}; - + if (scrollHeight - scrollTop - clientHeight < 100) { loadHistoryChunk(); } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 10e711b..883a577 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -28,6 +28,7 @@ "core:window:allow-show", "core:window:allow-set-focus", "core:window:allow-is-focused", - "core:window:allow-is-visible" + "core:window:allow-is-visible", + "fs:allow-read" ] } \ No newline at end of file diff --git a/src-tauri/src/api/clipboard.rs b/src-tauri/src/api/clipboard.rs index e382aa0..c587cb4 100644 --- a/src-tauri/src/api/clipboard.rs +++ b/src-tauri/src/api/clipboard.rs @@ -24,11 +24,12 @@ pub fn set_app_data_dir(path: std::path::PathBuf) { } #[tauri::command] -pub fn read_image(filename: String) -> Result, String> { +pub fn read_image(filename: String) -> Result { let app_data_dir = APP_DATA_DIR.lock().unwrap(); let app_data_dir = app_data_dir.as_ref().expect("App data directory not set"); let image_path = app_data_dir.join("images").join(filename); - fs::read(image_path).map_err(|e| e.to_string()) + let image_data = fs::read(image_path).map_err(|e| e.to_string())?; + Ok(STANDARD.encode(image_data)) } #[tauri::command] @@ -107,26 +108,22 @@ pub fn setup(app: &AppHandle) { let app = app.clone(); runtime.block_on(async move { if IS_PROGRAMMATIC_PASTE.load(Ordering::SeqCst) { - println!("Ignoring programmatic paste"); return; } let clipboard = app.state::(); let available_types = clipboard.available_types().unwrap(); - println!("Clipboard update detected"); - match get_pool(&app).await { Ok(pool) => { if available_types.image { println!("Handling image change"); if let Ok(image_data) = clipboard.read_image_base64() { - let base64_image = STANDARD.encode(&image_data); insert_content_if_not_exists( app.clone(), pool.clone(), "image", - base64_image, + image_data, ) .await; } diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index eaa23e6..9138326 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -3,6 +3,7 @@ use tauri::Manager; use crate::utils::commands::center_window_on_current_monitor; +#[warn(dead_code)] pub fn setup(app_handle: tauri::AppHandle) { std::thread::spawn(move || { let mut meta_pressed = false; diff --git a/src-tauri/src/api/tray.rs b/src-tauri/src/api/tray.rs index d332489..bf2af58 100644 --- a/src-tauri/src/api/tray.rs +++ b/src-tauri/src/api/tray.rs @@ -1,13 +1,12 @@ use tauri::{ menu::{MenuBuilder, MenuItemBuilder}, - tray::{MouseButton, TrayIconBuilder, TrayIconEvent}, + tray::TrayIconBuilder, Manager, }; pub fn setup(app: &mut tauri::App) -> Result<(), Box> { let window = app.get_webview_window("main").unwrap(); let window_clone_for_tray = window.clone(); - let window_clone_for_click = window.clone(); let icon_bytes = include_bytes!("../../icons/Square71x71Logo.png"); let icon = tauri::image::Image::from_bytes(icon_bytes).unwrap(); @@ -37,14 +36,6 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { } _ => (), }) - // .on_tray_icon_event(move |_tray, event| { - // if let TrayIconEvent::Click { button, .. } = event { - // if button == MouseButton::Left { - // window_clone_for_click.show().unwrap(); - // window_clone_for_click.set_focus().unwrap(); - // } - // } - // }) .icon(icon) .build(app)?;