diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index c18ac50..3a39494 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -387,10 +387,14 @@ pub fn get_all_note_titles(state: State<'_, AppState>) -> Result) -> Result) -> Result = HashSet::new(); let mut contents: Vec = Vec::new(); + let hn_dir = operations::helixnotes_dir(vault_path); for entry in walkdir::WalkDir::new(vault) .into_iter() + // Cross-platform exclusion (string "/.helixnotes/" checks miss Windows + // backslash paths; is_hidden also covers .stversions/.stfolder/.trash/.git). + .filter_entry(|e| !operations::is_hidden(e.path()) && !e.path().starts_with(&hn_dir)) .filter_map(|e| e.ok()) { let path = entry.path(); if !path.is_file() { continue; } let path_str = path.to_string_lossy().to_string(); - if path_str.contains("/.helixnotes/") || path_str.contains("/.trash/") - || path_str.contains("/.stversions/") || path_str.contains("/.stfolder") { continue; } if path.extension().and_then(|e| e.to_str()) != Some("md") { continue; } // Skip Syncthing conflict files if let Some(name) = path.file_name().and_then(|n| n.to_str()) { @@ -483,7 +486,8 @@ pub fn get_graph_data(state: State<'_, AppState>) -> Result (Vec, usi (entries, note_count) } -fn is_hidden(path: &Path) -> bool { +pub(crate) fn is_hidden(path: &Path) -> bool { path.file_name() .and_then(|n| n.to_str()) .map(|n| { @@ -708,17 +708,20 @@ fn update_wikilinks_after_rename( new_title: &str, ) { let vault = Path::new(vault_path); - let vault_prefix = format!("{}/", vault_path); + let hn_dir = helixnotes_dir(vault_path); - // Compute vault-relative path refs (without .md) for path-based wikilink refs - let old_rel_ref = old_path.strip_prefix(&vault_prefix) - .unwrap_or(old_path) - .strip_suffix(".md") - .unwrap_or(old_path); - let new_rel_ref = new_path.strip_prefix(&vault_prefix) - .unwrap_or(new_path) - .strip_suffix(".md") - .unwrap_or(new_path); + // Vault-relative path ref (without .md), normalized to forward slashes so it + // matches [[folder/note]] wikilinks on Windows (OS paths use backslashes there). + let rel_ref = |p: &str| -> String { + let rel = Path::new(p) + .strip_prefix(vault) + .map(|r| r.to_string_lossy().into_owned()) + .unwrap_or_else(|_| p.to_string()) + .replace('\\', "/"); + rel.strip_suffix(".md").unwrap_or(&rel).to_string() + }; + let old_rel_ref = rel_ref(old_path); + let new_rel_ref = rel_ref(new_path); // Check if another note with the same old_title exists in the vault. // If so, title-based replacements (rules 1-3) are ambiguous and must be skipped, @@ -726,6 +729,7 @@ fn update_wikilinks_after_rename( // vs. the other note with the same title. let title_is_unique = !WalkDir::new(vault) .into_iter() + .filter_entry(|e| !is_hidden(e.path()) && !e.path().starts_with(&hn_dir)) .filter_map(|e| e.ok()) .any(|e| { let p = e.path(); @@ -735,10 +739,6 @@ fn update_wikilinks_after_rename( if p.extension().and_then(|ext| ext.to_str()) != Some("md") { return false; } - let p_str = p.to_string_lossy(); - if p_str.contains("/.helixnotes/") || p_str.contains("/.trash/") { - return false; - } // Check if this note's filename (without .md) matches the old title p.file_stem() .and_then(|s| s.to_str()) @@ -748,12 +748,12 @@ fn update_wikilinks_after_rename( for entry in WalkDir::new(vault) .into_iter() + .filter_entry(|e| !is_hidden(e.path()) && !e.path().starts_with(&hn_dir)) .filter_map(|e| e.ok()) { let path = entry.path(); if !path.is_file() { continue; } let path_str = path.to_string_lossy(); - if path_str.contains("/.helixnotes/") || path_str.contains("/.trash/") { continue; } if path.extension().and_then(|e| e.to_str()) != Some("md") { continue; } // Skip the renamed note itself if *path_str == *new_path { continue; } diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index 64340d1..92be0f9 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -1325,7 +1325,10 @@ function wikiLinkFolderPath(entry: NoteTitleEntry): string { const vaultRoot = $appConfig?.active_vault; if (!vaultRoot || !entry.path) return ''; - const rel = entry.path.startsWith(vaultRoot + '/') ? entry.path.slice(vaultRoot.length + 1) : entry.path; + // Normalize Windows backslashes so the folder subtitle shows there too. + const path = entry.path.replace(/\\/g, '/'); + const root = vaultRoot.replace(/\\/g, '/'); + const rel = path.startsWith(root + '/') ? path.slice(root.length + 1) : path; const parts = rel.split('/'); // Return parent folder(s), excluding the filename return parts.length > 1 ? parts.slice(0, -1).join('/') + '/' : ''; diff --git a/src/lib/components/GraphView.svelte b/src/lib/components/GraphView.svelte index bde8ad9..9a0a44f 100644 --- a/src/lib/components/GraphView.svelte +++ b/src/lib/components/GraphView.svelte @@ -76,8 +76,10 @@ } function getFolderKey(path: string): string { - const vaultRoot = $appConfig?.active_vault || ''; - const rel = vaultRoot && path.startsWith(vaultRoot + '/') ? path.slice(vaultRoot.length + 1) : path; + // Normalize Windows backslashes so folder grouping/colours work there too. + const p = (path || '').replace(/\\/g, '/'); + const vaultRoot = ($appConfig?.active_vault || '').replace(/\\/g, '/'); + const rel = vaultRoot && p.startsWith(vaultRoot + '/') ? p.slice(vaultRoot.length + 1) : p; const slash = rel.indexOf('/'); return slash >= 0 ? rel.slice(0, slash) : ''; }