From fd55c000153ed79ab6aac0a7850840fc40b0c281 Mon Sep 17 00:00:00 2001 From: Yuri Karamian Date: Mon, 20 Jul 2026 02:03:15 +0200 Subject: [PATCH] Fix note unfiling and relative image paths (#222) --- src/lib/components/Editor.svelte | 25 ++++++++++++++----------- src/lib/components/NoteList.svelte | 21 +++++++++++++++++++++ src/lib/utils/paths.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/lib/utils/paths.ts diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index dd0b886..94690f5 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -48,6 +48,7 @@ import { WrapSelectedText } from '$lib/editor/extensions/wrapSelectedText'; import { calloutGroup, calloutIcon, calloutLabel, CALLOUT_MENU, transformCalloutBlockquotes, serializeCallout } from '$lib/editor/callouts'; import { wrapTextareaSelection } from '$lib/editor/source/selectionPairs'; + import { relativePath } from '$lib/utils/paths'; import GraphView from './GraphView.svelte'; import TagSuggestInput from './TagSuggestInput.svelte'; import { isMobile, isAndroid } from '$lib/platform'; @@ -3697,7 +3698,7 @@ } } // Convert asset:// URLs back to relative paths for saving - if (!src.startsWith('asset:') && !src.startsWith('http://asset.localhost')) return src; + if (!src.startsWith('asset:') && !src.startsWith('http://asset.localhost') && !src.startsWith('https://asset.localhost')) return src; let absPath = ''; try { const url = new URL(src); @@ -3707,18 +3708,20 @@ } // Clean up any leading double/triple slashes (URL parsing artifact) absPath = absPath.replace(/^\/{2,}/, '/'); - // Make relative to note directory (matches how resolveImageSrc works) + absPath = absPath.replace(/^\/([A-Za-z]:\/)/, '$1').replace(/\\/g, '/'); + // Preserve note-relative paths, including parent-directory segments. const notePath = $activeNotePath; - if (notePath) { - const noteDir = notePath.substring(0, notePath.lastIndexOf('/')); - if (absPath.startsWith(noteDir + '/')) { - return absPath.substring(noteDir.length + 1); - } - } - // Fallback: make relative to vault root - const vaultRoot = $appConfig?.active_vault; + const vaultRoot = $appConfig?.active_vault?.replace(/\\/g, '/').replace(/\/$/, ''); if (vaultRoot && absPath.startsWith(vaultRoot + '/')) { - return absPath.substring(vaultRoot.length + 1); + const vaultRelative = absPath.substring(vaultRoot.length + 1); + // HelixNotes-managed attachments have explicit vault-root semantics. + if (vaultRelative.startsWith('.helixnotes/')) return vaultRelative; + if (notePath) { + const normalizedNotePath = notePath.replace(/\\/g, '/'); + const noteDir = normalizedNotePath.substring(0, normalizedNotePath.lastIndexOf('/')); + return relativePath(noteDir, absPath); + } + return vaultRelative; } return absPath; } diff --git a/src/lib/components/NoteList.svelte b/src/lib/components/NoteList.svelte index 204c135..5430edd 100644 --- a/src/lib/components/NoteList.svelte +++ b/src/lib/components/NoteList.svelte @@ -787,6 +787,19 @@ } } + function isFiled(note: NoteEntry): boolean { + const vaultRoot = $appConfig?.active_vault?.replace(/\\/g, '/').replace(/\/$/, ''); + if (!vaultRoot) return false; + const normalizedPath = note.path.replace(/\\/g, '/'); + const noteDir = normalizedPath.substring(0, normalizedPath.lastIndexOf('/')); + return noteDir !== vaultRoot; + } + + function unfileNote(note: NoteEntry) { + const vaultRoot = $appConfig?.active_vault; + if (vaultRoot) handleMoveNote(note, vaultRoot); + } + function clampMenu(x: number, y: number, menuWidth = 220, menuHeight = 400): { x: number; y: number } { if (x + menuWidth > window.innerWidth) x = window.innerWidth - menuWidth - 8; if (y + menuHeight > window.innerHeight) y = window.innerHeight - menuHeight - 8; @@ -1535,6 +1548,14 @@ Move to... + {#if isFiled(contextMenu.note)} + + {/if} {#if !isMobile}