diff --git a/package.json b/package.json index 0d43ff4..7fa004a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "helixnotes", "private": true, "license": "AGPL-3.0-or-later", - "version": "1.1.4", + "version": "1.1.5", "type": "module", "scripts": { "dev": "vite dev", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f827031..7d45a17 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1750,7 +1750,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "helixnotes" -version = "1.1.4" +version = "1.1.5" dependencies = [ "chrono", "dirs", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2bb061d..5c09616 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "helixnotes" -version = "1.1.4" +version = "1.1.5" description = "Local markdown note-taking app" authors = ["HelixNotes"] license = "AGPL-3.0-or-later" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 8e798a7..155f1f6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "HelixNotes", - "version": "1.1.4", + "version": "1.1.5", "identifier": "com.helixnotes.app", "build": { "frontendDist": "../build", diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index 5a3f68a..b024aa7 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -37,7 +37,7 @@ import { readFile } from '@tauri-apps/plugin-fs'; import { openUrl } from '@tauri-apps/plugin-opener'; import { openFile, copyFileTo } from '$lib/api'; - import { save as saveDialog } from '@tauri-apps/plugin-dialog'; + import { save as saveDialog, open as openDialog } from '@tauri-apps/plugin-dialog'; import { activeNote, activeNotePath, appConfig, editorDirty, sourceMode, focusMode, readOnly, quickAccessPaths, notes } from '$lib/stores/app'; import { saveNote, saveImage, saveAttachment, addQuickAccess, removeQuickAccess, getQuickAccess, getNoteVersions, getNoteVersionContent, createVersion, aiAsk, getAllNoteTitles, readNote } from '$lib/api'; import type { VersionEntry, AiStreamEvent, NoteTitleEntry } from '$lib/types'; @@ -2827,6 +2827,77 @@ } } + async function pickAndInsertImage() { + try { + const selected = await openDialog({ + multiple: false, + filters: [{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp', 'ico', 'tiff', 'tif', 'avif'] }] + }); + if (!selected) return; + const filePath = typeof selected === 'string' ? selected : selected; + const fileName = (filePath as string).split('/').pop() || 'image.png'; + const fileData = await readFile(filePath as string); + const data = Array.from(fileData); + const relativePath = await saveImage(fileName, data); + if (editor) { + const displaySrc = resolveImageSrc(relativePath); + editor.chain().focus().setImage({ src: displaySrc }).run(); + } + } catch (e) { + console.error('Failed to pick and insert image:', e); + } + } + + async function pickAndInsertFile() { + try { + const selected = await openDialog({ + multiple: false, + }); + if (!selected) return; + const filePath = typeof selected === 'string' ? selected : selected; + const fileName = (filePath as string).split('/').pop() || 'file'; + const ext = fileName.split('.').pop()?.toLowerCase() || ''; + const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp', 'ico', 'tiff', 'tif', 'avif']; + const fileData = await readFile(filePath as string); + const data = Array.from(fileData); + + if (imageExts.includes(ext)) { + const relativePath = await saveImage(fileName, data); + if (editor) { + const displaySrc = resolveImageSrc(relativePath); + editor.chain().focus().setImage({ src: displaySrc }).run(); + } + } else if (ext === 'pdf') { + const relativePath = await saveAttachment(fileName, data); + if (!editor) return; + const usePdfPreview = $appConfig?.pdf_preview ?? false; + if (usePdfPreview) { + editor.chain().focus().insertContent({ + type: 'pdfEmbed', + attrs: { src: relativePath, name: fileName }, + }).run(); + } else { + const sizeKB = Math.round(fileData.length / 1024); + const label = `${fileName} (${sizeKB} kB)`; + editor.chain().focus() + .insertContent(`${label} `) + .run(); + } + } else { + const relativePath = await saveAttachment(fileName, data); + if (editor) { + const sizeKB = Math.round(fileData.length / 1024); + const label = `${fileName} (${sizeKB} kB)`; + editor.chain().focus() + .insertContent(`${label} `) + .run(); + } + } + } catch (e) { + console.error('Failed to pick and insert file:', e); + } + } + // Source mode toggle — only react to explicit user toggle, not note switches $effect(() => { const isSource = $sourceMode; @@ -3382,11 +3453,11 @@ {#if insertDropdown}
e.stopPropagation()}> - - @@ -3417,11 +3488,11 @@ {#if insertDropdown}
e.stopPropagation()}> - - @@ -3716,21 +3787,6 @@ {/if} {/if} - - { - const file = (e.target as HTMLInputElement).files?.[0]; - if (file) insertImage(file); - (e.target as HTMLInputElement).value = ''; - }} /> - { - const file = (e.target as HTMLInputElement).files?.[0]; - if (file) { - if (file.type.startsWith('image/')) insertImage(file); - else if (file.type === 'application/pdf') insertPdf(file); - else insertFileAttachment(file); - } - (e.target as HTMLInputElement).value = ''; - }} />
{#if linkContextMenu}