mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-07-24 07:45:57 +02:00
Notebook reorder: make path math separator-agnostic so manual sort works on Windows (backslash paths)
This commit is contained in:
@@ -55,6 +55,14 @@
|
|||||||
let draggedNotebookPath = $state<string | null>(null);
|
let draggedNotebookPath = $state<string | null>(null);
|
||||||
let nbPointer: { src: string } | null = null;
|
let nbPointer: { src: string } | null = null;
|
||||||
|
|
||||||
|
// Notebook paths arrive from Rust with native separators (backslashes on Windows), so path math must be separator-agnostic.
|
||||||
|
const norm = (p: string) => (p ?? '').replace(/\\/g, '/');
|
||||||
|
const lastSep = (p: string) => Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'));
|
||||||
|
const parentOf = (p: string) => { const i = lastSep(p); return i < 0 ? '' : p.slice(0, i); };
|
||||||
|
const baseOf = (p: string) => p.slice(lastSep(p) + 1);
|
||||||
|
const joinPath = (parent: string, name: string) => parent + (parent.includes('\\') ? '\\' : '/') + name;
|
||||||
|
const isDescendant = (child: string, anc: string) => child.startsWith(anc + '/') || child.startsWith(anc + '\\');
|
||||||
|
|
||||||
// Apply manual ordering recursively when in 'manual' sort mode.
|
// Apply manual ordering recursively when in 'manual' sort mode.
|
||||||
// In 'alphabetical' mode the Rust scanner already sorts, so we pass through unchanged.
|
// In 'alphabetical' mode the Rust scanner already sorts, so we pass through unchanged.
|
||||||
function sortNotebooksTree(tree: NotebookEntry[]): NotebookEntry[] {
|
function sortNotebooksTree(tree: NotebookEntry[]): NotebookEntry[] {
|
||||||
@@ -353,25 +361,25 @@
|
|||||||
dropPosition = null;
|
dropPosition = null;
|
||||||
draggedNotebookPath = null;
|
draggedNotebookPath = null;
|
||||||
// Don't move onto self or descendant
|
// Don't move onto self or descendant
|
||||||
if (destPath === srcPath || destPath.startsWith(srcPath + '/')) return;
|
if (destPath === srcPath || isDescendant(destPath, srcPath)) return;
|
||||||
// Don't move if already in that parent
|
// Don't move if already in that parent
|
||||||
const parentDir = srcPath.substring(0, srcPath.lastIndexOf('/'));
|
const parentDir = parentOf(srcPath);
|
||||||
if (parentDir === destPath) return;
|
if (parentDir === destPath) return;
|
||||||
try {
|
try {
|
||||||
const oldName = srcPath.split('/').pop() || '';
|
const oldName = baseOf(srcPath);
|
||||||
const newBasePath = destPath + '/' + oldName;
|
const newBasePath = joinPath(destPath, oldName);
|
||||||
await moveNotebook(srcPath, destPath);
|
await moveNotebook(srcPath, destPath);
|
||||||
// Update collapsedNotebooks paths
|
// Update collapsedNotebooks paths
|
||||||
$collapsedNotebooks = $collapsedNotebooks.map(p => {
|
$collapsedNotebooks = $collapsedNotebooks.map(p => {
|
||||||
if (p === srcPath) return newBasePath;
|
if (p === srcPath) return newBasePath;
|
||||||
if (p.startsWith(srcPath + '/')) return newBasePath + p.slice(srcPath.length);
|
if (isDescendant(p, srcPath)) return newBasePath + p.slice(srcPath.length);
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
// Update active notebook/note if inside the moved notebook
|
// Update active notebook/note if inside the moved notebook
|
||||||
if ($activeNotebook?.path === srcPath || $activeNotebook?.path.startsWith(srcPath + '/')) {
|
if ($activeNotebook?.path === srcPath || isDescendant($activeNotebook?.path ?? '', srcPath)) {
|
||||||
selectAllNotes();
|
selectAllNotes();
|
||||||
}
|
}
|
||||||
if ($activeNotePath && $activeNotePath.startsWith(srcPath + '/')) {
|
if ($activeNotePath && isDescendant($activeNotePath, srcPath)) {
|
||||||
const newNotePath = newBasePath + $activeNotePath.slice(srcPath.length);
|
const newNotePath = newBasePath + $activeNotePath.slice(srcPath.length);
|
||||||
$activeNotePath = newNotePath;
|
$activeNotePath = newNotePath;
|
||||||
$activeNote = await readNote(newNotePath);
|
$activeNote = await readNote(newNotePath);
|
||||||
@@ -447,9 +455,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findSiblingsOfPath(tree: NotebookEntry[], parentPath: string, vaultRoot: string): NotebookEntry[] {
|
function findSiblingsOfPath(tree: NotebookEntry[], parentPath: string, vaultRoot: string): NotebookEntry[] {
|
||||||
if (parentPath === vaultRoot) return tree;
|
const target = norm(parentPath);
|
||||||
|
if (target === norm(vaultRoot)) return tree;
|
||||||
for (const nb of tree) {
|
for (const nb of tree) {
|
||||||
if (nb.path === parentPath) return nb.children;
|
if (norm(nb.path) === target) return nb.children;
|
||||||
if (nb.children.length > 0) {
|
if (nb.children.length > 0) {
|
||||||
const found = findSiblingsOfPath(nb.children, parentPath, vaultRoot);
|
const found = findSiblingsOfPath(nb.children, parentPath, vaultRoot);
|
||||||
if (found.length > 0) return found;
|
if (found.length > 0) return found;
|
||||||
@@ -463,41 +472,39 @@
|
|||||||
dropPosition = null;
|
dropPosition = null;
|
||||||
draggedNotebookPath = null;
|
draggedNotebookPath = null;
|
||||||
if (srcPath === targetPath) return;
|
if (srcPath === targetPath) return;
|
||||||
if (targetPath.startsWith(srcPath + '/')) return; // can't move into descendant
|
if (isDescendant(targetPath, srcPath)) return; // can't move into descendant
|
||||||
const vaultRoot = $appConfig?.active_vault;
|
const vaultRoot = $appConfig?.active_vault;
|
||||||
if (!vaultRoot) return;
|
if (!vaultRoot) return;
|
||||||
|
|
||||||
const srcParent = srcPath.substring(0, srcPath.lastIndexOf('/'));
|
const srcParent = parentOf(srcPath);
|
||||||
const targetParent = targetPath.substring(0, targetPath.lastIndexOf('/'));
|
const targetParent = parentOf(targetPath);
|
||||||
let actualSrcPath = srcPath;
|
let actualSrcPath = srcPath;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 1: if different parents, move src into target's parent first
|
// Step 1: if different parents, move src into target's parent first
|
||||||
if (srcParent !== targetParent) {
|
if (srcParent !== targetParent) {
|
||||||
const oldName = srcPath.split('/').pop() || '';
|
const oldName = baseOf(srcPath);
|
||||||
const newPath = targetParent === vaultRoot
|
const newPath = joinPath(targetParent, oldName);
|
||||||
? vaultRoot + '/' + oldName
|
|
||||||
: targetParent + '/' + oldName;
|
|
||||||
await moveNotebook(srcPath, targetParent);
|
await moveNotebook(srcPath, targetParent);
|
||||||
// Migrate paths in collapsedNotebooks
|
// Migrate paths in collapsedNotebooks
|
||||||
$collapsedNotebooks = $collapsedNotebooks.map(p => {
|
$collapsedNotebooks = $collapsedNotebooks.map(p => {
|
||||||
if (p === srcPath) return newPath;
|
if (p === srcPath) return newPath;
|
||||||
if (p.startsWith(srcPath + '/')) return newPath + p.slice(srcPath.length);
|
if (isDescendant(p, srcPath)) return newPath + p.slice(srcPath.length);
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
// Migrate paths in notebookOrder
|
// Migrate paths in notebookOrder
|
||||||
const migrated: Record<string, number> = {};
|
const migrated: Record<string, number> = {};
|
||||||
for (const [p, v] of Object.entries($notebookOrder)) {
|
for (const [p, v] of Object.entries($notebookOrder)) {
|
||||||
if (p === srcPath) migrated[newPath] = v;
|
if (p === srcPath) migrated[newPath] = v;
|
||||||
else if (p.startsWith(srcPath + '/')) migrated[newPath + p.slice(srcPath.length)] = v;
|
else if (isDescendant(p, srcPath)) migrated[newPath + p.slice(srcPath.length)] = v;
|
||||||
else migrated[p] = v;
|
else migrated[p] = v;
|
||||||
}
|
}
|
||||||
$notebookOrder = migrated;
|
$notebookOrder = migrated;
|
||||||
// Update active notebook/note
|
// Update active notebook/note
|
||||||
if ($activeNotebook?.path === srcPath || $activeNotebook?.path.startsWith(srcPath + '/')) {
|
if ($activeNotebook?.path === srcPath || isDescendant($activeNotebook?.path ?? '', srcPath)) {
|
||||||
selectAllNotes();
|
selectAllNotes();
|
||||||
}
|
}
|
||||||
if ($activeNotePath && $activeNotePath.startsWith(srcPath + '/')) {
|
if ($activeNotePath && isDescendant($activeNotePath, srcPath)) {
|
||||||
const newNotePath = newPath + $activeNotePath.slice(srcPath.length);
|
const newNotePath = newPath + $activeNotePath.slice(srcPath.length);
|
||||||
$activeNotePath = newNotePath;
|
$activeNotePath = newNotePath;
|
||||||
$activeNote = await readNote(newNotePath);
|
$activeNote = await readNote(newNotePath);
|
||||||
|
|||||||
Reference in New Issue
Block a user