fix: keep default workspace views mounted

This commit is contained in:
lucaronin
2026-05-15 11:18:51 +02:00
parent 3695b6f3fe
commit ce04a49bae
5 changed files with 45 additions and 11 deletions

View File

@@ -245,7 +245,7 @@ describe('StatusBar', () => {
expect(screen.getByText('Work Vault')).toBeInTheDocument()
})
it('shows the active workspace as checked because it is always included', () => {
it('shows the active workspace real mount state so stale unmounted defaults can be repaired', () => {
render(
<StatusBar
noteCount={100}
@@ -263,8 +263,8 @@ describe('StatusBar', () => {
fireEvent.click(screen.getByRole('button', { name: 'Switch vault' }))
const activeCheckbox = screen.getByRole('checkbox', { name: 'Include Main Vault in the unified graph' })
expect(activeCheckbox).toBeChecked()
expect(activeCheckbox).toBeDisabled()
expect(activeCheckbox).not.toBeChecked()
expect(activeCheckbox).not.toBeDisabled()
})
it('uses the expanded multi-workspace vault picker layout', () => {

View File

@@ -95,6 +95,7 @@ interface MountToggleRequest {
canSetDefaultWorkspace: boolean
defaultPath: string
includedVaultCount: number
isMounted: boolean
path: string
}
@@ -304,9 +305,12 @@ function shouldDisableMountToggle({
canSetDefaultWorkspace,
defaultPath,
includedVaultCount,
isMounted,
path,
}: MountToggleRequest): boolean {
return path === defaultPath && (includedVaultCount <= 1 || !canSetDefaultWorkspace)
return path === defaultPath
&& isMounted
&& (includedVaultCount <= 1 || !canSetDefaultWorkspace)
}
function selectVaultPath({
@@ -351,9 +355,10 @@ function useVaultMenuInteractions({
canSetDefaultWorkspace: !!onSetDefaultWorkspace,
defaultPath,
includedVaultCount: includedVaults.length,
isMounted: includedVaults.find((vault) => vault.path === path)?.mounted !== false,
path,
})
), [defaultPath, includedVaults.length, onSetDefaultWorkspace])
), [defaultPath, includedVaults, onSetDefaultWorkspace])
const handleSelectVault = useCallback((path: string) => {
selectVaultPath({
@@ -435,7 +440,7 @@ function VaultMenuItem({
>
{multiWorkspaceEnabled && (
<WorkspaceMountCheckbox
checked={isActive || vault.mounted !== false}
checked={vault.mounted !== false}
disabled={unavailable || disableMountToggle}
locale={locale}
onMountedChange={onMountedChange}

View File

@@ -44,7 +44,8 @@ export function useWorkspaceIdentityActions({
const setDefaultWorkspace = useCallback((path: string) => {
setDefaultWorkspacePath(path)
}, [setDefaultWorkspacePath])
setExtraVaults(updateVaultOptionInList(path, { mounted: true }))
}, [setDefaultWorkspacePath, setExtraVaults])
return { setDefaultWorkspace, updateWorkspaceIdentity }
}

View File

@@ -69,8 +69,9 @@ function loadVaultEntriesWithCommand({ vaultPath, command }: VaultPathOptions &
.then((entries) => normalizeVaultEntries(entries, vaultPath))
}
function shouldSkipMountedVault(vault: VaultOption): boolean {
return vault.available === false || vault.mounted === false || !vault.path.trim()
function shouldIncludeVault(vault: VaultOption, primaryPaths: Set<string>): boolean {
if (!vault.path.trim() || vault.available === false) return false
return vault.mounted !== false || primaryPaths.has(vault.path)
}
function shouldReloadEmptyWorkspaceResult(entries: VaultEntry[], options: WorkspaceEntryLoadOptions): boolean {
@@ -109,10 +110,11 @@ export function loadWorkspaceEntries(
: entries)
}
function uniqueMountedVaults({ vaultPath, vaults = [], includeFallbackVault = true }: MountedVaultEntriesOptions): VaultOption[] {
function uniqueMountedVaults({ defaultWorkspacePath, vaultPath, vaults = [], includeFallbackVault = true }: MountedVaultEntriesOptions): VaultOption[] {
const byPath = new Map<string, VaultOption>()
const primaryPaths = new Set([vaultPath, defaultWorkspacePath ?? ''].filter((path) => path.trim()))
for (const vault of vaults) {
if (shouldSkipMountedVault(vault)) continue
if (!shouldIncludeVault(vault, primaryPaths)) continue
byPath.set(vault.path, vault)
}
if (shouldIncludeFallbackVault(byPath, vaultPath, includeFallbackVault)) {

View File

@@ -60,4 +60,30 @@ describe('loadMountedVaultViews', () => {
{ filename: 'focus.yml', name: 'Team Focus', rootPath: '/team', workspacePath: '/team' },
])
})
it('loads saved views from the default workspace even when stale state marks it unmounted', async () => {
mockInvoke.mockImplementation((command: string, args?: Record<string, unknown>) => {
if (command !== 'list_views') return Promise.resolve([])
if (args?.vaultPath === '/default') {
return Promise.resolve([{ filename: 'default.yml', definition: viewDefinition('Default View') }])
}
if (args?.vaultPath === '/mounted') {
return Promise.resolve([{ filename: 'mounted.yml', definition: viewDefinition('Mounted View') }])
}
throw new Error(`Unexpected vaultPath ${String(args?.vaultPath)}`)
})
const views = await loadMountedVaultViews({
defaultWorkspacePath: '/default',
vaultPath: '/default',
vaults: [
{ label: 'Default', path: '/default', alias: 'default', mounted: false, available: true },
{ label: 'Mounted', path: '/mounted', alias: 'mounted', mounted: true, available: true },
{ label: 'Hidden', path: '/hidden', alias: 'hidden', mounted: false, available: true },
],
})
expect(views.map((view) => view.definition.name)).toEqual(['Default View', 'Mounted View'])
})
})