fix: keep default workspace views mounted
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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'])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user