mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 16:03:49 +00:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user