mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-07-25 00:06:05 +02:00
Fix note unfiling and relative image paths (#222)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
</svg>
|
||||
Move to...
|
||||
</button>
|
||||
{#if isFiled(contextMenu.note)}
|
||||
<button onclick={() => unfileNote(contextMenu!.note)}>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/><path d="M9 13h6"/>
|
||||
</svg>
|
||||
Unfile Note
|
||||
</button>
|
||||
{/if}
|
||||
{#if !isMobile}
|
||||
<button onclick={async () => { const n = contextMenu!.note; contextMenu = null; await selectNote(n); setTimeout(() => window.print(), 300); }}>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
function pathParts(path: string): string[] {
|
||||
return path.replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
}
|
||||
|
||||
function pathRoot(path: string): string {
|
||||
const normalized = path.replace(/\\/g, '/');
|
||||
const drive = normalized.match(/^([A-Za-z]:)\//);
|
||||
if (drive) return drive[1].toLowerCase();
|
||||
return normalized.startsWith('/') ? '/' : '';
|
||||
}
|
||||
|
||||
export function relativePath(fromDirectory: string, targetPath: string): string {
|
||||
const from = pathParts(fromDirectory);
|
||||
const target = pathParts(targetPath);
|
||||
if (pathRoot(fromDirectory) !== pathRoot(targetPath)) return targetPath.replace(/\\/g, '/');
|
||||
const windowsPath = /^[A-Za-z]:[\\/]/.test(fromDirectory) || /^[A-Za-z]:[\\/]/.test(targetPath);
|
||||
const equal = windowsPath
|
||||
? (left: string, right: string) => left.toLowerCase() === right.toLowerCase()
|
||||
: (left: string, right: string) => left === right;
|
||||
|
||||
let common = 0;
|
||||
while (common < from.length && common < target.length && equal(from[common], target[common])) {
|
||||
common++;
|
||||
}
|
||||
|
||||
const result = [...Array(from.length - common).fill('..'), ...target.slice(common)];
|
||||
return result.join('/') || '.';
|
||||
}
|
||||
Reference in New Issue
Block a user