diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7ebf570..03c02e2 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -491,7 +491,7 @@ fn clipboard_read_text() -> Result { } #[tauri::command] -fn clipboard_read_image() -> Result { +fn clipboard_read_image(macos_image_scaling: bool) -> Result { let mut clipboard = arboard::Clipboard::new().map_err(|e| e.to_string())?; let image = clipboard.get_image().map_err(|e| e.to_string())?; @@ -500,14 +500,73 @@ fn clipboard_read_image() -> Result { { let encoder = image::codecs::png::PngEncoder::new(&mut png_data); use image::ImageEncoder; - encoder - .write_image( - image.bytes.as_ref(), - image.width as u32, - image.height as u32, - image::ExtendedColorType::Rgba8, - ) - .map_err(|e| e.to_string())?; + + // Check if running on macOS and scale image if needed + #[cfg(target_os = "macos")] + { + if macos_image_scaling { + // Use image crate for high-quality scaling + use image::{DynamicImage, ImageBuffer, Rgba}; + + // Convert arboard Image to ImageBuffer + let mut img_buffer = ImageBuffer::new(image.width as u32, image.height as u32); + for (x, y, pixel) in img_buffer.enumerate_pixels_mut() { + let idx = (y * image.width as u32 + x) as usize * 4; + if idx + 3 < image.bytes.len() { + *pixel = Rgba([ + image.bytes[idx], + image.bytes[idx + 1], + image.bytes[idx + 2], + image.bytes[idx + 3] + ]); + } + } + + // Create DynamicImage + let dynamic_image = DynamicImage::ImageRgba8(img_buffer); + + // Resize with high-quality Lanczos3 filter + let resized = dynamic_image.resize( + (image.width / 2) as u32, + (image.height / 2) as u32, + image::imageops::FilterType::Lanczos3 + ); + + // Write the resized image + let resized_rgba = resized.to_rgba8(); + encoder + .write_image( + resized_rgba.as_raw(), + (image.width / 2) as u32, + (image.height / 2) as u32, + image::ExtendedColorType::Rgba8, + ) + .map_err(|e| e.to_string())?; + } else { + // Use original image if scaling is disabled + encoder + .write_image( + image.bytes.as_ref(), + image.width as u32, + image.height as u32, + image::ExtendedColorType::Rgba8, + ) + .map_err(|e| e.to_string())?; + } + } + + #[cfg(not(target_os = "macos"))] + { + // For other platforms, use the original image + encoder + .write_image( + image.bytes.as_ref(), + image.width as u32, + image.height as u32, + image::ExtendedColorType::Rgba8, + ) + .map_err(|e| e.to_string())?; + } } use base64::{engine::general_purpose, Engine as _}; @@ -515,8 +574,8 @@ fn clipboard_read_image() -> Result { } #[tauri::command] -fn save_image(parent_dir: String, filename: String, base64_data: String) -> Result { - let img_dir = Path::new(&parent_dir).join("img"); +fn save_image(parent_dir: String, filename: String, base64_data: String, image_directory: String) -> Result { + let img_dir = Path::new(&parent_dir).join(&image_directory); if !img_dir.exists() { fs::create_dir_all(&img_dir).map_err(|e| e.to_string())?; } @@ -537,12 +596,12 @@ fn save_image(parent_dir: String, filename: String, base64_data: String) -> Resu fs::write(&file_path, bytes).map_err(|e| e.to_string())?; - Ok(format!("img/{}", filename)) + Ok(format!("{}/{}", image_directory, filename)) } #[tauri::command] -fn copy_file_to_img(src_path: String, parent_dir: String) -> Result { - let img_dir = Path::new(&parent_dir).join("img"); +fn copy_file_to_img(src_path: String, parent_dir: String, image_directory: String) -> Result { + let img_dir = Path::new(&parent_dir).join(&image_directory); if !img_dir.exists() { fs::create_dir_all(&img_dir).map_err(|e| e.to_string())?; } @@ -569,7 +628,7 @@ fn copy_file_to_img(src_path: String, parent_dir: String) -> Result Result<(), String> { } #[tauri::command] -fn cleanup_empty_img_dir(parent_dir: String) -> Result<(), String> { - let img_dir = Path::new(&parent_dir).join("img"); +fn cleanup_empty_img_dir(parent_dir: String, image_directory: String) -> Result<(), String> { + let img_dir = Path::new(&parent_dir).join(&image_directory); if img_dir.exists() && img_dir.is_dir() { if fs::read_dir(&img_dir) .map_err(|e| e.to_string())? diff --git a/src/lib/MarkdownViewer.svelte b/src/lib/MarkdownViewer.svelte index 202b3c0..e621aad 100644 --- a/src/lib/MarkdownViewer.svelte +++ b/src/lib/MarkdownViewer.svelte @@ -1610,6 +1610,10 @@ import { t } from './utils/i18n.js'; e.preventDefault(); zoomLevel = 100; } + if (cmdOrCtrl && key === ',') { + e.preventDefault(); + showSettings = !showSettings; + } } function pushScrollHistory() { diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index e4f7b35..3eee1df 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -604,12 +604,12 @@ const last = managedImages[managedImages.length - 1]; if (!currentContent.includes(last.embed)) { managedImages.pop(); - const imgPath = `${last.parentDir}\\img\\${last.filename}`; - invoke("delete_file", { path: imgPath }) - .then(() => { - invoke("cleanup_empty_img_dir", { parentDir: last.parentDir }); - }) - .catch(console.error); + const imgPath = `${last.parentDir}/${settings.imageDirectory}/${last.filename}`; + invoke("delete_file", { path: imgPath }) + .then(() => { + invoke("cleanup_empty_img_dir", { parentDir: last.parentDir, imageDirectory: settings.imageDirectory }); + }) + .catch(console.error); } } }); @@ -701,7 +701,7 @@ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyV, async () => { try { // check for image in clipboard via Rust - const base64Image = await invoke("clipboard_read_image").catch(() => null) as string | null; + const base64Image = await invoke("clipboard_read_image", { macos_image_scaling: settings.macosImageScaling }).catch(() => null) as string | null; if (base64Image && tabManager.activeTab?.path) { const ext = "png"; // output of Rust command is always PNG const filename = `paste_${Date.now()}.${ext}`; @@ -714,6 +714,7 @@ parentDir, filename, base64Data: base64Image, + imageDirectory: settings.imageDirectory, })) as string; const escapedPath = relPath.replace(/ /g, "%20"); const embed = `![alt](${escapedPath})`; @@ -1010,6 +1011,7 @@ const relPath = (await invoke("copy_file_to_img", { srcPath: path, parentDir, + imageDirectory: settings.imageDirectory, })) as string; const escapedPath = relPath.replace(/ /g, "%20"); const embed = `![alt](${escapedPath})`; diff --git a/src/lib/components/Settings.svelte b/src/lib/components/Settings.svelte index 488b4f0..a0787ec 100644 --- a/src/lib/components/Settings.svelte +++ b/src/lib/components/Settings.svelte @@ -634,13 +634,37 @@ +
+ + +
+ +
+ + + Default: img +
+ + {#if settings.osType === 'macos'}
- + + Reduce size by 50%
+ {/if} {/if} @@ -664,18 +688,18 @@ } .settings-modal { - background: var(--color-canvas-default); - border: 1px solid var(--color-border-default); - border-radius: 6px; - box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3); - width: 560px; - max-width: 90vw; - height: 420px; - display: flex; - flex-direction: column; - overflow: hidden; - font-family: var(--win-font); - } + background: var(--color-canvas-default); + border: 1px solid var(--color-border-default); + border-radius: 6px; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3); + width: 560px; + max-width: 90vw; + height: 420px; + display: flex; + flex-direction: column; + overflow: hidden; + font-family: var(--win-font); + } .settings-header { display: flex; diff --git a/src/lib/stores/settings.svelte.ts b/src/lib/stores/settings.svelte.ts index 2911e98..ef9e281 100644 --- a/src/lib/stores/settings.svelte.ts +++ b/src/lib/stores/settings.svelte.ts @@ -225,10 +225,10 @@ export class SettingsStore { localStorage.setItem('editor.startInEditor', String(this.startInEditor)); localStorage.setItem('editor.maxWidth', String(this.editorMaxWidth)); localStorage.setItem('editor.pinnedToc', String(this.pinnedToc)); - localStorage.setItem('editor.tocSide', this.tocSide); - localStorage.setItem('editor.imageDirectory', this.imageDirectory); - localStorage.setItem('editor.macosImageScaling', String(this.macosImageScaling)); - localStorage.setItem('editor.language', this.language); + localStorage.setItem('editor.tocSide', this.tocSide); + localStorage.setItem('editor.imageDirectory', this.imageDirectory); + localStorage.setItem('editor.macosImageScaling', String(this.macosImageScaling)); + localStorage.setItem('editor.language', this.language); localStorage.setItem('editor.font', this.editorFont); localStorage.setItem('editor.fontSize', String(this.editorFontSize)); localStorage.setItem('preview.font', this.previewFont);