mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-28 18:19:28 +00:00
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import { useStore } from '@nanostores/react'
|
|
import { useEffect, useMemo } from 'react'
|
|
|
|
import type { StatusbarItem } from '@/app/shell/statusbar-controls'
|
|
import {
|
|
DropdownMenuLabel,
|
|
DropdownMenuRadioGroup,
|
|
DropdownMenuRadioItem,
|
|
DropdownMenuSeparator
|
|
} from '@/components/ui/dropdown-menu'
|
|
import { useI18n } from '@/i18n'
|
|
import { Zap, ZapFilled } from '@/lib/icons'
|
|
import {
|
|
$approvalModes,
|
|
type ApprovalMode,
|
|
type ApprovalModeRequester,
|
|
setApprovalModeForProfile,
|
|
syncApprovalModeForProfile
|
|
} from '@/store/approval-mode'
|
|
|
|
export function useApprovalModeStatusbarItem(profile: string, requestGateway: ApprovalModeRequester): StatusbarItem {
|
|
const { t } = useI18n()
|
|
const copy = t.shell.approvalMode
|
|
const modes = useStore($approvalModes)
|
|
const mode = modes[profile.trim() || 'default'] ?? 'smart'
|
|
|
|
const labels = useMemo<Record<ApprovalMode, string>>(
|
|
() => ({ manual: copy.manual, smart: copy.smart, off: copy.off }),
|
|
[copy.manual, copy.off, copy.smart]
|
|
)
|
|
|
|
const descriptions = useMemo<Record<ApprovalMode, string>>(
|
|
() => ({
|
|
manual: copy.manualDescription,
|
|
smart: copy.smartDescription,
|
|
off: copy.offDescription
|
|
}),
|
|
[copy.manualDescription, copy.offDescription, copy.smartDescription]
|
|
)
|
|
|
|
useEffect(() => {
|
|
void syncApprovalModeForProfile(requestGateway, profile).catch(() => undefined)
|
|
}, [profile, requestGateway])
|
|
|
|
return {
|
|
className: mode === 'off' ? 'bg-(--chrome-action-hover) text-foreground' : undefined,
|
|
icon: mode === 'off' ? <ZapFilled className="size-3.5" /> : <Zap className="size-3.5 opacity-70" />,
|
|
id: 'approval-mode',
|
|
label: labels[mode],
|
|
menuAlign: 'end',
|
|
menuClassName: 'w-72 p-1',
|
|
menuContent: (
|
|
<>
|
|
<DropdownMenuLabel>{copy.title}</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuRadioGroup
|
|
onValueChange={value => {
|
|
void setApprovalModeForProfile(requestGateway, profile, value as ApprovalMode).catch(() => undefined)
|
|
}}
|
|
value={mode}
|
|
>
|
|
{(['manual', 'smart', 'off'] as const).map(value => (
|
|
<DropdownMenuRadioItem className="items-start gap-2" key={value} value={value}>
|
|
<span className="flex min-w-0 flex-col gap-0.5">
|
|
<span className="text-xs text-foreground">{labels[value]}</span>
|
|
<span className="text-[0.6875rem] leading-snug text-(--ui-text-tertiary)">{descriptions[value]}</span>
|
|
</span>
|
|
</DropdownMenuRadioItem>
|
|
))}
|
|
</DropdownMenuRadioGroup>
|
|
</>
|
|
),
|
|
title: copy.ariaLabel(labels[mode]),
|
|
variant: 'menu'
|
|
}
|
|
}
|