From 61bfcfead52f2efd6ec6be07a65af7ac27f009b8 Mon Sep 17 00:00:00 2001 From: Aji Anaz Date: Thu, 9 Jul 2026 23:07:53 +0700 Subject: [PATCH] feat(docs): clickable breadcrumb for ancestor navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document breadcrumbs were display-only (truncated UUID segments). Now each ancestor crumb is clickable and navigates to that parent doc. - Build a flat id→DocEntry lookup (docById) in loadRootDocs from the already-loaded full doc list — no extra network requests. - getBreadcrumb() resolves selectedDoc.path (materialized ancestor UUID chain) to real DocEntry[] via docById. - Each crumb renders the ancestor title and calls selectDoc(ancestor) on click. 'Documents' root crumb still clears the selection. --- src/lib/components/DocumentsView.svelte | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/lib/components/DocumentsView.svelte b/src/lib/components/DocumentsView.svelte index f494aff..4d5782d 100644 --- a/src/lib/components/DocumentsView.svelte +++ b/src/lib/components/DocumentsView.svelte @@ -87,6 +87,8 @@ let rootDocs = $state([]); let expandedIds = $state>(new Set()); let childrenCache = $state>(new Map()); + // Flat id → DocEntry lookup for ancestor lookups (breadcrumb navigation). + let docById = $state>(new Map()); let selectedDoc = $state(null); let editorContent = $state(''); let editorTitle = $state(''); @@ -159,8 +161,10 @@ const all = await docs.list(); // Group by parent_id; roots have no parent_id. const byParent = new Map(); + const byId = new Map(); const roots: DocEntry[] = []; for (const d of all) { + byId.set(d.id, d); const pid = d.parent_id ?? null; if (pid) { const arr = byParent.get(pid) ?? []; @@ -172,6 +176,7 @@ } childrenCache = byParent; rootDocs = roots; + docById = byId; // Expand every folder by default so the full tree is visible. expandedIds = new Set(byParent.keys()); } catch (e: any) { @@ -509,13 +514,16 @@ } // ─── Build breadcrumb path ──────────────────────────────────────── - function getBreadcrumb(): string[] { - if (!selectedDoc) return []; - if (selectedDoc.path && selectedDoc.path.length > 0) { - const parts = selectedDoc.path.split('/').filter(Boolean); - return parts.length > 0 ? parts : []; - } - return []; + // `selectedDoc.path` is a materialized ancestor chain of UUIDs + // ("/uuid/uuid/"). Map each segment to its DocEntry via the flat lookup so + // each crumb is clickable and shows a real title. + function getBreadcrumb(): DocEntry[] { + if (!selectedDoc?.path) return []; + return selectedDoc.path + .split('/') + .filter(Boolean) + .map((id) => docById.get(id)) + .filter((d): d is DocEntry => !!d); } // ─── Word count & reading time ──────────────────────────────────── @@ -674,9 +682,9 @@