feat(settings): unlock hidden experimental group via Option-click on the Experimental page title

Replaces the Shift-click-sidebar unlock with the Updates-header idiom:
Option-click the Experimental page title toggles the hidden group.
Removes the now-unused click-modifier plumbing from the settings sidebar.
This commit is contained in:
Brennan Benson
2026-08-27 18:00:53 -07:00
parent b9cc7ffa68
commit 4b385d8c25
4 changed files with 25 additions and 52 deletions
@@ -25,7 +25,7 @@ type ExperimentalPaneProps = {
settings: GlobalSettings
updateSettings: (updates: Partial<GlobalSettings>) => void
/** Hidden-experimental group is only rendered once the user has unlocked
* it via Shift-clicking the Experimental sidebar entry. */
* it via Option-clicking the Experimental page title. */
hiddenExperimentalUnlocked?: boolean
}
@@ -389,7 +389,7 @@ function Settings(): React.JSX.Element {
const [hasUnsavedBranchPromptChanges, setHasUnsavedBranchPromptChanges] = useState(false)
const [sourceControlAiPromptDiscardSignal, setSourceControlAiPromptDiscardSignal] = useState(0)
const confirm = useConfirmationDialog()
// Why: session-only (deliberately not persisted) unlock — Shift-click the Experimental entry reveals the hidden group.
// Why: session-only (deliberately not persisted) unlock — Option-click the Experimental page title reveals the hidden group.
const [hiddenExperimentalUnlocked, setHiddenExperimentalUnlocked] = useState(false)
const contentScrollRef = useRef<HTMLDivElement | null>(null)
const searchInputRef = useRef<HTMLInputElement | null>(null)
@@ -1108,17 +1108,10 @@ function Settings(): React.JSX.Element {
])
const scrollToSection = useCallback(
async (
sectionId: string,
modifiers?: { metaKey: boolean; ctrlKey: boolean; shiftKey: boolean; altKey: boolean }
): Promise<void> => {
async (sectionId: string): Promise<void> => {
if (sectionId !== activeSectionId && !(await confirmDiscardSourceControlAiPromptChanges())) {
return
}
// Why: Shift-click the Experimental row unlocks the hidden power-user group (session-only).
if (sectionId === 'experimental' && modifiers?.shiftKey) {
setHiddenExperimentalUnlocked((previous) => !previous)
}
const container = contentScrollRef.current
if (container) {
container.scrollTo({ top: 0 })
@@ -1844,6 +1837,13 @@ function Settings(): React.JSX.Element {
'New features that are still taking shape. Give them a try.'
)}
searchEntries={getSectionSearchEntries('experimental')}
// Why: Option-click the page title unlocks the hidden staff group
// (session-only) — same idiom as Option-click on the Updates header.
onTitleClick={(event) => {
if (event.altKey) {
setHiddenExperimentalUnlocked((previous) => !previous)
}
}}
>
{isSectionMounted('experimental') ? (
<ExperimentalPane
@@ -31,6 +31,9 @@ type SettingsSectionProps = {
* section-scoped actions (e.g. "Import from Ghostty") that would otherwise
* crowd the settings list as their own row. */
headerAction?: React.ReactNode
/** Click handler on the section title — the hook for hidden staff
* affordances (Option-click reveals), mirroring the Updates header. */
onTitleClick?: (event: React.MouseEvent<HTMLHeadingElement>) => void
}
export function SettingsSection({
@@ -45,7 +48,8 @@ export function SettingsSection({
badgeAccessory,
forceVisible = false,
isActive,
headerAction
headerAction,
onTitleClick
}: SettingsSectionProps): React.JSX.Element | null {
const query = useAppStore((state) => state.settingsSearchQuery)
const activeFromContext = useContext(ActiveSettingsSectionContext)
@@ -66,7 +70,10 @@ export function SettingsSection({
<section id={id} data-settings-section={id} className={cn('scroll-mt-8 space-y-6', className)}>
<div className="flex flex-wrap items-start justify-between gap-4 border-b border-border/60 pb-5">
<div className="min-w-0 space-y-2">
<h2 className="flex flex-wrap items-center gap-2 text-2xl font-semibold leading-tight text-foreground">
<h2
className="flex flex-wrap items-center gap-2 text-2xl font-semibold leading-tight text-foreground"
onClick={onTitleClick}
>
{title}
{badge ? (
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.05em] text-muted-foreground">
@@ -50,15 +50,7 @@ type SettingsSidebarProps = {
searchInputRef?: RefObject<HTMLInputElement | null>
searchAutoFocus?: boolean
onBack: () => void
onSelectSection: (
sectionId: string,
modifiers: {
metaKey: boolean
ctrlKey: boolean
shiftKey: boolean
altKey: boolean
}
) => void
onSelectSection: (sectionId: string) => void
}
function SettingsSearchField({
@@ -116,12 +108,7 @@ function isVisibleInstallStatus(
type SettingsSetupGuideRowProps = {
progress: SettingsSetupGuideProgress
setupActive: boolean
onSelect: (modifiers: {
metaKey: boolean
ctrlKey: boolean
shiftKey: boolean
altKey: boolean
}) => void
onSelect: () => void
}
function SettingsSetupGuideNavRow({
@@ -138,14 +125,7 @@ function SettingsSetupGuideNavRow({
'Onboarding checklist, {{value0}} of {{value1}} done. Show setup guide.',
{ value0: progress.doneCount, value1: progress.total }
)}
onClick={(event) =>
onSelect({
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,
shiftKey: event.shiftKey,
altKey: event.altKey
})
}
onClick={() => onSelect()}
className={cn(
'flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-worktree-sidebar-ring/50',
setupActive
@@ -238,7 +218,7 @@ export function SettingsSidebar({
<SettingsSetupGuideNavRow
progress={setupGuideProgress}
setupActive={setupActive}
onSelect={(modifiers) => onSelectSection('setup-guide', modifiers)}
onSelect={() => onSelectSection('setup-guide')}
/>
</div>
) : null}
@@ -262,14 +242,7 @@ export function SettingsSidebar({
key={section.id}
aria-current={isActive ? 'page' : undefined}
data-current={isActive ? 'true' : undefined}
onClick={(event) =>
onSelectSection(section.id, {
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,
shiftKey: event.shiftKey,
altKey: event.altKey
})
}
onClick={() => onSelectSection(section.id)}
className={navItemClassName(isActive)}
>
<Icon className="size-4 shrink-0" />
@@ -305,14 +278,7 @@ export function SettingsSidebar({
key={section.id}
aria-current={isActive ? 'page' : undefined}
data-current={isActive ? 'true' : undefined}
onClick={(event) =>
onSelectSection(section.id, {
metaKey: event.metaKey,
ctrlKey: event.ctrlKey,
shiftKey: event.shiftKey,
altKey: event.altKey
})
}
onClick={() => onSelectSection(section.id)}
className={navItemClassName(isActive)}
>
<RepoIconGlyph