Fix AppImage crash when inserting images by using Tauri native file dialog instead of GTK file chooser

This commit is contained in:
Yuri Karamian
2026-02-21 19:46:50 +01:00
parent 8baa976a02
commit 31f4867e91
5 changed files with 80 additions and 24 deletions
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -1750,7 +1750,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "helixnotes"
version = "1.1.4"
version = "1.1.5"
dependencies = [
"chrono",
"dirs",
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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",
+76 -20
View File
@@ -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(`<a href="${relativePath}">${label}</a> `)
.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(`<a href="${relativePath}">${label}</a> `)
.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}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="fmt-dropdown insert-dropdown" onclick={(e) => e.stopPropagation()}>
<button onclick={() => { insertDropdown = false; document.querySelector<HTMLInputElement>('#insert-image-input')?.click(); }}>
<button onclick={() => { insertDropdown = false; pickAndInsertImage(); }}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 00-2.828 0L6 21"/></svg>
Image
</button>
<button onclick={() => { insertDropdown = false; document.querySelector<HTMLInputElement>('#insert-file-input')?.click(); }}>
<button onclick={() => { insertDropdown = false; pickAndInsertFile(); }}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 01-2-2V4a2 2 0 012-2h8a2.4 2.4 0 011.704.706l3.588 3.588A2.4 2.4 0 0120 8v12a2 2 0 01-2 2z"/><path d="M14 2v5a1 1 0 001 1h5"/></svg>
File
</button>
@@ -3417,11 +3488,11 @@
{#if insertDropdown}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="fmt-dropdown insert-dropdown" onclick={(e) => e.stopPropagation()}>
<button onclick={() => { insertDropdown = false; document.querySelector<HTMLInputElement>('#insert-image-input')?.click(); }}>
<button onclick={() => { insertDropdown = false; pickAndInsertImage(); }}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 00-2.828 0L6 21"/></svg>
Image
</button>
<button onclick={() => { insertDropdown = false; document.querySelector<HTMLInputElement>('#insert-file-input')?.click(); }}>
<button onclick={() => { insertDropdown = false; pickAndInsertFile(); }}>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 01-2-2V4a2 2 0 012-2h8a2.4 2.4 0 011.704.706l3.588 3.588A2.4 2.4 0 0120 8v12a2 2 0 01-2 2z"/><path d="M14 2v5a1 1 0 001 1h5"/></svg>
File
</button>
@@ -3716,21 +3787,6 @@
{/if}
{/if}
<!-- Hidden file inputs for Insert dropdown -->
<input type="file" id="insert-image-input" accept="image/*" style="display:none" onchange={(e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) insertImage(file);
(e.target as HTMLInputElement).value = '';
}} />
<input type="file" id="insert-file-input" style="display:none" onchange={(e) => {
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 = '';
}} />
</div>
{#if linkContextMenu}