From 7a6e3863ad2d3e78245a36c691cfaaefdb065289 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 20 Apr 2026 16:04:11 +0200 Subject: [PATCH] fix: reopen active note after pull updates --- src/App.tsx | 7 ++++--- src/utils/pulledVaultRefresh.test.ts | 5 +++-- src/utils/pulledVaultRefresh.ts | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 3b634367..f5460ce2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -480,8 +480,8 @@ function App() { closeAllTabs, openTabWithContent, } = notes - const handlePulledVaultUpdate = useCallback((updatedFiles: string[]) => - refreshPulledVaultState({ + const handlePulledVaultUpdate = useCallback(async (updatedFiles: string[]) => { + await refreshPulledVaultState({ activeTabPath: notes.activeTabPath, closeAllTabs, hasUnsavedChanges: (path) => vault.unsavedPaths.has(path), @@ -491,7 +491,8 @@ function App() { replaceActiveTab: handleReplaceActiveTab, updatedFiles, vaultPath: resolvedPath, - }), [ + }) + }, [ closeAllTabs, handleReplaceActiveTab, notes.activeTabPath, diff --git a/src/utils/pulledVaultRefresh.test.ts b/src/utils/pulledVaultRefresh.test.ts index 101b7c60..ba788e19 100644 --- a/src/utils/pulledVaultRefresh.test.ts +++ b/src/utils/pulledVaultRefresh.test.ts @@ -39,8 +39,8 @@ describe('refreshPulledVaultState', () => { expect(options.reloadVault).toHaveBeenCalledOnce() expect(options.reloadFolders).toHaveBeenCalledOnce() expect(options.reloadViews).toHaveBeenCalledOnce() + expect(options.closeAllTabs).toHaveBeenCalledOnce() expect(options.replaceActiveTab).toHaveBeenCalledWith(entries[0]) - expect(options.closeAllTabs).not.toHaveBeenCalled() }) it('reloads the active tab after any successful pull with updates', async () => { @@ -49,8 +49,8 @@ describe('refreshPulledVaultState', () => { await refreshPulledVaultState(options) expect(options.reloadVault).toHaveBeenCalledOnce() - expect(options.replaceActiveTab).toHaveBeenCalledWith(expect.objectContaining({ path: '/vault/active.md' })) expect(options.closeAllTabs).not.toHaveBeenCalled() + expect(options.replaceActiveTab).toHaveBeenCalledWith(expect.objectContaining({ path: '/vault/active.md' })) }) it('matches macOS /tmp and /private/tmp aliases when reloading the active tab entry', async () => { @@ -63,6 +63,7 @@ describe('refreshPulledVaultState', () => { await refreshPulledVaultState(options) + expect(options.closeAllTabs).toHaveBeenCalledOnce() expect(options.replaceActiveTab).toHaveBeenCalledWith(activeEntry) }) diff --git a/src/utils/pulledVaultRefresh.ts b/src/utils/pulledVaultRefresh.ts index e46de9d3..7dbb59e1 100644 --- a/src/utils/pulledVaultRefresh.ts +++ b/src/utils/pulledVaultRefresh.ts @@ -19,6 +19,16 @@ function normalizePath(path: string): string { .replace(/\/+$/u, '') } +function resolveUpdatedFilePath(path: string, vaultPath: string): string { + if (path.startsWith('/')) return normalizePath(path) + return normalizePath(`${vaultPath}/${path}`) +} + +function didPullUpdateActiveNote(updatedFiles: string[], vaultPath: string, activeTabPath: string): boolean { + const normalizedActivePath = normalizePath(activeTabPath) + return updatedFiles.some((path) => resolveUpdatedFilePath(path, vaultPath) === normalizedActivePath) +} + export async function refreshPulledVaultState(options: PulledVaultRefreshOptions): Promise { const { activeTabPath, @@ -28,6 +38,8 @@ export async function refreshPulledVaultState(options: PulledVaultRefreshOptions reloadVault, reloadViews, replaceActiveTab, + updatedFiles, + vaultPath, } = options const [entries] = await Promise.all([ @@ -44,6 +56,13 @@ export async function refreshPulledVaultState(options: PulledVaultRefreshOptions return entries } + // Native BlockNote can keep rendering the previous document after a pull that + // changes the active file in place. Dropping the tab first forces a full + // reopen for that specific case without affecting unrelated pull updates. + if (didPullUpdateActiveNote(updatedFiles, vaultPath, activeTabPath)) { + closeAllTabs() + } + await replaceActiveTab(refreshedEntry) return entries }