mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-14 14:12:44 +00:00
feat(pets): polish generate flow and reduce hatch CPU pressure
Ship the final pet-generation UX polish (provider picker behavior, step-2 cancel flow, banner integration, and visual consistency) and make saturated-chroma background removal C-op driven so hatch processing no longer hammers the machine during long runs.
This commit is contained in:
parent
b674f7ba28
commit
1fe013ee16
35 changed files with 2013 additions and 729 deletions
|
|
@ -35,16 +35,98 @@ function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Dial
|
|||
)
|
||||
}
|
||||
|
||||
type DialogBannerTone = 'error' | 'warn' | 'info'
|
||||
|
||||
// Tinted, edge-to-edge bottom banner per tone. Error/warn keep their semantic
|
||||
// destructive/primary tokens; info derives from the dialog's own bubble
|
||||
// background so it reads as part of the themed dialog — lifted 30% toward white
|
||||
// in light mode, deepened 20% toward black in dark mode.
|
||||
const DIALOG_BANNER_TONES: Record<DialogBannerTone, string> = {
|
||||
error: 'bg-destructive/12 text-destructive',
|
||||
warn: 'bg-primary/12 text-primary',
|
||||
info: 'bg-[color-mix(in_srgb,var(--ui-chat-bubble-background),white_30%)] text-[color-mix(in_srgb,var(--ui-chat-bubble-background),black_60%)] dark:bg-[color-mix(in_srgb,var(--ui-chat-bubble-background),black_20%)] dark:text-[color-mix(in_srgb,var(--ui-chat-bubble-background),white_60%)]'
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
fitContent = false,
|
||||
banner,
|
||||
bannerTone = 'error',
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean
|
||||
// Size the dialog to its content (capped at the viewport) instead of the
|
||||
// default fixed `max-w-lg`. For content that has no intrinsic width (grids,
|
||||
// full-width inputs) pair it with a `min-w-*` in `className`.
|
||||
fitContent?: boolean
|
||||
// A dialog-level notice rendered as a banner flush to the bottom edge (tinted,
|
||||
// inherited bottom radius) so it reads as part of the dialog, not a floating
|
||||
// alert. Falsy → no banner. Tone picks the colour.
|
||||
banner?: React.ReactNode
|
||||
bannerTone?: DialogBannerTone
|
||||
}) {
|
||||
const { t } = useI18n()
|
||||
|
||||
const widthClass = fitContent ? 'w-auto max-w-[92vw]' : 'w-full max-w-lg'
|
||||
|
||||
const closeButton = showCloseButton ? (
|
||||
<DialogPrimitive.Close asChild data-slot="dialog-close-button">
|
||||
<Button
|
||||
aria-label={t.common.close}
|
||||
className="absolute right-2.5 top-2.5 z-20 text-(--ui-text-tertiary) hover:bg-(--chrome-action-hover) hover:text-foreground"
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
>
|
||||
<X className="size-4" />
|
||||
<span className="sr-only">{t.common.close}</span>
|
||||
</Button>
|
||||
</DialogPrimitive.Close>
|
||||
) : null
|
||||
|
||||
// With a banner, the border can't live on the scroll/clip box (it would draw a
|
||||
// line around the banner too). The white body keeps its own bottom radius and
|
||||
// sits over the tinted footer; the outer shell only clips the banner to the
|
||||
// dialog's rounded bottom edge.
|
||||
if (banner) {
|
||||
return (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
className={cn(
|
||||
'fixed left-1/2 top-1/2 z-[130] pointer-events-auto flex max-h-[85vh] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-(--ui-chat-bubble-background) text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
widthClass,
|
||||
className,
|
||||
// Callers often pass `gap-*` for the no-banner grid layout — suppress
|
||||
// it here so the banner can tuck under the body's rounded bottom edge.
|
||||
'gap-0'
|
||||
)}
|
||||
data-slot="dialog-content"
|
||||
{...props}
|
||||
>
|
||||
{/* Scroll lives on an inner box so this shell keeps a painted bottom radius. */}
|
||||
<div className="relative z-10 overflow-hidden rounded-xl border border-b-0 border-(--stroke-nous) bg-(--ui-chat-bubble-background)">
|
||||
<div className="grid max-h-[calc(85vh-5rem)] min-h-0 gap-3 overflow-y-auto p-4">{children}</div>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
// Overlap by one corner radius so the white bottom lobes read clearly
|
||||
// over the tint instead of meeting it on a straight seam.
|
||||
'relative z-0 -mt-[var(--radius-xl)] px-4 pb-2.5 pt-[calc(var(--radius-xl)+0.625rem)] text-center text-[length:var(--conversation-tool-font-size)] leading-relaxed shadow-[inset_0_7px_7px_-4px_rgb(0_0_0/0.28)]',
|
||||
DIALOG_BANNER_TONES[bannerTone]
|
||||
)}
|
||||
data-slot="dialog-banner"
|
||||
role={bannerTone === 'error' ? 'alert' : 'status'}
|
||||
>
|
||||
{banner}
|
||||
</div>
|
||||
{closeButton}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
|
|
@ -53,26 +135,15 @@ function DialogContent({
|
|||
// Cap height at 85vh and let long content scroll inside the dialog
|
||||
// instead of overflowing off-screen (long cron titles, tool detail
|
||||
// dumps, etc.). Individual dialogs can still override via className.
|
||||
'fixed left-1/2 top-1/2 z-[130] pointer-events-auto grid max-h-[85vh] w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-3 overflow-y-auto rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) p-4 text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'fixed left-1/2 top-1/2 z-[130] pointer-events-auto grid max-h-[85vh] -translate-x-1/2 -translate-y-1/2 gap-3 overflow-y-auto rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) p-4 text-[length:var(--conversation-text-font-size)] text-foreground shadow-nous duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
widthClass,
|
||||
className
|
||||
)}
|
||||
data-slot="dialog-content"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close asChild data-slot="dialog-close-button">
|
||||
<Button
|
||||
aria-label={t.common.close}
|
||||
className="absolute right-2.5 top-2.5 text-(--ui-text-tertiary) hover:bg-(--chrome-action-hover) hover:text-foreground"
|
||||
size="icon-xs"
|
||||
variant="ghost"
|
||||
>
|
||||
<X className="size-4" />
|
||||
<span className="sr-only">{t.common.close}</span>
|
||||
</Button>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
{closeButton}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue