add unfiled notes

This commit is contained in:
Yuri Karamian
2026-03-17 21:45:51 +01:00
parent 79f16b454a
commit 9f595212dd
6 changed files with 65 additions and 4 deletions
+7
View File
@@ -112,6 +112,13 @@ pub fn get_notebooks(state: State<'_, AppState>) -> Result<Vec<NotebookEntry>, S
operations::scan_notebooks(vault_path) operations::scan_notebooks(vault_path)
} }
#[tauri::command]
pub fn count_root_notes(state: State<'_, AppState>) -> Result<usize, String> {
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] #[tauri::command]
pub fn create_notebook( pub fn create_notebook(
state: State<'_, AppState>, state: State<'_, AppState>,
+1
View File
@@ -98,6 +98,7 @@ pub fn run() {
commands::set_font_family, commands::set_font_family,
commands::set_line_height, commands::set_line_height,
commands::get_notebooks, commands::get_notebooks,
commands::count_root_notes,
commands::create_notebook, commands::create_notebook,
commands::rename_notebook, commands::rename_notebook,
commands::delete_notebook, commands::delete_notebook,
+16
View File
@@ -167,6 +167,22 @@ fn is_hidden(path: &Path) -> bool {
.unwrap_or(false) .unwrap_or(false)
} }
pub fn count_root_notes(vault_path: &str) -> Result<usize, String> {
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<Vec<NoteEntry>, String> { pub fn scan_notes(vault_path: &str, notebook_path: Option<&str>) -> Result<Vec<NoteEntry>, String> {
let scan_path = notebook_path.unwrap_or(vault_path); let scan_path = notebook_path.unwrap_or(vault_path);
let root = Path::new(scan_path); let root = Path::new(scan_path);
+4
View File
@@ -47,6 +47,10 @@ export async function getNotebooks(): Promise<NotebookEntry[]> {
return invoke("get_notebooks"); return invoke("get_notebooks");
} }
export async function countRootNotes(): Promise<number> {
return invoke("count_root_notes");
}
export async function createNotebook( export async function createNotebook(
parentRelative: string | null, parentRelative: string | null,
name: string, name: string,
+36 -4
View File
@@ -15,9 +15,10 @@
notebookIcons, notebookIcons,
appConfig, appConfig,
quickAccessPaths, quickAccessPaths,
collapsedNotebooks collapsedNotebooks,
rootNoteCount
} from '$lib/stores/app'; } 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 { open as openDialog } from '@tauri-apps/plugin-dialog';
import { readFile } from '@tauri-apps/plugin-fs'; import { readFile } from '@tauri-apps/plugin-fs';
import { convertFileSrc } from '@tauri-apps/api/core'; import { convertFileSrc } from '@tauri-apps/api/core';
@@ -53,16 +54,20 @@
try { try {
if (isMobile) { if (isMobile) {
// On mobile, parallelize and skip getAllTags (derive from $notes instead) // 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(), getNotebooks(),
getNotebookIcons(), getNotebookIcons(),
getQuickAccess(), getQuickAccess(),
countRootNotes(),
]); ]);
$notebooks = nbs; $notebooks = nbs;
$notebookIcons = icons; $notebookIcons = icons;
$quickAccessPaths = qaNotes.map(n => n.relative_path); $quickAccessPaths = qaNotes.map(n => n.relative_path);
$rootNoteCount = rootCount;
} else { } else {
$notebooks = await getNotebooks(); const [nbs, rootCount] = await Promise.all([getNotebooks(), countRootNotes()]);
$notebooks = nbs;
$rootNoteCount = rootCount;
$tags = await getAllTags(); $tags = await getAllTags();
$notebookIcons = await getNotebookIcons(); $notebookIcons = await getNotebookIcons();
const qaNotes = await getQuickAccess(); const qaNotes = await getQuickAccess();
@@ -80,6 +85,19 @@
onViewChanged(); 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) { function selectNotebook(nb: NotebookEntry) {
// Deselect if clicking the already-active notebook // Deselect if clicking the already-active notebook
if ($viewMode === 'notebook' && $activeNotebook?.path === nb.path) { if ($viewMode === 'notebook' && $activeNotebook?.path === nb.path) {
@@ -466,6 +484,20 @@
{/if} {/if}
<div class="notebook-list"> <div class="notebook-list">
{#if $rootNoteCount > 0}
<button
class="notebook-item"
class:active={$viewMode === 'notebook' && $activeNotebook?.relative_path === ''}
onclick={selectUnfiled}
>
<span class="chevron-spacer"></span>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;opacity:0.6">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
<polyline points="14 2 14 8 20 8" />
</svg>
<span class="notebook-name">Unfiled Notes <span class="notebook-count">{$rootNoteCount}</span></span>
</button>
{/if}
{#each $notebooks as nb (nb.path)} {#each $notebooks as nb (nb.path)}
{@render notebookItem(nb, 0)} {@render notebookItem(nb, 0)}
{/each} {/each}
+1
View File
@@ -31,6 +31,7 @@ export const collapsedNotebooks = writable<string[]>([]);
// Data // Data
export const notebooks = writable<NotebookEntry[]>([]); export const notebooks = writable<NotebookEntry[]>([]);
export const rootNoteCount = writable<number>(0);
export const notes = writable<NoteEntry[]>([]); export const notes = writable<NoteEntry[]>([]);
export const tags = writable<[string, number][]>([]); export const tags = writable<[string, number][]>([]);
export const activeNote = writable<NoteContent | null>(null); export const activeNote = writable<NoteContent | null>(null);