feat(desktop): add reset-to-defaults to the statusbar context menu
Once you have toggled a few items on and off there is no way to get back to the shipped layout short of remembering which ids are in STATUSBAR_HIDDEN_BY_DEFAULT. Add a row to the bar's right-click menu that restores that set. The row is disabled rather than hidden when nothing is customized, so it also advertises that a shipped layout exists. Reset touches item layout only — whole-bar visibility is a separate preference, and resetting from the bar's own menu should not make the bar you are right-clicking disappear.
This commit is contained in:
committed by
brooklyn!
parent
c692312704
commit
e505ff9777
@@ -17,7 +17,13 @@ import { ContribRender } from '@/contrib/react/boundary'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { useKeybindHint } from '@/lib/keybinds/use-keybind-hint'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $statusbarHiddenIds, setStatusbarItemVisible, toggleStatusbarVisible } from '@/store/statusbar-prefs'
|
||||
import {
|
||||
$statusbarHiddenIds,
|
||||
isStatusbarLayoutDefault,
|
||||
resetStatusbarLayout,
|
||||
setStatusbarItemVisible,
|
||||
toggleStatusbarVisible
|
||||
} from '@/store/statusbar-prefs'
|
||||
|
||||
// Shared chrome styling for interactive statusbar items (button / link / menu
|
||||
// trigger). The 'text' variant intentionally omits hover/transition/disabled.
|
||||
@@ -176,6 +182,18 @@ function StatusbarVisibilityMenu({
|
||||
</ContextMenuCheckboxItem>
|
||||
))}
|
||||
<ContextMenuSeparator />
|
||||
{/* Disabled rather than hidden when nothing is customized: the row is
|
||||
also how you find out there IS a shipped layout to get back to.
|
||||
Groups with the hide row below — both act on the bar, not an item. */}
|
||||
<ContextMenuItem
|
||||
disabled={isStatusbarLayoutDefault(hiddenIds)}
|
||||
onSelect={event => {
|
||||
event.preventDefault()
|
||||
resetStatusbarLayout()
|
||||
}}
|
||||
>
|
||||
<span className="truncate">{copy.resetStatusbar}</span>
|
||||
</ContextMenuItem>
|
||||
</>
|
||||
)}
|
||||
<ContextMenuItem onSelect={toggleStatusbarVisible}>
|
||||
|
||||
@@ -125,6 +125,68 @@ describe('statusbar item visibility', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('reset to defaults', () => {
|
||||
it('puts a customized bar back to the shipped show/hide set', async () => {
|
||||
$statusbarHiddenIds.set(['gateway-health'])
|
||||
|
||||
const statusbar = bar([item('cron', 'Cron'), item('gateway-health', 'Gateway')])
|
||||
|
||||
expect(screen.queryByText('Gateway')).toBeNull()
|
||||
expect(within(statusbar).getByText('Cron')).toBeTruthy()
|
||||
|
||||
openContextMenu(statusbar)
|
||||
fireEvent.click(await screen.findByRole('menuitem', { name: /reset to defaults/i }))
|
||||
|
||||
expect($statusbarHiddenIds.get()).toEqual([...STATUSBAR_HIDDEN_BY_DEFAULT])
|
||||
expect(within(statusbar).getByText('Gateway')).toBeTruthy()
|
||||
// Scoped to the bar: the menu stays open after a reset, so an unscoped query
|
||||
// matches its still-listed 'Cron' checkbox row rather than a bar item.
|
||||
expect(within(statusbar).queryByText('Cron')).toBeNull()
|
||||
})
|
||||
|
||||
it('disables the row when the layout is already default', async () => {
|
||||
const statusbar = bar([item('cron', 'Cron'), item('gateway-health', 'Gateway')])
|
||||
|
||||
openContextMenu(statusbar)
|
||||
|
||||
const row = await screen.findByRole('menuitem', { name: /reset to defaults/i })
|
||||
expect(row.getAttribute('data-disabled')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('enables the row as soon as one item differs, in either direction', async () => {
|
||||
const statusbar = bar([item('cron', 'Cron'), item('gateway-health', 'Gateway')])
|
||||
|
||||
// Showing a default-hidden item counts…
|
||||
$statusbarHiddenIds.set(STATUSBAR_HIDDEN_BY_DEFAULT.filter(id => id !== 'cron'))
|
||||
openContextMenu(statusbar)
|
||||
expect(
|
||||
(await screen.findByRole('menuitem', { name: /reset to defaults/i })).getAttribute('data-disabled')
|
||||
).toBeNull()
|
||||
|
||||
// …and so does hiding a default-shown one.
|
||||
$statusbarHiddenIds.set([...STATUSBAR_HIDDEN_BY_DEFAULT, 'gateway-health'])
|
||||
expect(
|
||||
(await screen.findByRole('menuitem', { name: /reset to defaults/i })).getAttribute('data-disabled')
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('leaves whole-bar visibility alone — reset is about items, not the bar', async () => {
|
||||
// Set to the NON-default so a reset that wrongly restored bar visibility too
|
||||
// would flip this back to true and fail. StatusbarControls doesn't read the
|
||||
// atom (the controller gates the mount), so the menu is still reachable here.
|
||||
$statusbarVisible.set(false)
|
||||
$statusbarHiddenIds.set([])
|
||||
|
||||
const statusbar = bar([item('gateway-health', 'Gateway')])
|
||||
|
||||
openContextMenu(statusbar)
|
||||
fireEvent.click(await screen.findByRole('menuitem', { name: /reset to defaults/i }))
|
||||
|
||||
expect($statusbarHiddenIds.get()).toEqual([...STATUSBAR_HIDDEN_BY_DEFAULT])
|
||||
expect($statusbarVisible.get()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('whole-bar visibility', () => {
|
||||
it('hides the bar from the context menu, leaving the keybind as the way back', async () => {
|
||||
const statusbar = bar([item('gateway-health', 'Gateway')])
|
||||
|
||||
@@ -2561,6 +2561,7 @@ export const en: Translations = {
|
||||
gatewayTitle: 'Gateway',
|
||||
customizeTitle: 'Show in status bar',
|
||||
hideStatusbar: 'Hide status bar',
|
||||
resetStatusbar: 'Reset to defaults',
|
||||
toggleApprovalMode: 'Approvals',
|
||||
toggleBackendVersion: 'Backend version',
|
||||
toggleCommandCenter: 'Command Center',
|
||||
|
||||
@@ -2153,6 +2153,7 @@ export interface Translations {
|
||||
gatewayTitle: string
|
||||
customizeTitle: string
|
||||
hideStatusbar: string
|
||||
resetStatusbar: string
|
||||
toggleApprovalMode: string
|
||||
toggleBackendVersion: string
|
||||
toggleCommandCenter: string
|
||||
|
||||
@@ -2739,6 +2739,7 @@ export const zh: Translations = {
|
||||
gatewayTitle: '网关',
|
||||
customizeTitle: '在状态栏中显示',
|
||||
hideStatusbar: '隐藏状态栏',
|
||||
resetStatusbar: '恢复默认设置',
|
||||
toggleApprovalMode: '审批',
|
||||
toggleBackendVersion: '后端版本',
|
||||
toggleCommandCenter: '命令中心',
|
||||
|
||||
@@ -51,3 +51,20 @@ export function setStatusbarItemVisible(id: string, visible: boolean) {
|
||||
|
||||
$statusbarHiddenIds.set(visible ? hidden.filter(entry => entry !== id) : [...hidden, id])
|
||||
}
|
||||
|
||||
/** Pure so the menu can derive its reset row's disabled state from the hidden
|
||||
* list it already subscribes to, rather than reading the atom out of band.
|
||||
* Set-compared: order is incidental (items are appended as they're hidden) and
|
||||
* a duplicated id shouldn't read as a customization. */
|
||||
export function isStatusbarLayoutDefault(hidden: readonly string[]) {
|
||||
const ids = new Set(hidden)
|
||||
|
||||
return ids.size === STATUSBAR_HIDDEN_BY_DEFAULT.length && STATUSBAR_HIDDEN_BY_DEFAULT.every(id => ids.has(id))
|
||||
}
|
||||
|
||||
/** Put the show/hide set back to what ships. Only touches item layout — whole-bar
|
||||
* visibility is a separate preference, and resetting from the bar's own menu
|
||||
* shouldn't make the bar the user is right-clicking disappear. */
|
||||
export function resetStatusbarLayout() {
|
||||
$statusbarHiddenIds.set([...STATUSBAR_HIDDEN_BY_DEFAULT])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user