diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 987dcc5..3039968 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -112,6 +112,13 @@ pub fn get_notebooks(state: State<'_, AppState>) -> Result, S operations::scan_notebooks(vault_path) } +#[tauri::command] +pub fn count_root_notes(state: State<'_, AppState>) -> Result { + let config = state.config.lock().map_err(|e| e.to_string())?; + let vault_path = config.active_vault.as_ref().ok_or("No active vault")?; + operations::count_root_notes(vault_path) +} + #[tauri::command] pub fn create_notebook( state: State<'_, AppState>, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 427dcd8..b6649cf 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -98,6 +98,7 @@ pub fn run() { commands::set_font_family, commands::set_line_height, commands::get_notebooks, + commands::count_root_notes, commands::create_notebook, commands::rename_notebook, commands::delete_notebook, diff --git a/src-tauri/src/vault/operations.rs b/src-tauri/src/vault/operations.rs index 30d84cc..6c779af 100644 --- a/src-tauri/src/vault/operations.rs +++ b/src-tauri/src/vault/operations.rs @@ -167,6 +167,22 @@ fn is_hidden(path: &Path) -> bool { .unwrap_or(false) } +pub fn count_root_notes(vault_path: &str) -> Result { + let root = Path::new(vault_path); + if !root.exists() { + return Err("Vault path does not exist".to_string()); + } + let count = fs::read_dir(root) + .map_err(|e| e.to_string())? + .filter_map(|e| e.ok()) + .filter(|e| { + e.file_type().map(|ft| ft.is_file()).unwrap_or(false) + && e.path().extension().and_then(|x| x.to_str()) == Some("md") + }) + .count(); + Ok(count) +} + pub fn scan_notes(vault_path: &str, notebook_path: Option<&str>) -> Result, String> { let scan_path = notebook_path.unwrap_or(vault_path); let root = Path::new(scan_path); diff --git a/src/lib/api.ts b/src/lib/api.ts index 30be7b7..77794cc 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -47,6 +47,10 @@ export async function getNotebooks(): Promise { return invoke("get_notebooks"); } +export async function countRootNotes(): Promise { + return invoke("count_root_notes"); +} + export async function createNotebook( parentRelative: string | null, name: string, diff --git a/src/lib/components/Sidebar.svelte b/src/lib/components/Sidebar.svelte index 1fe2c55..0dce135 100644 --- a/src/lib/components/Sidebar.svelte +++ b/src/lib/components/Sidebar.svelte @@ -15,9 +15,10 @@ notebookIcons, appConfig, quickAccessPaths, - collapsedNotebooks + collapsedNotebooks, + rootNoteCount } from '$lib/stores/app'; - import { getNotebooks, getAllTags, createNotebook, deleteNotebook, renameNotebook, moveNotebook, getNotebookIcons, setNotebookIcon, saveAttachment, getQuickAccess, addQuickAccess, removeQuickAccess, emptyTrash, moveNote, readNote, getNotes } from '$lib/api'; + import { getNotebooks, getAllTags, createNotebook, deleteNotebook, renameNotebook, moveNotebook, getNotebookIcons, setNotebookIcon, saveAttachment, getQuickAccess, addQuickAccess, removeQuickAccess, emptyTrash, moveNote, readNote, getNotes, countRootNotes } from '$lib/api'; import { open as openDialog } from '@tauri-apps/plugin-dialog'; import { readFile } from '@tauri-apps/plugin-fs'; import { convertFileSrc } from '@tauri-apps/api/core'; @@ -53,16 +54,20 @@ try { if (isMobile) { // On mobile, parallelize and skip getAllTags (derive from $notes instead) - const [nbs, icons, qaNotes] = await Promise.all([ + const [nbs, icons, qaNotes, rootCount] = await Promise.all([ getNotebooks(), getNotebookIcons(), getQuickAccess(), + countRootNotes(), ]); $notebooks = nbs; $notebookIcons = icons; $quickAccessPaths = qaNotes.map(n => n.relative_path); + $rootNoteCount = rootCount; } else { - $notebooks = await getNotebooks(); + const [nbs, rootCount] = await Promise.all([getNotebooks(), countRootNotes()]); + $notebooks = nbs; + $rootNoteCount = rootCount; $tags = await getAllTags(); $notebookIcons = await getNotebookIcons(); const qaNotes = await getQuickAccess(); @@ -80,6 +85,19 @@ onViewChanged(); } + function selectUnfiled() { + const vault = $appConfig?.active_vault; + if (!vault) return; + if ($viewMode === 'notebook' && $activeNotebook?.relative_path === '') { + selectAllNotes(); + return; + } + $viewMode = 'notebook'; + $activeNotebook = { name: 'Unfiled Notes', path: vault, relative_path: '', children: [], note_count: $rootNoteCount }; + $activeTag = null; + onViewChanged(); + } + function selectNotebook(nb: NotebookEntry) { // Deselect if clicking the already-active notebook if ($viewMode === 'notebook' && $activeNotebook?.path === nb.path) { @@ -466,6 +484,20 @@ {/if}
+ {#if $rootNoteCount > 0} + + {/if} {#each $notebooks as nb (nb.path)} {@render notebookItem(nb, 0)} {/each} diff --git a/src/lib/stores/app.ts b/src/lib/stores/app.ts index 752f5d2..8189016 100644 --- a/src/lib/stores/app.ts +++ b/src/lib/stores/app.ts @@ -31,6 +31,7 @@ export const collapsedNotebooks = writable([]); // Data export const notebooks = writable([]); +export const rootNoteCount = writable(0); export const notes = writable([]); export const tags = writable<[string, number][]>([]); export const activeNote = writable(null);