Clarify inherited worktree visibility settings

This commit is contained in:
Brennan Benson
2026-08-13 14:40:18 -07:00
parent f263c6319e
commit e49897c3c4
5 changed files with 125 additions and 2 deletions
@@ -634,6 +634,21 @@ describe('WorktreeVisibilityDialog', () => {
expect(sourceRow('GSD').textContent).toContain('Using global: Hidden')
expect(sourceRow('Other locations').textContent).toContain('Using global: Hidden')
expect(document.querySelector('button[aria-label^="Use global for"]')).toBeNull()
expect(document.body.textContent).toContain(
'Sources enabled in Global Settings also apply to this project.'
)
})
it('does not show the global impact notice when inherited sources are hidden', async () => {
mocks.state.repos = [makeRepo({ externalWorktreeVisibility: undefined })]
mocks.state.settings = { worktreeVisibilityDefaults: { external: 'hide' } }
await renderDialog()
expect(document.body.textContent).not.toContain(
'Sources enabled in Global Settings also apply to this project.'
)
expect(buttonWithText('Manage in Global Settings')).not.toBeNull()
})
it('labels project overrides and resets a built-in source to global inheritance', async () => {
@@ -57,6 +57,7 @@ import {
import { WorktreeVisibilityGlobalSettingsLink } from './WorktreeVisibilityGlobalSettingsLink'
import { WorktreeVisibilityScanStatus } from './WorktreeVisibilityScanStatus'
import { createWorktreeVisibilityUseGlobalMutation } from './worktree-visibility-use-global'
import { hasGloballyShownWorktreeVisibilitySource } from './worktree-visibility-source-provenance'
export default function WorktreeVisibilityDialog(): React.JSX.Element | null {
const activeModal = useAppStore((s) => s.activeModal)
@@ -91,6 +92,10 @@ export default function WorktreeVisibilityDialog(): React.JSX.Element | null {
const effectivelyToggling = isToggling || activeMutation?.kind === 'toggle'
const visibilityDefaults = useRepoOwnerVisibilityDefaults(repo)
const removableSourceIds = useMemo(() => getRepoCustomWorktreeVisibilitySourceIds(repo), [repo])
const hasGloballyShownSource = useMemo(
() => (repo ? hasGloballyShownWorktreeVisibilitySource(repo, visibilityDefaults) : false),
[repo, visibilityDefaults]
)
useLayoutEffect(() => {
currentMutationScopeRef.current = mutationScope
@@ -388,7 +393,7 @@ export default function WorktreeVisibilityDialog(): React.JSX.Element | null {
onUseDefault={handleUseDefault}
/>
<WorktreeVisibilityGlobalSettingsLink />
<WorktreeVisibilityGlobalSettingsLink hasGloballyShownSource={hasGloballyShownSource} />
<WorktreeVisibilityScanStatus
state={listState}
@@ -16,7 +16,7 @@ function openGlobalWorktreeVisibilitySettings(): void {
store.openSettingsPage()
}
export function WorktreeVisibilityGlobalSettingsLink(): React.JSX.Element {
function GlobalSettingsButton(): React.JSX.Element {
return (
<Button
type="button"
@@ -33,3 +33,25 @@ export function WorktreeVisibilityGlobalSettingsLink(): React.JSX.Element {
</Button>
)
}
export function WorktreeVisibilityGlobalSettingsLink({
hasGloballyShownSource
}: {
hasGloballyShownSource: boolean
}): React.JSX.Element {
if (!hasGloballyShownSource) {
return <GlobalSettingsButton />
}
return (
<div className="grid gap-1 rounded-lg border border-border bg-muted/30 px-2.5 py-2">
<p className="text-xs text-muted-foreground">
{translate(
'auto.components.sidebar.WorktreeVisibilityDialog.globalSettingsApply',
'Sources enabled in Global Settings also apply to this project.'
)}
</p>
<GlobalSettingsButton />
</div>
)
}
@@ -0,0 +1,49 @@
import { describe, expect, it } from 'vitest'
import type { Repo, WorktreeVisibilityDefaults } from '../../../../shared/types'
import { hasGloballyShownWorktreeVisibilitySource } from './worktree-visibility-source-provenance'
function repo(overrides: Partial<Repo> = {}): Repo {
return {
id: 'repo-1',
path: '/repo',
displayName: 'orca',
badgeColor: '#000000',
addedAt: 0,
externalWorktreeVisibility: undefined,
...overrides
}
}
describe('hasGloballyShownWorktreeVisibilitySource', () => {
const shownClaude: WorktreeVisibilityDefaults = {
external: 'hide',
sourcePreferences: { builtIn: { claude: 'show' } }
}
it('detects an inherited source enabled in global settings', () => {
expect(hasGloballyShownWorktreeVisibilitySource(repo(), shownClaude)).toBe(true)
})
it('ignores a globally enabled source overridden by the project', () => {
expect(
hasGloballyShownWorktreeVisibilitySource(
repo({ worktreeVisibilitySourcePreferences: { builtIn: { claude: 'hide' } } }),
shownClaude
)
).toBe(false)
})
it('ignores global settings when all inherited sources are hidden', () => {
expect(hasGloballyShownWorktreeVisibilitySource(repo(), { external: 'hide' })).toBe(false)
})
it('detects an inherited custom source enabled in global settings', () => {
expect(
hasGloballyShownWorktreeVisibilitySource(repo(), {
external: 'hide',
customSources: [{ id: 'team', rootPath: '/srv/team' }],
sourcePreferences: { custom: { team: 'show' } }
})
).toBe(true)
})
})
@@ -5,6 +5,7 @@ import type { Repo, WorktreeVisibilityDefaults } from '../../../../shared/types'
import {
effectiveDefaultBuiltInWorktreeSourceVisibility,
effectiveDefaultCustomWorktreeSourceVisibility,
normalizeCustomWorktreeVisibilitySources,
normalizeWorktreeVisibilitySourcePreferences
} from '../../../../shared/worktree-visibility-sources'
import type { WorktreeVisibilitySourceRow } from './WorktreeVisibilitySourceList'
@@ -14,6 +15,37 @@ type SourceProvenance = {
globalEnabled?: boolean
}
export function hasGloballyShownWorktreeVisibilitySource(
repo: Repo,
visibilityDefaults: WorktreeVisibilityDefaults | undefined
): boolean {
const defaults = visibilityDefaults ?? {}
const repoCustomSourceIds = new Set(
normalizeCustomWorktreeVisibilitySources(repo.customWorktreeVisibilitySources)?.map(
(source) => source.id
) ?? []
)
const sources: WorktreeVisibilitySourceRow[] = [
{ kind: 'built-in', id: 'claude' },
{ kind: 'built-in', id: 'gsd' },
...(normalizeCustomWorktreeVisibilitySources(defaults.customSources) ?? []).map((source) => ({
kind: 'custom' as const,
source
})),
{ kind: 'other' }
]
return sources.some((source) => {
const provenance = getWorktreeVisibilitySourceProvenance(
repo,
source,
defaults,
repoCustomSourceIds
)
return provenance?.kind === 'global' && provenance.globalEnabled === true
})
}
export function getWorktreeVisibilitySourceProvenance(
repo: Repo | undefined,
source: WorktreeVisibilitySourceRow,