From e76746f5542542dfa6c0d968fbde3fcfe6a6df96 Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sat, 7 Feb 2026 21:55:46 +0900 Subject: [PATCH 1/4] fix(navigation): smooth page transitions without loading indicators - Reorder navigation flow to update UI immediately, load data in background - Remove unnecessary loading state flags during page navigation - Remove Suspense fallback from BlockEditor to eliminate flickering - Improves perceived performance and eliminates brief progress bar display --- src/App.tsx | 10 +--- src/components/Breadcrumb.tsx | 73 +++++++++++------------- src/components/SubPagesSection.tsx | 10 +++- src/components/fileTree/PageTreeItem.tsx | 37 ++++++------ 4 files changed, 61 insertions(+), 69 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 745e1810..b087c047 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -378,15 +378,7 @@ function AppContent({ workspacePath }: AppContentProps) { ) : currentPageId ? ( - - - Loading editor... - - - } - > + state.selectPage); const pagesById = usePageStore((state) => state.pagesById); - const [isLoading, setIsLoading] = useState(false); - const handleZoomToLevel = useCallback((index: number) => { const { zoomOutToIndex } = useViewStore.getState(); zoomOutToIndex(index); @@ -106,40 +104,37 @@ export function Breadcrumb({ workspaceName, onNavigateHome }: BreadcrumbProps) { const handleNavigateToPage = useCallback( async (pageIdIndex: number) => { - try { - setIsLoading(true); - const pageId = pagePathIds[pageIdIndex]; - const page = pagesById[pageId]; - - if (!pageId || !page) { - console.error( - "[Breadcrumb] Invalid page navigation: pageId or page not found", - ); - return; - } - - await selectPage(pageId); - await loadPage(pageId); + const pageId = pagePathIds[pageIdIndex]; + const page = pagesById[pageId]; + + if (!pageId || !page) { + console.error( + "[Breadcrumb] Invalid page navigation: pageId or page not found", + ); + return; + } - const parentNames: string[] = []; - const parentIds: string[] = []; + const parentNames: string[] = []; + const parentIds: string[] = []; - for (let i = 0; i < pageIdIndex; i++) { - const parentId = pagePathIds[i]; - const parentPage = pagesById[parentId]; - if (parentPage) { - parentNames.push(parentPage.title); - parentIds.push(parentId); - } + for (let i = 0; i < pageIdIndex; i++) { + const parentId = pagePathIds[i]; + const parentPage = pagesById[parentId]; + if (parentPage) { + parentNames.push(parentPage.title); + parentIds.push(parentId); } - parentIds.push(pageId); + } + parentIds.push(pageId); - openNote(pageId, page.title, parentNames, parentIds); - handleZoomOutToPage(); + selectPage(pageId); + openNote(pageId, page.title, parentNames, parentIds); + handleZoomOutToPage(); + + try { + await loadPage(pageId); } catch (error) { - console.error("[Breadcrumb] Failed to navigate to page:", error); - } finally { - setIsLoading(false); + console.error("[Breadcrumb] Failed to load page:", error); } }, [ @@ -184,12 +179,10 @@ export function Breadcrumb({ workspaceName, onNavigateHome }: BreadcrumbProps) { onClick={ isWorkspace ? onNavigateHome - : isLoading - ? undefined - : () => { - const pageIdIndex = index - 1; - handleNavigateToPage(pageIdIndex); - } + : () => { + const pageIdIndex = index - 1; + handleNavigateToPage(pageIdIndex); + } } /> @@ -223,9 +216,7 @@ export function Breadcrumb({ workspaceName, onNavigateHome }: BreadcrumbProps) { ariaLabel={displayText} ariaCurrentPage={isZoomLast} onClick={ - isZoomLast || isLoading - ? undefined - : () => handleZoomToLevel(index) + isZoomLast ? undefined : () => handleZoomToLevel(index) } /> diff --git a/src/components/SubPagesSection.tsx b/src/components/SubPagesSection.tsx index 2e91e299..525b30b7 100644 --- a/src/components/SubPagesSection.tsx +++ b/src/components/SubPagesSection.tsx @@ -54,7 +54,6 @@ export function SubPagesSection({ currentPageId }: SubPagesSectionProps) { const page = pagesById[pageId]; if (!page) return; - // Build parent path const parentNames: string[] = []; const pagePathIds: string[] = []; @@ -74,9 +73,14 @@ export function SubPagesSection({ currentPageId }: SubPagesSectionProps) { buildParentPath(page.parentId); } - await selectPage(page.id); - await loadPage(page.id); + selectPage(page.id); openNote(page.id, page.title, parentNames, pagePathIds); + + try { + await loadPage(page.id); + } catch (error) { + console.error("[SubPagesSection] Failed to load page:", error); + } }; const toggleCollapse = (pageId: string) => { diff --git a/src/components/fileTree/PageTreeItem.tsx b/src/components/fileTree/PageTreeItem.tsx index d75f7c94..6ab65cee 100644 --- a/src/components/fileTree/PageTreeItem.tsx +++ b/src/components/fileTree/PageTreeItem.tsx @@ -131,7 +131,7 @@ export function PageTreeItem({ ], }, ], - [page.id, page.title, page.parentId, onAddChild, onEdit, onDelete, t] + [page.id, page.title, page.parentId, onAddChild, onEdit, onDelete, t], ); const handlePageClick = async (e: React.MouseEvent) => { @@ -159,23 +159,28 @@ export function PageTreeItem({ pagePathIds.push(page.id); - // Batch state updates: load data first, then switch view - const dataLoadStartTime = performance.now(); - await Promise.all([selectPage(page.id), loadPage(page.id)]); - const dataLoadTime = performance.now() - dataLoadStartTime; - - // Use startTransition for view switch to allow React to prioritize user input - const openStartTime = performance.now(); + const uiStartTime = performance.now(); + selectPage(page.id); startTransition(() => { openNote(page.id, page.title, parentNames, pagePathIds); }); - const openTime = performance.now() - openStartTime; + const uiTime = performance.now() - uiStartTime; + + const dataLoadStartTime = performance.now(); + try { + await loadPage(page.id); + } catch (error) { + console.error("[PageTreeItem] Failed to load page:", error); + } + const dataLoadTime = performance.now() - dataLoadStartTime; const totalTime = performance.now() - clickStartTime; console.log( - `[PageTreeItem:timing] === CLICK HANDLER COMPLETE: dataLoad=${dataLoadTime.toFixed( - 2 - )}ms, open=${openTime.toFixed(2)}ms, total=${totalTime.toFixed(2)}ms ===` + `[PageTreeItem:timing] === CLICK HANDLER COMPLETE: ui=${uiTime.toFixed( + 2, + )}ms, dataLoad=${dataLoadTime.toFixed(2)}ms, total=${totalTime.toFixed( + 2, + )}ms ===`, ); }; @@ -221,7 +226,7 @@ export function PageTreeItem({ const moveFocus = (direction: number) => { const buttons = Array.from( - document.querySelectorAll(".page-tree-item-button") + document.querySelectorAll(".page-tree-item-button"), ) as HTMLElement[]; const currentIndex = buttons.indexOf(document.activeElement as HTMLElement); if (currentIndex !== -1) { @@ -316,8 +321,8 @@ export function PageTreeItem({ ? isCollapsed ? "var(--opacity-disabled)" : isHovered - ? "var(--opacity-dimmed)" - : 0 + ? "var(--opacity-dimmed)" + : 0 : 0, visibility: hasChildren ? "visible" : "hidden", }} @@ -460,7 +465,7 @@ export function PageTreeItem({ id: page.id, title: page.title, parentId: page.parentId, - } + }, ); onDelete(page.id); }} From a2a14ab7a7622d102dce229e444d1caeddcd9c1f Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sat, 7 Feb 2026 21:57:52 +0900 Subject: [PATCH 2/4] fix: disable progress indicator display Hide the sync progress indicator component to eliminate visual clutter during page navigation --- src/components/SyncProgress.tsx | 52 ++------------------------------- 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/src/components/SyncProgress.tsx b/src/components/SyncProgress.tsx index b4cbef53..2a47bea4 100644 --- a/src/components/SyncProgress.tsx +++ b/src/components/SyncProgress.tsx @@ -1,59 +1,11 @@ -import { Box, Group, Progress, Text } from "@mantine/core"; -import { IconRefresh } from "@tabler/icons-react"; import { useSyncStore } from "../stores/syncStore"; export function SyncProgress() { - const { isReindexing, progress, message } = useSyncStore(); + const { isReindexing } = useSyncStore(); if (!isReindexing) { return null; } - return ( - - - - - {message || "Re-indexing..."} - - - {Math.round(progress)}% - - - - - - ); + return null; } From 4b5c21c8db9b9aa3911a9f28f9063e0939570033 Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sat, 7 Feb 2026 21:59:40 +0900 Subject: [PATCH 3/4] refactor: remove SyncProgress component entirely The progress indicator was showing during page navigation and causing visual clutter. Removed the component completely since reindex operations (the only use case) should be silent background operations. --- src/App.tsx | 2 -- src/components/SyncProgress.tsx | 11 ----------- 2 files changed, 13 deletions(-) delete mode 100644 src/components/SyncProgress.tsx diff --git a/src/App.tsx b/src/App.tsx index b087c047..bd20ab8d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,7 +18,6 @@ if (typeof window !== "undefined") { import { ErrorNotifications } from "./components/ErrorNotifications"; import { GitStatusIndicator } from "./components/GitStatusIndicator"; import { SnowEffect } from "./components/SnowEffect"; -import { SyncProgress } from "./components/SyncProgress"; import { TitleBar } from "./components/TitleBar"; import { CopilotButton } from "./components/copilot/CopilotButton"; import { BottomLeftControls } from "./components/layout/BottomLeftControls"; @@ -558,7 +557,6 @@ function App() { return ( - ); diff --git a/src/components/SyncProgress.tsx b/src/components/SyncProgress.tsx deleted file mode 100644 index 2a47bea4..00000000 --- a/src/components/SyncProgress.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { useSyncStore } from "../stores/syncStore"; - -export function SyncProgress() { - const { isReindexing } = useSyncStore(); - - if (!isReindexing) { - return null; - } - - return null; -} From ef10c6daf926094c212c1e66270d6ee351c199fd Mon Sep 17 00:00:00 2001 From: 0010capacity <0010capacity@gmail.com> Date: Sat, 7 Feb 2026 22:04:14 +0900 Subject: [PATCH 4/4] fix: hide loading indicator in LinkedReferences Remove the 'Loading linked references...' message and loader that was showing during page navigation. Backlinks now load silently in the background without interrupting the page view experience. --- src/components/LinkedReferences.tsx | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/components/LinkedReferences.tsx b/src/components/LinkedReferences.tsx index 9a629c2a..ce8b5b6e 100644 --- a/src/components/LinkedReferences.tsx +++ b/src/components/LinkedReferences.tsx @@ -1,4 +1,4 @@ -import { Accordion, Box, Loader, Stack, Text } from "@mantine/core"; +import { Accordion, Box, Stack, Text } from "@mantine/core"; import { invoke } from "@tauri-apps/api/core"; import { useEffect, useState } from "react"; import { usePageStore } from "../stores/pageStore"; @@ -23,7 +23,6 @@ interface LinkedReferencesProps { export function LinkedReferences({ pageId }: LinkedReferencesProps) { const [backlinks, setBacklinks] = useState([]); - const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const workspacePath = useWorkspaceStore((state) => state.workspacePath); const pagesById = usePageStore((state) => state.pagesById); @@ -34,7 +33,6 @@ export function LinkedReferences({ pageId }: LinkedReferencesProps) { if (!pageId || !workspacePath) return; const fetchBacklinks = async () => { - setIsLoading(true); setError(null); try { @@ -48,8 +46,6 @@ export function LinkedReferences({ pageId }: LinkedReferencesProps) { setError( err instanceof Error ? err.message : "Failed to load backlinks", ); - } finally { - setIsLoading(false); } }; @@ -91,25 +87,6 @@ export function LinkedReferences({ pageId }: LinkedReferencesProps) { ); const pageCount = backlinks.length; - if (isLoading) { - return ( - - - - - Loading linked references... - - - - ); - } - if (error) { return (