mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(desktop): stop tip-wrapping kebab menu triggers
Remove the ActionsMenu tooltip prop and Tip wrappers on session, project, workspace, panel, and credential ⋯ menus. aria-label stays for a11y; drop the unused credentialActions string.
This commit is contained in:
parent
58714e0e2a
commit
26413bbf57
13 changed files with 19 additions and 55 deletions
|
|
@ -19,7 +19,6 @@ import {
|
|||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { $panesFlipped, dismissAutoProject } from '@/store/layout'
|
||||
|
|
@ -192,13 +191,9 @@ export function ProjectMenu({
|
|||
</DropdownMenuItem>
|
||||
)
|
||||
|
||||
// The bare trigger button (no Tip, no anchor) — composed with whichever of
|
||||
// Tip / PopoverAnchor apply below, always OUTSIDE the asChild chain that
|
||||
// ends at this button, never wrapping it directly. asChild clones only its
|
||||
// immediate child, so any of these wrappers placed inside another
|
||||
// asChild-consuming component (instead of around it) would have its
|
||||
// injected props silently swallowed by that inner component instead of
|
||||
// reaching the real DOM button (see #67500).
|
||||
// When anchorRef is absent, PopoverAnchor wraps the trigger so the
|
||||
// appearance popover positions against this button. Keep asChild chains free
|
||||
// of non-forwarding wrappers (#67500).
|
||||
const triggerButton = (
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
|
|
@ -217,16 +212,7 @@ export function ProjectMenu({
|
|||
</DropdownMenuTrigger>
|
||||
)
|
||||
|
||||
// Tip always wraps the outermost element of whatever we render — either the
|
||||
// trigger directly (anchorRef present: the popover anchors to the whole row
|
||||
// via a separate virtualRef, so PopoverAnchor isn't involved here), or the
|
||||
// PopoverAnchor-wrapped trigger (anchorRef absent: the popover anchors to
|
||||
// this button itself). Either way, Tip > PopoverAnchor > DropdownMenuTrigger
|
||||
// > button, so asChild composes props/ref all the way down to the real DOM
|
||||
// node instead of stopping at an intermediate wrapper.
|
||||
const trigger = (
|
||||
<Tip label={p.menu}>{anchorRef ? triggerButton : <PopoverAnchor asChild>{triggerButton}</PopoverAnchor>}</Tip>
|
||||
)
|
||||
const trigger = anchorRef ? triggerButton : <PopoverAnchor asChild>{triggerButton}</PopoverAnchor>
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={setAppearanceOpen} open={appearanceOpen}>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export function WorkspaceMenu({ path, onRemove }: { path: null | string; onRemov
|
|||
const items = useWorkspaceItems({ onRemove, path })
|
||||
|
||||
return (
|
||||
<ActionsMenu ariaLabel={p.menu} contentClassName="w-48" items={items} tooltip={p.menu}>
|
||||
<ActionsMenu ariaLabel={p.menu} contentClassName="w-48" items={items}>
|
||||
<button
|
||||
aria-label={p.menu}
|
||||
className="grid size-4 shrink-0 place-items-center rounded-sm bg-transparent text-(--ui-text-quaternary) opacity-0 transition-opacity hover:bg-(--ui-control-hover-background) hover:text-foreground group-hover/workspace:opacity-100 data-[state=open]:opacity-100"
|
||||
|
|
|
|||
|
|
@ -388,13 +388,10 @@ function useSessionActions({
|
|||
interface SessionActionsMenuProps
|
||||
extends SessionActions, Pick<React.ComponentProps<typeof ActionsMenu>, 'align' | 'sideOffset'> {
|
||||
children: React.ReactNode
|
||||
/** Tooltip label for the trigger. */
|
||||
tooltip?: React.ReactNode
|
||||
}
|
||||
|
||||
export function SessionActionsMenu({
|
||||
children,
|
||||
tooltip,
|
||||
align = 'end',
|
||||
sideOffset = 6,
|
||||
...actions
|
||||
|
|
@ -410,7 +407,6 @@ export function SessionActionsMenu({
|
|||
contentClassName="w-40"
|
||||
items={renderItems}
|
||||
sideOffset={sideOffset}
|
||||
tooltip={tooltip}
|
||||
>
|
||||
{children}
|
||||
</ActionsMenu>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ export function SidebarSessionRow({
|
|||
profile={session.profile}
|
||||
sessionId={session.id}
|
||||
title={title}
|
||||
tooltip={r.actionsFor(title)}
|
||||
>
|
||||
<Button
|
||||
aria-label={r.actionsFor(title)}
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ export function PanelRowMenu({ items, label = 'Actions' }: { items: PanelMenuIte
|
|||
}
|
||||
|
||||
return (
|
||||
<ActionsMenu ariaLabel={label} contentClassName="w-40" items={renderPanelMenuItems(items)} tooltip={label}>
|
||||
<ActionsMenu ariaLabel={label} contentClassName="w-40" items={renderPanelMenuItems(items)}>
|
||||
<Button
|
||||
aria-label={label}
|
||||
className="size-5 rounded-[4px] bg-transparent text-(--ui-text-tertiary) opacity-0 transition-colors duration-100 hover:bg-(--ui-control-active-background) hover:text-foreground focus-visible:opacity-100 focus-visible:ring-0 group-hover/row:opacity-100 data-[state=open]:bg-(--ui-control-active-background) data-[state=open]:text-foreground data-[state=open]:opacity-100 [&_svg]:size-3.5!"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
} from '@/components/ui/actions-menu'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { ExternalLink, Eye, EyeOff, KeyRound, Trash2 } from '@/lib/icons'
|
||||
|
|
@ -174,16 +173,14 @@ export function EnvVarActionsTrigger({ className, label, ...props }: EnvVarActio
|
|||
const copy = t.settings.envActions
|
||||
|
||||
return (
|
||||
<Tip label={copy.credentialActions}>
|
||||
<Button
|
||||
aria-label={copy.actionsFor(label)}
|
||||
className={cn('text-muted-foreground hover:text-foreground', className)}
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
{...props}
|
||||
>
|
||||
<Codicon name="ellipsis" size="0.875rem" />
|
||||
</Button>
|
||||
</Tip>
|
||||
<Button
|
||||
aria-label={copy.actionsFor(label)}
|
||||
className={cn('text-muted-foreground hover:text-foreground', className)}
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
{...props}
|
||||
>
|
||||
<Codicon name="ellipsis" size="0.875rem" />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import {
|
|||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
|
||||
// One place to define a set of actions and get BOTH a kebab dropdown and a
|
||||
// matching right-click context menu — so a row's ⋯ menu and its right-click menu
|
||||
|
|
@ -108,15 +107,14 @@ interface ActionsMenuProps extends Pick<
|
|||
items: (kit: MenuKit) => React.ReactNode
|
||||
ariaLabel?: string
|
||||
contentClassName?: string
|
||||
/** Optional tooltip on the trigger (composed INSIDE the asChild chain). */
|
||||
tooltip?: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* A kebab dropdown menu. Pair it with `ActionsContextMenu` using the same
|
||||
* `items` render function so the two menus stay identical.
|
||||
* `items` render function so the two menus stay identical. No tip on the
|
||||
* trigger — `aria-label` on the button is enough (see DESIGN.md).
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
align = 'end',
|
||||
|
|
@ -127,17 +125,11 @@ export function ActionsMenu({
|
|||
onOpenChange,
|
||||
open,
|
||||
side,
|
||||
sideOffset = 6,
|
||||
tooltip
|
||||
sideOffset = 6
|
||||
}: ActionsMenuProps) {
|
||||
// Tip wraps the trigger, not the reverse: Tip doesn't forward the ref/props an
|
||||
// `asChild` clone injects, so a Tip placed as the trigger's child silently
|
||||
// drops onClick/aria-haspopup and the menu stops opening (#67500).
|
||||
const trigger = <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
||||
|
||||
return (
|
||||
<DropdownMenu onOpenChange={onOpenChange} open={open}>
|
||||
{tooltip ? <Tip label={tooltip}>{trigger}</Tip> : trigger}
|
||||
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align={align}
|
||||
aria-label={ariaLabel}
|
||||
|
|
|
|||
|
|
@ -640,7 +640,6 @@ export const ar = defineLocale({
|
|||
},
|
||||
envActions: {
|
||||
actionsFor: label => `إجراءات ${label}`,
|
||||
credentialActions: 'إجراءات بيانات الاعتماد',
|
||||
docs: 'الوثائق',
|
||||
hideValue: 'إخفاء القيمة',
|
||||
revealValue: 'إظهار القيمة',
|
||||
|
|
|
|||
|
|
@ -574,7 +574,6 @@ export const en: Translations = {
|
|||
},
|
||||
envActions: {
|
||||
actionsFor: label => `Actions for ${label}`,
|
||||
credentialActions: 'Credential actions',
|
||||
manageInKeys: 'Manage in API Keys',
|
||||
docs: 'Docs',
|
||||
hideValue: 'Hide value',
|
||||
|
|
|
|||
|
|
@ -672,7 +672,6 @@ export const ja = defineLocale({
|
|||
},
|
||||
envActions: {
|
||||
actionsFor: label => `${label} のアクション`,
|
||||
credentialActions: '認証情報のアクション',
|
||||
manageInKeys: 'API キーで管理',
|
||||
docs: 'ドキュメント',
|
||||
hideValue: '値を非表示',
|
||||
|
|
|
|||
|
|
@ -477,7 +477,6 @@ export interface Translations {
|
|||
}
|
||||
envActions: {
|
||||
actionsFor: (label: string) => string
|
||||
credentialActions: string
|
||||
manageInKeys: string
|
||||
docs: string
|
||||
hideValue: string
|
||||
|
|
|
|||
|
|
@ -659,7 +659,6 @@ export const zhHant = defineLocale({
|
|||
},
|
||||
envActions: {
|
||||
actionsFor: label => `${label} 的動作`,
|
||||
credentialActions: '憑證動作',
|
||||
manageInKeys: '在 API 金鑰中管理',
|
||||
docs: '文件',
|
||||
hideValue: '隱藏值',
|
||||
|
|
|
|||
|
|
@ -780,7 +780,6 @@ export const zh: Translations = {
|
|||
},
|
||||
envActions: {
|
||||
actionsFor: label => `${label} 的操作`,
|
||||
credentialActions: '凭据操作',
|
||||
manageInKeys: '在 API 密钥中管理',
|
||||
docs: '文档',
|
||||
hideValue: '隐藏值',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue