refactor(webui): unify shared shape system

This commit is contained in:
Xubin Ren
2026-08-14 19:55:25 +09:00
parent 7c04af86f9
commit 60993597de
56 changed files with 305 additions and 219 deletions
@@ -59,7 +59,7 @@ export function FeishuAssistantsPanel({
/> />
), ),
footer: ( footer: (
<div className="mt-4 overflow-hidden rounded-[16px] border border-border/70 bg-background px-4 py-4"> <div className="mt-4 overflow-hidden rounded-floating border border-border/70 bg-background px-4 py-4">
<div className="text-[13px] font-semibold text-foreground"> <div className="text-[13px] font-semibold text-foreground">
{tx("custom.createAnother", "Create another assistant")} {tx("custom.createAnother", "Create another assistant")}
</div> </div>
@@ -144,7 +144,7 @@ function FeishuInstanceAction({
</Button> </Button>
</div> </div>
{error ? ( {error ? (
<div className="mt-3 rounded-[12px] border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive"> <div className="mt-3 rounded-control border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}
@@ -197,7 +197,7 @@ export function WeixinPanel({
}); });
return ( return (
<aside className="min-h-full rounded-[20px] bg-settings-surface p-5"> <aside className="min-h-full rounded-panel bg-settings-surface p-5">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex min-w-0 items-start gap-3"> <div className="flex min-w-0 items-start gap-3">
<WeixinLogo showBrandLogos={showBrandLogos} /> <WeixinLogo showBrandLogos={showBrandLogos} />
@@ -251,7 +251,7 @@ export function WeixinPanel({
</div> </div>
{runtimeError ? ( {runtimeError ? (
<div className="mt-4 rounded-[12px] border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive"> <div className="mt-4 rounded-control border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive">
{runtimeError} {runtimeError}
</div> </div>
) : null} ) : null}
@@ -304,7 +304,7 @@ export function WeixinPanel({
{saveError ? ( {saveError ? (
<div <div
role="alert" role="alert"
className="rounded-[12px] border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive" className="rounded-control border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive"
> >
{saveError} {saveError}
</div> </div>
@@ -413,7 +413,7 @@ function WeixinLogo({ showBrandLogos }: { showBrandLogos: boolean }) {
const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(logoUrls); const { logoUrl, onLogoError, onLogoLoad } = useLogoFallback(logoUrls);
if (showBrandLogos && logoUrl) { if (showBrandLogos && logoUrl) {
return ( return (
<span className="grid h-10 w-10 shrink-0 place-items-center rounded-[12px] bg-background"> <span className="grid h-10 w-10 shrink-0 place-items-center rounded-control bg-background">
<img <img
src={logoUrl} src={logoUrl}
alt="" alt=""
@@ -428,7 +428,7 @@ function WeixinLogo({ showBrandLogos }: { showBrandLogos: boolean }) {
} }
return ( return (
<span <span
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[12px] bg-background text-[11px] font-bold" className="flex h-10 w-10 shrink-0 items-center justify-center rounded-control bg-background text-[11px] font-bold"
style={{ color: "#07C160" }} style={{ color: "#07C160" }}
aria-hidden aria-hidden
> >
+1 -1
View File
@@ -563,7 +563,7 @@ function PairingCodePopup({
aria-label={t("app.pairing.title", { defaultValue: "Pair a chat user" })} aria-label={t("app.pairing.title", { defaultValue: "Pair a chat user" })}
className={cn( className={cn(
"fixed right-4 top-[calc(0.75rem+env(safe-area-inset-top))] z-[70]", "fixed right-4 top-[calc(0.75rem+env(safe-area-inset-top))] z-[70]",
"w-[min(calc(100vw-2rem),24rem)] rounded-[24px]", "w-[min(calc(100vw-2rem),24rem)] rounded-modal",
floatingSurfaceElevationClassName, floatingSurfaceElevationClassName,
"p-4", "p-4",
"animate-in fade-in-0 slide-in-from-top-2 duration-200", "animate-in fade-in-0 slide-in-from-top-2 duration-200",
+3 -3
View File
@@ -94,7 +94,7 @@ export function AttachmentTile({ attachment, className, inline = false, variant
title={attachment.name ?? undefined} title={attachment.name ?? undefined}
aria-label={label} aria-label={label}
className={cn( className={cn(
"flex max-w-[18rem] items-center gap-2 rounded-[14px]", "flex max-w-[18rem] items-center gap-2 rounded-control",
"border border-border/60 bg-muted/40 px-3 py-2 text-xs text-muted-foreground", "border border-border/60 bg-muted/40 px-3 py-2 text-xs text-muted-foreground",
"transition-colors hover:bg-muted/55 hover:text-foreground", "transition-colors hover:bg-muted/55 hover:text-foreground",
variant === "compact" && "max-w-[14rem] rounded-xl px-2.5 py-1.5 text-[11.5px]", variant === "compact" && "max-w-[14rem] rounded-xl px-2.5 py-1.5 text-[11.5px]",
@@ -109,7 +109,7 @@ export function AttachmentTile({ attachment, className, inline = false, variant
return ( return (
<div <div
className={cn( className={cn(
"flex max-w-[18rem] items-center gap-2 rounded-[14px]", "flex max-w-[18rem] items-center gap-2 rounded-control",
"border border-border/60 bg-muted/35 px-3 py-2 text-xs text-muted-foreground", "border border-border/60 bg-muted/35 px-3 py-2 text-xs text-muted-foreground",
variant === "compact" && "max-w-[14rem] rounded-xl px-2.5 py-1.5 text-[11.5px]", variant === "compact" && "max-w-[14rem] rounded-xl px-2.5 py-1.5 text-[11.5px]",
className, className,
@@ -139,7 +139,7 @@ function AttachmentFrame({
variant?: "default" | "compact"; variant?: "default" | "compact";
}) { }) {
const frameClassName = cn( const frameClassName = cn(
"not-prose my-3 block w-fit max-w-full overflow-hidden rounded-[14px]", "not-prose my-3 block w-fit max-w-full overflow-hidden rounded-control",
"border border-border/60 bg-muted/40", "border border-border/60 bg-muted/40",
attachment.kind === "image" && "bg-background/85", attachment.kind === "image" && "bg-background/85",
attachment.kind === "video" ? "w-[min(100%,32rem)]" : "", attachment.kind === "video" ? "w-[min(100%,32rem)]" : "",
+3 -3
View File
@@ -807,7 +807,7 @@ export const ChatList = memo(function ChatList({
actionMenus.openFromContextMenu(event, actionMenuId) actionMenus.openFromContextMenu(event, actionMenuId)
)} )}
className={cn( className={cn(
"group flex min-w-0 max-w-full items-center gap-1 rounded-[0.65rem] px-2 text-[13px]", "group flex min-w-0 max-w-full items-center gap-1 rounded-control px-2 text-[13px]",
SIDEBAR_SELECTION_ITEM_CLASS, SIDEBAR_SELECTION_ITEM_CLASS,
compact ? "min-h-7" : "min-h-8", compact ? "min-h-7" : "min-h-8",
topicActive topicActive
@@ -1081,7 +1081,7 @@ function WorkbenchTabHeader({
data-workbench-tab data-workbench-tab
onContextMenu={(event) => actionMenus.openFromContextMenu(event, actionMenuId)} onContextMenu={(event) => actionMenus.openFromContextMenu(event, actionMenuId)}
className={cn( className={cn(
"group/tab flex min-w-0 items-center gap-0.5 rounded-[0.65rem] px-1.5 text-sidebar-foreground/85", "group/tab flex min-w-0 items-center gap-0.5 rounded-control px-1.5 text-sidebar-foreground/85",
collapsed ? "min-h-6" : "min-h-7", collapsed ? "min-h-6" : "min-h-7",
)} )}
> >
@@ -1281,7 +1281,7 @@ function ActivePaneRows({
actionMenus.openFromContextMenu(event, actionMenuId) actionMenus.openFromContextMenu(event, actionMenuId)
)} )}
className={cn( className={cn(
"group/pane flex min-w-0 max-w-full items-center gap-1 rounded-[0.65rem] px-2 text-[13px]", "group/pane flex min-w-0 max-w-full items-center gap-1 rounded-control px-2 text-[13px]",
SIDEBAR_SELECTION_ITEM_CLASS, SIDEBAR_SELECTION_ITEM_CLASS,
compact ? "min-h-7" : "min-h-8", compact ? "min-h-7" : "min-h-8",
active active
+2 -2
View File
@@ -203,7 +203,7 @@ export function CliAppMentionToken({
<span <span
data-testid={`${testIdPrefix}-cli-mention-logo-${app.name}`} data-testid={`${testIdPrefix}-cli-mention-logo-${app.name}`}
className={cn( className={cn(
"absolute left-1/2 top-1/2 grid place-items-center overflow-hidden rounded-[3px]", "absolute left-1/2 top-1/2 grid place-items-center overflow-hidden rounded-mark",
"-translate-x-1/2 -translate-y-1/2", "-translate-x-1/2 -translate-y-1/2",
isHero ? "h-[0.74em] w-[0.74em]" : "h-[0.72em] w-[0.72em]", isHero ? "h-[0.74em] w-[0.74em]" : "h-[0.72em] w-[0.72em]",
)} )}
@@ -260,7 +260,7 @@ export function McpPresetMentionToken({
<span <span
data-testid={`${testIdPrefix}-mcp-mention-logo-${preset.name}`} data-testid={`${testIdPrefix}-mcp-mention-logo-${preset.name}`}
className={cn( className={cn(
"absolute left-1/2 top-1/2 grid place-items-center overflow-hidden rounded-[3px]", "absolute left-1/2 top-1/2 grid place-items-center overflow-hidden rounded-mark",
"-translate-x-1/2 -translate-y-1/2", "-translate-x-1/2 -translate-y-1/2",
isHero ? "h-[0.74em] w-[0.74em]" : "h-[0.72em] w-[0.72em]", isHero ? "h-[0.74em] w-[0.74em]" : "h-[0.72em] w-[0.72em]",
)} )}
+1 -1
View File
@@ -202,7 +202,7 @@ export function CodeBlock({
<div <div
className={cn( className={cn(
"not-prose relative overflow-hidden", "not-prose relative overflow-hidden",
hasChrome && "rounded-[18px] bg-secondary/70", hasChrome && "rounded-floating bg-secondary/70",
className, className,
)} )}
data-language={language || t("code.fallbackLanguage")} data-language={language || t("code.fallbackLanguage")}
+3 -3
View File
@@ -96,16 +96,16 @@ export function DeleteConfirm({
</div> </div>
) : null} ) : null}
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter className="mt-7 !grid grid-cols-1 gap-3 space-x-0 sm:grid-cols-2 sm:space-x-0"> <AlertDialogFooter className="mt-6 !grid grid-cols-1 gap-2 sm:grid-cols-2">
<AlertDialogCancel <AlertDialogCancel
onClick={onCancel} onClick={onCancel}
className="mt-0 h-11 w-full min-w-0 rounded-full border-0 bg-muted/70 px-5 text-[15px] font-semibold text-foreground shadow-none hover:bg-muted" className="mt-0 w-full min-w-0"
> >
{t("deleteConfirm.cancel")} {t("deleteConfirm.cancel")}
</AlertDialogCancel> </AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={onConfirm} onClick={onConfirm}
className="h-11 w-full min-w-0 !whitespace-normal rounded-full bg-destructive px-5 text-center text-[15px] font-semibold text-destructive-foreground shadow-none hover:bg-destructive/90" className="w-full min-w-0 !whitespace-normal bg-destructive text-center text-destructive-foreground hover:bg-destructive/90"
> >
{hasAutomations {hasAutomations
? t("deleteConfirm.confirmWithAutomations") ? t("deleteConfirm.confirmWithAutomations")
+1 -1
View File
@@ -183,7 +183,7 @@ export function FilePreviewPanel({
) : null} ) : null}
<span <span
className={cn( className={cn(
"min-w-0 truncate rounded-[4px] px-1 py-0.5", "min-w-0 truncate rounded-mark px-1 py-0.5",
isLast isLast
? "font-medium text-foreground" ? "font-medium text-foreground"
: "max-w-[26vw] shrink text-muted-foreground/78", : "max-w-[26vw] shrink text-muted-foreground/78",
+2 -2
View File
@@ -78,7 +78,7 @@ export function FileReferenceChip({
"text-sky-600 transition-colors hover:text-sky-700", "text-sky-600 transition-colors hover:text-sky-700",
"dark:text-sky-300 dark:hover:text-sky-200", "dark:text-sky-300 dark:hover:text-sky-200",
interactive && [ interactive && [
"cursor-pointer rounded-[5px]", "cursor-pointer rounded-compact",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-400/45", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-400/45",
], ],
)} )}
@@ -110,7 +110,7 @@ export function FileReferenceChip({
sideOffset={8} sideOffset={8}
collisionPadding={12} collisionPadding={12}
className={cn( className={cn(
"max-w-[min(38rem,calc(100vw-2rem))] rounded-[10px]", "max-w-[min(38rem,calc(100vw-2rem))] rounded-control",
"px-2.5 py-1.5", "px-2.5 py-1.5",
"break-all font-mono text-[11px] leading-snug text-popover-foreground", "break-all font-mono text-[11px] leading-snug text-popover-foreground",
)} )}
+1 -1
View File
@@ -120,7 +120,7 @@ export function ImageLightbox({
alt={current.name ?? ""} alt={current.name ?? ""}
decoding="async" decoding="async"
draggable={false} draggable={false}
className="max-h-[92vh] max-w-[94vw] select-none rounded-[6px] object-contain shadow-2xl" className="max-h-[92vh] max-w-[94vw] select-none rounded-compact object-contain shadow-2xl"
/> />
</div> </div>
@@ -450,7 +450,7 @@ function InlineLinkPreviewRow({ link }: { link: InlineLinkPreview }) {
> >
<span <span
className={cn( className={cn(
"relative grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px]", "relative grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-mark",
"border border-border/65 bg-background text-muted-foreground", "border border-border/65 bg-background text-muted-foreground",
)} )}
aria-hidden aria-hidden
@@ -459,7 +459,7 @@ function InlineLinkPreviewRow({ link }: { link: InlineLinkPreview }) {
<img <img
src={favicon} src={favicon}
alt="" alt=""
className="h-3 w-3 rounded-[2px] object-contain" className="h-3 w-3 rounded-mark object-contain"
decoding="async" decoding="async"
loading="lazy" loading="lazy"
referrerPolicy="no-referrer" referrerPolicy="no-referrer"
@@ -745,7 +745,7 @@ export default function MarkdownTextRenderer({
}, },
mark({ children: markdownChildren }) { mark({ children: markdownChildren }) {
return ( return (
<mark className="rounded-[5px] bg-yellow-200/75 px-1 py-0.5 text-inherit dark:bg-yellow-300/25"> <mark className="rounded-compact bg-yellow-200/75 px-1 py-0.5 text-inherit dark:bg-yellow-300/25">
{markdownChildren} {markdownChildren}
</mark> </mark>
); );
+4 -4
View File
@@ -331,7 +331,7 @@ export function MessageBubble({
<p <p
data-temporary-message={temporary ? "true" : undefined} data-temporary-message={temporary ? "true" : undefined}
className={cn( className={cn(
"ml-auto w-fit max-w-full min-w-0 rounded-[18px] px-4 py-2", "ml-auto w-fit max-w-full min-w-0 rounded-floating px-4 py-2",
"text-left text-[16px]/[1.75] whitespace-pre-wrap [overflow-wrap:anywhere]", "text-left text-[16px]/[1.75] whitespace-pre-wrap [overflow-wrap:anywhere]",
temporary temporary
? "border border-dashed border-muted-foreground/40 bg-transparent" ? "border border-dashed border-muted-foreground/40 bg-transparent"
@@ -499,7 +499,7 @@ function UserQuotedContext({ text, label }: { text: string; label: string }) {
return ( return (
<blockquote <blockquote
className={cn( className={cn(
"ml-auto flex w-fit max-w-full min-w-0 items-start gap-2 rounded-[14px]", "ml-auto flex w-fit max-w-full min-w-0 items-start gap-2 rounded-control",
"border border-border/60 bg-muted/35 px-3 py-2 text-left text-muted-foreground", "border border-border/60 bg-muted/35 px-3 py-2 text-left text-muted-foreground",
)} )}
aria-label={label} aria-label={label}
@@ -718,8 +718,8 @@ function UserImageCell({
const tileClasses = cn( const tileClasses = cn(
"relative overflow-hidden border border-border/60 bg-muted/40", "relative overflow-hidden border border-border/60 bg-muted/40",
size === "large" size === "large"
? "w-[min(100%,34rem)] rounded-[20px] bg-transparent" ? "w-[min(100%,34rem)] rounded-panel bg-transparent"
: "h-24 w-24 rounded-[14px]", : "h-24 w-24 rounded-control",
"shadow-[0_6px_18px_-14px_rgba(0,0,0,0.45)]", "shadow-[0_6px_18px_-14px_rgba(0,0,0,0.45)]",
); );
+8 -11
View File
@@ -44,41 +44,38 @@ export function RenameChatDialog({
<Dialog open={open} onOpenChange={(next) => { <Dialog open={open} onOpenChange={(next) => {
if (!next) onCancel(); if (!next) onCancel();
}}> }}>
<DialogContent className="max-w-sm rounded-[20px] p-5 sm:p-6"> <DialogContent className="max-w-sm">
<form <form
className="grid gap-5" className="grid gap-4"
onSubmit={(event) => { onSubmit={(event) => {
event.preventDefault(); event.preventDefault();
if (!trimmed) return; if (!trimmed) return;
onConfirm(trimmed); onConfirm(trimmed);
}} }}
> >
<DialogHeader className="space-y-1 text-left"> <DialogHeader className="text-left">
<DialogTitle className="text-xl leading-tight"> <DialogTitle>{dialogTitle ?? t("chat.renameTitle")}</DialogTitle>
{dialogTitle ?? t("chat.renameTitle")} <DialogDescription>
</DialogTitle>
<DialogDescription className="leading-relaxed">
{description ?? t("chat.renameDescription")} {description ?? t("chat.renameDescription")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<Input <Input
className="h-11 rounded-xl border-border/80 bg-muted/20 px-3.5 text-[15px]"
value={value} value={value}
onChange={(event) => setValue(event.target.value)} onChange={(event) => setValue(event.target.value)}
placeholder={placeholder ?? t("chat.renamePlaceholder")} placeholder={placeholder ?? t("chat.renamePlaceholder")}
autoFocus autoFocus
maxLength={160} maxLength={160}
/> />
<DialogFooter className="gap-2 pt-1 sm:space-x-0"> <DialogFooter>
<Button <Button
className="min-w-20 rounded-xl" className="min-w-20"
type="button" type="button"
variant="outline" variant="outline"
onClick={onCancel} onClick={onCancel}
> >
{t("deleteConfirm.cancel")} {t("deleteConfirm.cancel")}
</Button> </Button>
<Button className="min-w-20 rounded-xl" type="submit" disabled={!trimmed}> <Button className="min-w-20" type="submit" disabled={!trimmed}>
{t("chat.renameSave")} {t("chat.renameSave")}
</Button> </Button>
</DialogFooter> </DialogFooter>
+1 -2
View File
@@ -120,7 +120,6 @@ export function SessionSearchDialog({
showCloseButton={false} showCloseButton={false}
className={cn( className={cn(
"flex max-h-[min(40rem,calc(100vh-2rem))] w-[calc(100vw-2rem)] max-w-[42rem] flex-col gap-0 overflow-hidden p-0", "flex max-h-[min(40rem,calc(100vh-2rem))] w-[calc(100vw-2rem)] max-w-[42rem] flex-col gap-0 overflow-hidden p-0",
"rounded-[22px]",
)} )}
> >
<DialogTitle className="sr-only">{t("sidebar.searchAria")}</DialogTitle> <DialogTitle className="sr-only">{t("sidebar.searchAria")}</DialogTitle>
@@ -183,7 +182,7 @@ export function SessionSearchDialog({
onMouseEnter={() => setHighlightedIndex(index)} onMouseEnter={() => setHighlightedIndex(index)}
aria-current={active ? "page" : undefined} aria-current={active ? "page" : undefined}
className={cn( className={cn(
"grid min-h-[54px] w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-[11px] px-3 py-2 text-left transition-colors", "grid min-h-[54px] w-full min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-control px-3 py-2 text-left transition-colors",
highlighted highlighted
? "bg-muted text-foreground" ? "bg-muted text-foreground"
: "text-foreground hover:bg-muted", : "text-foreground hover:bg-muted",
@@ -621,7 +621,7 @@ export function SettingsPage({
) : null} ) : null}
{loading ? ( {loading ? (
<div className="flex h-48 items-center justify-center rounded-[22px] bg-settings-surface text-sm text-muted-foreground"> <div className="flex h-48 items-center justify-center rounded-panel bg-settings-surface text-sm text-muted-foreground">
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className="mr-2 h-4 w-4 animate-spin" />
{t("settings.status.loading")} {t("settings.status.loading")}
</div> </div>
@@ -640,7 +640,7 @@ export function SettingsPage({
)} )}
> >
{error ? ( {error ? (
<div className="rounded-[18px] border border-destructive/20 bg-destructive/5 px-4 py-3 text-[13px] text-destructive"> <div className="rounded-floating border border-destructive/20 bg-destructive/5 px-4 py-3 text-[13px] text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}
@@ -102,7 +102,7 @@ export function SettingsSidebar({
<button <button
type="button" type="button"
aria-label={`${t("settings.sidebar.title")}: ${activeLabel}`} aria-label={`${t("settings.sidebar.title")}: ${activeLabel}`}
className="touch-target flex h-11 w-full items-center gap-2.5 rounded-[14px] bg-sidebar-accent px-3 text-left text-[13px] font-medium text-foreground transition-colors hover:bg-sidebar-accent/80 lg:hidden" className="touch-target flex h-11 w-full items-center gap-2.5 rounded-control bg-sidebar-accent px-3 text-left text-[13px] font-medium text-foreground transition-colors hover:bg-sidebar-accent/80 lg:hidden"
> >
<ActiveIcon className="h-4 w-4 shrink-0" strokeWidth={2} aria-hidden /> <ActiveIcon className="h-4 w-4 shrink-0" strokeWidth={2} aria-hidden />
<span className="min-w-0 flex-1 truncate">{activeLabel}</span> <span className="min-w-0 flex-1 truncate">{activeLabel}</span>
@@ -176,7 +176,7 @@ export function SettingsSidebar({
type="button" type="button"
variant="ghost" variant="ghost"
onClick={onLogout} onClick={onLogout}
className="h-9 w-full justify-start gap-2 rounded-[10px] px-2.5 text-[13px] font-medium text-muted-foreground hover:bg-destructive/8 hover:text-destructive" className="h-9 w-full justify-start gap-2 rounded-control px-2.5 text-[13px] font-medium text-muted-foreground hover:bg-destructive/8 hover:text-destructive"
> >
<LogOut className="h-4 w-4" aria-hidden /> <LogOut className="h-4 w-4" aria-hidden />
{t("app.account.logout")} {t("app.account.logout")}
@@ -98,7 +98,7 @@ export function SkillsCatalogSettings({ skills }: { skills: SkillSummary[] }) {
/> />
{view === "installed" ? ( {view === "installed" ? (
<section className="overflow-hidden rounded-[22px] bg-settings-surface"> <section className="overflow-hidden rounded-panel bg-settings-surface">
<div className="flex flex-col gap-3 px-4 pb-2 pt-4 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-3 px-4 pb-2 pt-4 sm:flex-row sm:items-center sm:justify-between">
<div className="relative w-full sm:max-w-[320px]"> <div className="relative w-full sm:max-w-[320px]">
<Search <Search
@@ -114,7 +114,7 @@ export function SkillsCatalogSettings({ skills }: { skills: SkillSummary[] }) {
aria-label={t("settings.skills.searchInstalled", { aria-label={t("settings.skills.searchInstalled", {
defaultValue: "Search installed skills", defaultValue: "Search installed skills",
})} })}
className="h-9 rounded-[11px] bg-background pl-9 text-[13px]" className="h-9 bg-background pl-9 text-[13px]"
/> />
</div> </div>
<SegmentedControl <SegmentedControl
@@ -220,7 +220,7 @@ function SkillCatalogRow({
})} })}
onClick={() => onSelect(skill)} onClick={() => onSelect(skill)}
className={cn( className={cn(
"group flex w-full min-w-0 items-center gap-3 rounded-[14px] px-2 py-3 text-left", "group flex w-full min-w-0 items-center gap-3 rounded-control px-2 py-3 text-left",
"transition-colors duration-150", "transition-colors duration-150",
"hover:bg-muted/70", "hover:bg-muted/70",
"focus-visible:bg-muted/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "focus-visible:bg-muted/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
@@ -436,7 +436,7 @@ function SkillDetailSheet({
{t("settings.skills.loadingDetail", { defaultValue: "Loading skill details..." })} {t("settings.skills.loadingDetail", { defaultValue: "Loading skill details..." })}
</div> </div>
) : loadFailed ? ( ) : loadFailed ? (
<div className="mt-8 rounded-[16px] bg-destructive/10 px-3 py-3 text-sm text-destructive"> <div className="mt-8 rounded-floating bg-destructive/10 px-3 py-3 text-sm text-destructive">
{t("settings.skills.loadFailed", { defaultValue: "Could not load skill details." })} {t("settings.skills.loadFailed", { defaultValue: "Could not load skill details." })}
</div> </div>
) : ( ) : (
@@ -484,7 +484,7 @@ function SkillDetailSheet({
</div> </div>
{actionError ? ( {actionError ? (
<div className="rounded-[14px] bg-destructive/10 px-3 py-2.5 text-[13px] text-destructive"> <div className="rounded-control bg-destructive/10 px-3 py-2.5 text-[13px] text-destructive">
{actionError} {actionError}
</div> </div>
) : null} ) : null}
@@ -533,7 +533,7 @@ function SkillDetailSheet({
</Sheet> </Sheet>
<AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}> <AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
<AlertDialogContent className="rounded-[20px]"> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle> <AlertDialogTitle>
{t("settings.skills.deleteConfirmTitle", { {t("settings.skills.deleteConfirmTitle", {
@@ -574,7 +574,7 @@ function RawInstructionsBlock({ markdown }: { markdown: string }) {
}); });
return ( return (
<details className="group rounded-[18px] border border-border/45 bg-muted/20 px-3 py-3"> <details className="group rounded-floating border border-border/45 bg-muted/20 px-3 py-3">
<summary className="flex min-h-11 cursor-pointer select-none items-center justify-between gap-3 text-[13px] font-medium text-foreground/90 transition-colors hover:text-foreground"> <summary className="flex min-h-11 cursor-pointer select-none items-center justify-between gap-3 text-[13px] font-medium text-foreground/90 transition-colors hover:text-foreground">
<span> <span>
{t("settings.skills.instructionsTitle", { defaultValue: "Skill instructions" })} {t("settings.skills.instructionsTitle", { defaultValue: "Skill instructions" })}
@@ -583,7 +583,7 @@ function RawInstructionsBlock({ markdown }: { markdown: string }) {
SKILL.md SKILL.md
</code> </code>
</summary> </summary>
<div className="mt-3 overflow-hidden rounded-[14px] border border-border/35 bg-background/70"> <div className="mt-3 overflow-hidden rounded-control border border-border/35 bg-background/70">
<pre <pre
className={cn( className={cn(
"max-h-[min(42vh,32rem)] overflow-auto overscroll-contain px-3.5 py-3 pr-4", "max-h-[min(42vh,32rem)] overflow-auto overscroll-contain px-3.5 py-3 pr-4",
@@ -625,7 +625,7 @@ function RequirementsSection({
}; };
return ( return (
<section className="rounded-[18px] border border-amber-500/20 bg-amber-500/[0.06] p-4"> <section className="rounded-floating border border-amber-500/20 bg-amber-500/[0.06] p-4">
<div className="flex items-start gap-2.5"> <div className="flex items-start gap-2.5">
<CircleAlert <CircleAlert
className="mt-0.5 h-4 w-4 shrink-0 text-amber-700 dark:text-amber-300" className="mt-0.5 h-4 w-4 shrink-0 text-amber-700 dark:text-amber-300"
@@ -648,7 +648,7 @@ function RequirementsSection({
{installOptions.map((option) => ( {installOptions.map((option) => (
<div <div
key={`${option.id}:${option.command}`} key={`${option.id}:${option.command}`}
className="flex min-w-0 items-center gap-2 rounded-[12px] bg-background/80 px-3 py-2" className="flex min-w-0 items-center gap-2 rounded-control bg-background/80 px-3 py-2"
> >
<Terminal className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden /> <Terminal className="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden />
<code className="min-w-0 flex-1 overflow-x-auto whitespace-nowrap font-mono text-[11px] text-foreground/80"> <code className="min-w-0 flex-1 overflow-x-auto whitespace-nowrap font-mono text-[11px] text-foreground/80">
@@ -661,7 +661,7 @@ function RequirementsSection({
})} })}
title={option.label} title={option.label}
onClick={() => void copyCommand(option.command)} onClick={() => void copyCommand(option.command)}
className="flex h-9 w-9 shrink-0 items-center justify-center rounded-[9px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:h-7 sm:w-7" className="flex h-9 w-9 shrink-0 items-center justify-center rounded-control text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:h-7 sm:w-7"
> >
{copiedCommand === option.command ? ( {copiedCommand === option.command ? (
<Check className="h-3.5 w-3.5 text-emerald-600" aria-hidden /> <Check className="h-3.5 w-3.5 text-emerald-600" aria-hidden />
@@ -208,7 +208,7 @@ export function SkillsMarketplace({
aria-label={t("settings.skills.marketplaceSearchLabel", { aria-label={t("settings.skills.marketplaceSearchLabel", {
defaultValue: "Search skills", defaultValue: "Search skills",
})} })}
className="h-11 rounded-[14px] bg-settings-surface pl-9" className="h-11 rounded-control bg-settings-surface pl-9"
/> />
{loading ? ( {loading ? (
<span <span
@@ -226,13 +226,13 @@ export function SkillsMarketplace({
</div> </div>
{error ? ( {error ? (
<div className="rounded-[14px] bg-destructive/10 px-3 py-2.5 text-[13px] text-destructive"> <div className="rounded-control bg-destructive/10 px-3 py-2.5 text-[13px] text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}
{query.trim().length < 2 ? ( {query.trim().length < 2 ? (
<section className="overflow-hidden rounded-[22px] bg-settings-surface"> <section className="overflow-hidden rounded-panel bg-settings-surface">
<div className="flex flex-col items-start gap-2 px-4 pb-2 pt-4 sm:flex-row sm:items-center sm:justify-between sm:px-5"> <div className="flex flex-col items-start gap-2 px-4 pb-2 pt-4 sm:flex-row sm:items-center sm:justify-between sm:px-5">
<h2 className="text-[14px] font-semibold"> <h2 className="text-[14px] font-semibold">
{t("settings.skills.marketplaceTrendingTitle", { {t("settings.skills.marketplaceTrendingTitle", {
@@ -271,14 +271,14 @@ export function SkillsMarketplace({
)} )}
</section> </section>
) : !loading && visibleResults.length === 0 && !error ? ( ) : !loading && visibleResults.length === 0 && !error ? (
<div className="rounded-[22px] bg-settings-surface px-5 py-12 text-center text-sm text-muted-foreground"> <div className="rounded-panel bg-settings-surface px-5 py-12 text-center text-sm text-muted-foreground">
{t("settings.skills.marketplaceEmpty", { {t("settings.skills.marketplaceEmpty", {
query: query.trim(), query: query.trim(),
defaultValue: "No skills found for “{{query}}”.", defaultValue: "No skills found for “{{query}}”.",
})} })}
</div> </div>
) : ( ) : (
<div className="overflow-hidden rounded-[22px] bg-settings-surface"> <div className="overflow-hidden rounded-panel bg-settings-surface">
<MarketplaceSkillGroups <MarketplaceSkillGroups
skills={visibleResults} skills={visibleResults}
installedNames={installedNames} installedNames={installedNames}
@@ -296,9 +296,9 @@ export function SkillsMarketplace({
if (!open) setSelected(null); if (!open) setSelected(null);
}} }}
> >
<AlertDialogContent className="rounded-[20px]"> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<div className="mb-1 flex h-10 w-10 items-center justify-center rounded-[12px] bg-amber-500/10 text-amber-700 dark:text-amber-300"> <div className="mb-1 flex h-10 w-10 items-center justify-center rounded-control bg-amber-500/10 text-amber-700 dark:text-amber-300">
<ShieldAlert className="h-5 w-5" aria-hidden /> <ShieldAlert className="h-5 w-5" aria-hidden />
</div> </div>
<AlertDialogTitle> <AlertDialogTitle>
@@ -316,7 +316,7 @@ export function SkillsMarketplace({
"This third-party skill comes from {{provider}} ({{source}}) and may include instructions or executable scripts.", "This third-party skill comes from {{provider}} ({{source}}) and may include instructions or executable scripts.",
})} })}
</span> </span>
<span className="flex flex-wrap items-center gap-2 rounded-md bg-muted px-2 py-1.5 text-[12px] text-foreground"> <span className="flex flex-wrap items-center gap-2 rounded-control bg-muted px-2 py-1.5 text-[12px] text-foreground">
{selected ? <ProviderMark provider={selected.provider} /> : null} {selected ? <ProviderMark provider={selected.provider} /> : null}
<code>{selected?.source}</code> <code>{selected?.source}</code>
{selected?.version ? <span>v{selected.version}</span> : null} {selected?.version ? <span>v{selected.version}</span> : null}
@@ -224,7 +224,7 @@ export function TokenUsageHeatmap({
<span <span
aria-label={ariaLabel} aria-label={ariaLabel}
className={cn( className={cn(
"aspect-square w-full rounded-[2px] transition-transform hover:scale-110 sm:rounded-[4px]", "aspect-square w-full rounded-mark transition-transform hover:scale-110",
tokenUsageCellClass(level, cell.future), tokenUsageCellClass(level, cell.future),
)} )}
/> />
@@ -136,7 +136,7 @@ export function ChannelLogo({
if (showBrandLogos && logoUrl) { if (showBrandLogos && logoUrl) {
return ( return (
<span <span
className="grid h-10 w-10 shrink-0 place-items-center rounded-[12px] bg-background" className="grid h-10 w-10 shrink-0 place-items-center rounded-control bg-background"
> >
<img <img
src={logoUrl} src={logoUrl}
@@ -154,7 +154,7 @@ export function ChannelLogo({
if (Icon) { if (Icon) {
return ( return (
<span <span
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[12px] bg-background" className="flex h-10 w-10 shrink-0 items-center justify-center rounded-control bg-background"
style={{ color }} style={{ color }}
aria-hidden aria-hidden
> >
@@ -165,7 +165,7 @@ export function ChannelLogo({
return ( return (
<span <span
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-[12px] bg-background text-[11px] font-bold" className="flex h-10 w-10 shrink-0 items-center justify-center rounded-control bg-background text-[11px] font-bold"
style={{ color }} style={{ color }}
aria-hidden aria-hidden
> >
@@ -275,7 +275,7 @@ export function ChannelRuntimeError({
}) { }) {
if (!message) return null; if (!message) return null;
return ( return (
<div className={`${className} rounded-[12px] border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive`}> <div className={`${className} rounded-control border border-destructive/20 bg-destructive/5 px-3 py-2 text-[12px] leading-5 text-destructive`}>
{message} {message}
</div> </div>
); );
@@ -144,7 +144,7 @@ export function ChannelInstancesPanel({
}; };
return ( return (
<aside className="min-h-full rounded-[20px] bg-settings-surface p-5"> <aside className="min-h-full rounded-panel bg-settings-surface p-5">
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-start gap-3"> <div className="flex min-w-0 items-start gap-3">
<ChannelLogo feature={feature} showBrandLogos={showBrandLogos} /> <ChannelLogo feature={feature} showBrandLogos={showBrandLogos} />
@@ -177,7 +177,7 @@ export function ChannelInstancesPanel({
<article <article
key={instance.id} key={instance.id}
className={cn( className={cn(
"overflow-hidden rounded-[18px] transition-colors", "overflow-hidden rounded-floating transition-colors",
expanded expanded
? "bg-background" ? "bg-background"
: "bg-background/70 hover:bg-muted", : "bg-background/70 hover:bg-muted",
@@ -313,7 +313,7 @@ export function ChannelInstancesPanel({
{customization.footer} {customization.footer}
{notice ? ( {notice ? (
<div className="mt-3 rounded-[12px] border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive"> <div className="mt-3 rounded-control border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive">
{notice} {notice}
</div> </div>
) : null} ) : null}
@@ -251,8 +251,8 @@ export function ChannelQrConnectFlow({
return ( return (
<div className="mt-3 space-y-3"> <div className="mt-3 space-y-3">
{pending ? ( {pending ? (
<div className="grid gap-4 rounded-[14px] border border-border/70 p-4 sm:grid-cols-[auto_minmax(0,1fr)]"> <div className="grid gap-4 rounded-control border border-border/70 p-4 sm:grid-cols-[auto_minmax(0,1fr)]">
<div className="grid h-[196px] w-[196px] place-items-center rounded-[14px] border border-border/60 bg-background"> <div className="grid h-[196px] w-[196px] place-items-center rounded-control border border-border/60 bg-background">
{qrDataUrl ? ( {qrDataUrl ? (
<img <img
src={qrDataUrl} src={qrDataUrl}
@@ -293,20 +293,20 @@ export function ChannelQrConnectFlow({
) : null} ) : null}
{succeeded && !suppressSucceeded ? ( {succeeded && !suppressSucceeded ? (
<div className="flex items-center gap-2 rounded-[12px] border border-emerald-500/20 px-3 py-2 text-[12px] font-medium text-emerald-700 dark:text-emerald-200"> <div className="flex items-center gap-2 rounded-control border border-emerald-500/20 px-3 py-2 text-[12px] font-medium text-emerald-700 dark:text-emerald-200">
<Check className="h-3.5 w-3.5" aria-hidden /> <Check className="h-3.5 w-3.5" aria-hidden />
{displayMessage ?? labels.connected} {displayMessage ?? labels.connected}
</div> </div>
) : null} ) : null}
{connect && ["expired", "failed", "cancelled"].includes(connect.status) ? ( {connect && ["expired", "failed", "cancelled"].includes(connect.status) ? (
<div className="rounded-[12px] border border-border/60 px-3 py-2 text-[12px] leading-5 text-muted-foreground"> <div className="rounded-control border border-border/60 px-3 py-2 text-[12px] leading-5 text-muted-foreground">
{displayMessage || labels.stopped} {displayMessage || labels.stopped}
</div> </div>
) : null} ) : null}
{error ? ( {error ? (
<div className="rounded-[12px] border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive"> <div className="rounded-control border border-destructive/20 px-3 py-2 text-[12px] leading-5 text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}
@@ -87,7 +87,7 @@ export function ChannelCatalogRow({
aria-pressed={selected} aria-pressed={selected}
onClick={onSelect} onClick={onSelect}
className={cn( className={cn(
"group flex w-full min-w-0 items-center gap-3 rounded-[14px] px-3 py-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border/80", "group flex w-full min-w-0 items-center gap-3 rounded-control px-3 py-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border/80",
selected ? "bg-background" : "hover:bg-muted", selected ? "bg-background" : "hover:bg-muted",
)} )}
> >
@@ -187,7 +187,7 @@ export function ChannelSetupPanel({
}); });
return ( return (
<aside className="min-h-full rounded-[20px] bg-settings-surface p-5"> <aside className="min-h-full rounded-panel bg-settings-surface p-5">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex min-w-0 items-start gap-3"> <div className="flex min-w-0 items-start gap-3">
<ChannelLogo feature={feature} showBrandLogos={showBrandLogos} /> <ChannelLogo feature={feature} showBrandLogos={showBrandLogos} />
@@ -482,7 +482,7 @@ function ChannelSetupSurface({
) : null} ) : null}
</div> </div>
{setup.command ? ( {setup.command ? (
<code className="mt-3 block rounded-[10px] border border-border/50 bg-muted/45 px-2.5 py-2 font-mono text-[11px] leading-5 text-foreground"> <code className="mt-3 block rounded-control border border-border/50 bg-muted/45 px-2.5 py-2 font-mono text-[11px] leading-5 text-foreground">
{setup.command} {setup.command}
</code> </code>
) : null} ) : null}
@@ -538,7 +538,7 @@ function ChannelSetupSurface({
{notice ? ( {notice ? (
<div <div
role="status" role="status"
className="rounded-[12px] bg-muted/55 px-3 py-2.5 text-[12px] leading-5 text-muted-foreground" className="rounded-control bg-muted/55 px-3 py-2.5 text-[12px] leading-5 text-muted-foreground"
> >
{notice} {notice}
</div> </div>
@@ -60,13 +60,13 @@ export function ChannelGuideLink({
"inline-flex max-w-full items-center gap-2 bg-background/80 font-semibold text-foreground transition-colors hover:bg-background", "inline-flex max-w-full items-center gap-2 bg-background/80 font-semibold text-foreground transition-colors hover:bg-background",
compact compact
? "shrink-0 rounded-full py-1 pl-1 pr-2.5 text-[11.5px]" ? "shrink-0 rounded-full py-1 pl-1 pr-2.5 text-[11.5px]"
: "mt-3 rounded-[12px] py-1.5 pl-1.5 pr-3 text-[12px]", : "mt-3 rounded-control py-1.5 pl-1.5 pr-3 text-[12px]",
)} )}
> >
<span <span
className={cn( className={cn(
"grid shrink-0 place-items-center overflow-hidden bg-muted/70 font-bold", "grid shrink-0 place-items-center overflow-hidden bg-muted/70 font-bold",
compact ? "h-5 w-5 rounded-full text-[9px]" : "h-6 w-6 rounded-[7px] text-[10px]", compact ? "h-5 w-5 rounded-full text-[9px]" : "h-6 w-6 rounded-compact text-[10px]",
)} )}
style={{ color }} style={{ color }}
aria-hidden aria-hidden
@@ -231,7 +231,7 @@ export function ChannelProviderPresets({
<div <div
role="radiogroup" role="radiogroup"
aria-label={t("settings.channels.providerPreset", { defaultValue: "Provider" })} aria-label={t("settings.channels.providerPreset", { defaultValue: "Provider" })}
className="grid rounded-[10px] bg-muted p-0.5 text-[12px] font-medium text-muted-foreground" className="grid rounded-control bg-muted p-0.5 text-[12px] font-medium text-muted-foreground"
style={{ gridTemplateColumns: `repeat(${presets.length}, minmax(0, 1fr))` }} style={{ gridTemplateColumns: `repeat(${presets.length}, minmax(0, 1fr))` }}
> >
{presets.map((preset) => ( {presets.map((preset) => (
@@ -245,7 +245,7 @@ export function ChannelProviderPresets({
onApply(preset); onApply(preset);
}} }}
className={cn( className={cn(
"min-h-8 rounded-[8px] px-2 py-1.5 transition-colors hover:text-foreground", "min-h-8 rounded-compact px-2 py-1.5 transition-colors hover:text-foreground",
selected === preset.id && "bg-background text-foreground", selected === preset.id && "bg-background text-foreground",
)} )}
> >
@@ -370,7 +370,7 @@ export function ChannelSetupSteps({
))} ))}
</ol> </ol>
{tryIt ? ( {tryIt ? (
<div className="mt-3 rounded-[12px] bg-background/75 px-3 py-2 text-[12px] text-muted-foreground"> <div className="mt-3 rounded-control bg-background/75 px-3 py-2 text-[12px] text-muted-foreground">
<span className="font-medium text-foreground"> <span className="font-medium text-foreground">
{tx("settings.channels.tryIt", "Try it")} {tx("settings.channels.tryIt", "Try it")}
</span> </span>
@@ -159,7 +159,7 @@ export function CredentialForm({
<span <span
role="radiogroup" role="radiogroup"
aria-label={field.label} aria-label={field.label}
className="mt-1 grid rounded-[10px] bg-muted p-0.5 text-[12px] font-medium text-muted-foreground" className="mt-1 grid rounded-control bg-muted p-0.5 text-[12px] font-medium text-muted-foreground"
style={{ gridTemplateColumns: `repeat(${field.options.length}, minmax(0, 1fr))` }} style={{ gridTemplateColumns: `repeat(${field.options.length}, minmax(0, 1fr))` }}
> >
{field.options.map((option) => ( {field.options.map((option) => (
@@ -170,7 +170,7 @@ export function CredentialForm({
aria-checked={selectedOption === option.value} aria-checked={selectedOption === option.value}
onClick={() => onChange(field.key, option.value)} onClick={() => onChange(field.key, option.value)}
className={cn( className={cn(
"min-h-8 rounded-[8px] px-2 py-1.5 transition-colors hover:text-foreground", "min-h-8 rounded-compact px-2 py-1.5 transition-colors hover:text-foreground",
selectedOption === option.value selectedOption === option.value
&& "bg-background text-foreground ring-1 ring-inset ring-border/45", && "bg-background text-foreground ring-1 ring-inset ring-border/45",
)} )}
@@ -200,7 +200,7 @@ export function CredentialForm({
value={values[field.key] ?? ""} value={values[field.key] ?? ""}
onChange={(event) => onChange(field.key, event.target.value)} onChange={(event) => onChange(field.key, event.target.value)}
className={cn( className={cn(
"h-9 rounded-[10px] border-border/60 bg-muted/35 text-[13px]", "h-9 rounded-control border-border/60 bg-muted/35 text-[13px]",
showSecretToggle && "pr-9", showSecretToggle && "pr-9",
)} )}
/> />
@@ -117,7 +117,7 @@ export function ModelPresetDeleteDialog({
t(key, { defaultValue: fallback, ...(values ?? {}) }); t(key, { defaultValue: fallback, ...(values ?? {}) });
return ( return (
<Dialog open={preset !== null} onOpenChange={onOpenChange}> <Dialog open={preset !== null} onOpenChange={onOpenChange}>
<DialogContent className="max-w-[440px] rounded-[24px]"> <DialogContent className="max-w-[440px]">
<DialogHeader className="text-left"> <DialogHeader className="text-left">
<DialogTitle> <DialogTitle>
{tx("settings.models.deletePresetTitle", "Delete model preset?")} {tx("settings.models.deletePresetTitle", "Delete model preset?")}
@@ -130,11 +130,10 @@ export function ModelPresetDeleteDialog({
)} )}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter className="gap-2 sm:space-x-0"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
className="rounded-full"
disabled={deleting} disabled={deleting}
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
> >
@@ -143,7 +142,6 @@ export function ModelPresetDeleteDialog({
<Button <Button
type="button" type="button"
variant="destructive" variant="destructive"
className="rounded-full"
disabled={deleting} disabled={deleting}
onClick={onConfirm} onClick={onConfirm}
> >
@@ -343,7 +341,7 @@ export function ModelsSettings({
<div <div
id="model-preset-editor" id="model-preset-editor"
data-testid="model-preset-editor" data-testid="model-preset-editor"
className="mx-3 mb-3 divide-y divide-border/45 overflow-hidden rounded-[18px] border border-border/45 bg-background/80 shadow-sm motion-reduce:animate-none animate-in fade-in-0 slide-in-from-top-1 duration-200 sm:mx-5 lg:mx-auto lg:w-[calc(100%-2.5rem)] lg:max-w-6xl" className="mx-3 mb-3 divide-y divide-border/45 overflow-hidden rounded-floating border border-border/45 bg-background/80 shadow-sm motion-reduce:animate-none animate-in fade-in-0 slide-in-from-top-1 duration-200 sm:mx-5 lg:mx-auto lg:w-[calc(100%-2.5rem)] lg:max-w-6xl"
> >
{creating ? ( {creating ? (
<div className="flex min-h-[52px] items-center px-4 py-3 sm:px-5"> <div className="flex min-h-[52px] items-center px-4 py-3 sm:px-5">
@@ -527,7 +525,7 @@ export function ModelsSettings({
{!settings.model_call_order_editable ? ( {!settings.model_call_order_editable ? (
<div className="flex flex-col gap-4 px-4 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-5"> <div className="flex flex-col gap-4 px-4 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-5">
<div className="flex min-w-0 items-start gap-3"> <div className="flex min-w-0 items-start gap-3">
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-[12px] bg-muted text-muted-foreground"> <span className="grid h-9 w-9 shrink-0 place-items-center rounded-control bg-muted text-muted-foreground">
<ListOrdered className="h-4 w-4" aria-hidden /> <ListOrdered className="h-4 w-4" aria-hidden />
</span> </span>
<div className="min-w-0"> <div className="min-w-0">
@@ -674,7 +672,7 @@ export function ModelsSettings({
aria-controls={isSelected ? "model-preset-editor" : undefined} aria-controls={isSelected ? "model-preset-editor" : undefined}
disabled={!preset} disabled={!preset}
onClick={() => preset && selectPreset(preset, key)} onClick={() => preset && selectPreset(preset, key)}
className="flex min-w-0 flex-1 items-center gap-3 rounded-[12px] text-left outline-none focus-visible:ring-2 focus-visible:ring-ring" className="flex min-w-0 flex-1 items-center gap-3 rounded-control text-left outline-none focus-visible:ring-2 focus-visible:ring-ring"
> >
{ordered ? ( {ordered ? (
<span className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-muted font-mono text-[11px] font-semibold tabular-nums text-muted-foreground"> <span className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-muted font-mono text-[11px] font-semibold tabular-nums text-muted-foreground">
@@ -841,7 +839,7 @@ function ModelAdvancedFields({
const value = Number(event.target.value); const value = Number(event.target.value);
if (Number.isFinite(value)) onChange({ maxTokens: value }); if (Number.isFinite(value)) onChange({ maxTokens: value });
}} }}
className="h-9 rounded-[12px] text-[13px]" className="h-9 text-[13px]"
/> />
</label> </label>
<label className="block"> <label className="block">
@@ -858,7 +856,7 @@ function ModelAdvancedFields({
const value = Number(event.target.value); const value = Number(event.target.value);
if (Number.isFinite(value)) onChange({ temperature: value }); if (Number.isFinite(value)) onChange({ temperature: value });
}} }}
className="h-9 rounded-[12px] text-[13px]" className="h-9 text-[13px]"
/> />
</label> </label>
</div> </div>
@@ -887,7 +885,7 @@ function ModelAdvancedFields({
placeholder={tx("settings.values.default", "Default")} placeholder={tx("settings.values.default", "Default")}
autoCapitalize="none" autoCapitalize="none"
spellCheck={false} spellCheck={false}
className="h-9 rounded-[12px] text-[13px]" className="h-9 text-[13px]"
/> />
</label> </label>
</div> </div>
@@ -276,7 +276,7 @@ export function ProviderOAuthLoginDialog({
if (!open) onClose(); if (!open) onClose();
}} }}
> >
<DialogContent className="w-[min(calc(100vw-2rem),28rem)] rounded-[24px]"> <DialogContent className="w-[min(calc(100vw-2rem),28rem)]">
<form <form
className="space-y-4" className="space-y-4"
onSubmit={(event) => { onSubmit={(event) => {
@@ -296,7 +296,7 @@ export function ProviderOAuthLoginDialog({
: t("settings.oauth.localCodeHelp")} : t("settings.oauth.localCodeHelp")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex items-center gap-2 rounded-[14px] border border-border/45 bg-muted/35 px-3 py-2.5 text-[12px] text-muted-foreground"> <div className="flex items-center gap-2 rounded-control border border-border/45 bg-muted/35 px-3 py-2.5 text-[12px] text-muted-foreground">
{expectsCallbackUrl && remoteBrowserAccess ? ( {expectsCallbackUrl && remoteBrowserAccess ? (
<Clipboard className="h-3.5 w-3.5 shrink-0" aria-hidden /> <Clipboard className="h-3.5 w-3.5 shrink-0" aria-hidden />
) : ( ) : (
@@ -341,12 +341,12 @@ export function ProviderOAuthLoginDialog({
{error ? ( {error ? (
<p <p
role="alert" role="alert"
className="rounded-[14px] border border-destructive/20 bg-destructive/5 px-3 py-2.5 text-[12px] text-destructive" className="rounded-control border border-destructive/20 bg-destructive/5 px-3 py-2.5 text-[12px] text-destructive"
> >
{error} {error}
</p> </p>
) : null} ) : null}
<DialogFooter className="gap-2 sm:space-x-0"> <DialogFooter>
<Button type="button" variant="outline" onClick={onOpenAuthorization}> <Button type="button" variant="outline" onClick={onOpenAuthorization}>
<ExternalLink className="mr-2 h-4 w-4" aria-hidden /> <ExternalLink className="mr-2 h-4 w-4" aria-hidden />
{expectsCallbackUrl {expectsCallbackUrl
@@ -380,7 +380,7 @@ function ProviderRequestOptions({
const tx = (key: string, fallback: string) => t(key, { defaultValue: fallback }); const tx = (key: string, fallback: string) => t(key, { defaultValue: fallback });
return ( return (
<div className="overflow-hidden rounded-[18px] border border-border/45 bg-background/75"> <div className="overflow-hidden rounded-floating border border-border/45 bg-background/75">
{options.map((option, index) => { {options.map((option, index) => {
const title = tx(option.titleKey, option.title); const title = tx(option.titleKey, option.title);
const Icon = option.kind === "priority" ? Zap : Globe2; const Icon = option.kind === "priority" ? Zap : Globe2;
@@ -599,7 +599,7 @@ function ProviderAdvancedOptions({
onChange={(event) => onChange({ extraHeaders: event.target.value })} onChange={(event) => onChange({ extraHeaders: event.target.value })}
placeholder={'{"X-Header":"value"}'} placeholder={'{"X-Header":"value"}'}
spellCheck={false} spellCheck={false}
className="min-h-[88px] resize-y rounded-[14px] bg-background font-mono text-[12px]" className="min-h-[88px] resize-y bg-background font-mono text-[12px]"
/> />
</label> </label>
) : null} ) : null}
@@ -613,7 +613,7 @@ function ProviderAdvancedOptions({
onChange={(event) => onChange({ extraQuery: event.target.value })} onChange={(event) => onChange({ extraQuery: event.target.value })}
placeholder={'{"api-version":"2024-02-01"}'} placeholder={'{"api-version":"2024-02-01"}'}
spellCheck={false} spellCheck={false}
className="min-h-[88px] resize-y rounded-[14px] bg-background font-mono text-[12px]" className="min-h-[88px] resize-y bg-background font-mono text-[12px]"
/> />
</label> </label>
) : null} ) : null}
@@ -627,7 +627,7 @@ function ProviderAdvancedOptions({
onChange={(event) => onChange({ extraBody: event.target.value })} onChange={(event) => onChange({ extraBody: event.target.value })}
placeholder={'{"service_tier":"priority"}'} placeholder={'{"service_tier":"priority"}'}
spellCheck={false} spellCheck={false}
className="min-h-[96px] resize-y rounded-[14px] bg-background font-mono text-[12px]" className="min-h-[96px] resize-y bg-background font-mono text-[12px]"
/> />
</label> </label>
) : null} ) : null}
@@ -825,7 +825,7 @@ export function ProvidersSettings({
) : null} ) : null}
{isOauthProvider ? ( {isOauthProvider ? (
<> <>
<div className="flex flex-col gap-3 rounded-[18px] border border-border/45 bg-background/75 px-4 py-3 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-3 rounded-floating border border-border/45 bg-background/75 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[13px] font-semibold text-foreground"> <p className="text-[13px] font-semibold text-foreground">
{tx("settings.oauth.authentication", "OAuth authentication")} {tx("settings.oauth.authentication", "OAuth authentication")}
@@ -1240,7 +1240,7 @@ export function ProvidersSettings({
className="group flex min-h-[70px] w-full items-center justify-between gap-4 px-4 py-3 text-left transition-colors hover:bg-muted/35 sm:px-5" className="group flex min-h-[70px] w-full items-center justify-between gap-4 px-4 py-3 text-left transition-colors hover:bg-muted/35 sm:px-5"
> >
<span className="flex min-w-0 items-center gap-3"> <span className="flex min-w-0 items-center gap-3">
<span className="grid h-10 w-10 shrink-0 place-items-center rounded-[14px] bg-muted text-muted-foreground"> <span className="grid h-10 w-10 shrink-0 place-items-center rounded-control bg-muted text-muted-foreground">
<Plus className="h-5 w-5" aria-hidden /> <Plus className="h-5 w-5" aria-hidden />
</span> </span>
<span className="truncate text-[15px] font-semibold text-foreground"> <span className="truncate text-[15px] font-semibold text-foreground">
@@ -1334,7 +1334,7 @@ function ProviderIcon({
return ( return (
<span <span
data-testid={`provider-logo-${provider}`} data-testid={`provider-logo-${provider}`}
className="grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-[14px] border border-border/45 bg-background" className="grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-control border border-border/45 bg-background"
> >
<img <img
src={logoUrl} src={logoUrl}
@@ -1352,7 +1352,7 @@ function ProviderIcon({
return ( return (
<span <span
data-testid={`provider-logo-fallback-${provider}`} data-testid={`provider-logo-fallback-${provider}`}
className="grid h-10 w-10 shrink-0 place-items-center rounded-[14px] text-[11px] font-semibold text-white" className="grid h-10 w-10 shrink-0 place-items-center rounded-control text-[11px] font-semibold text-white"
style={{ backgroundColor: brand.color }} style={{ backgroundColor: brand.color }}
aria-hidden aria-hidden
> >
@@ -129,7 +129,7 @@ export function OverviewSettings({
: tx("settings.values.ready", "Ready"); : tx("settings.values.ready", "Ready");
return ( return (
<div className="space-y-7"> <div className="space-y-7">
<section className="rounded-[22px] bg-settings-surface px-4 py-4 sm:px-5"> <section className="rounded-panel bg-settings-surface px-4 py-4 sm:px-5">
<TokenUsageHeatmap usage={settings.usage} timeZone={settings.agent.timezone} /> <TokenUsageHeatmap usage={settings.usage} timeZone={settings.agent.timezone} />
</section> </section>
@@ -433,7 +433,7 @@ function OverviewRowIcon({
icon: LucideIcon; icon: LucideIcon;
}) { }) {
return ( return (
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-[12px] bg-muted text-foreground/82 transition-colors group-hover:bg-muted/80 dark:bg-muted/70"> <span className="grid h-9 w-9 shrink-0 place-items-center rounded-control bg-muted text-foreground/82 transition-colors group-hover:bg-muted/80 dark:bg-muted/70">
<Icon className="h-4 w-4" aria-hidden /> <Icon className="h-4 w-4" aria-hidden />
</span> </span>
); );
@@ -309,7 +309,7 @@ export function ModelIdPicker({
key={model.id} key={model.id}
{...navigation.getOptionProps(model.id)} {...navigation.getOptionProps(model.id)}
className={cn( className={cn(
"flex cursor-default items-center justify-between gap-2 rounded-[12px] px-2 py-1.5 text-[12px]", "flex cursor-default items-center justify-between gap-2 rounded-control px-2 py-1.5 text-[12px]",
options.selected && "text-foreground", options.selected && "text-foreground",
)} )}
> >
@@ -455,7 +455,7 @@ export function ModelIdPicker({
) : null} ) : null}
<ComboboxOption <ComboboxOption
{...navigation.getOptionProps(customCandidate)} {...navigation.getOptionProps(customCandidate)}
className="flex cursor-default items-center gap-2 rounded-[12px] px-2 py-1.5 text-[12px]" className="flex cursor-default items-center gap-2 rounded-control px-2 py-1.5 text-[12px]"
> >
<span className="grid h-5 w-5 shrink-0 place-items-center rounded-md bg-muted/80 text-muted-foreground"> <span className="grid h-5 w-5 shrink-0 place-items-center rounded-md bg-muted/80 text-muted-foreground">
<Pencil className="h-3 w-3" aria-hidden /> <Pencil className="h-3 w-3" aria-hidden />
@@ -31,7 +31,7 @@ export function CapabilityInstallNotice({
installing?: boolean; installing?: boolean;
}) { }) {
return ( return (
<div className="flex items-start gap-3 rounded-[14px] border border-border/55 bg-muted/22 px-3.5 py-3"> <div className="flex items-start gap-3 rounded-control border border-border/55 bg-muted/22 px-3.5 py-3">
{installing ? ( {installing ? (
<Loader2 className="mt-0.5 h-4 w-4 shrink-0 animate-spin text-muted-foreground" aria-hidden /> <Loader2 className="mt-0.5 h-4 w-4 shrink-0 animate-spin text-muted-foreground" aria-hidden />
) : ( ) : (
@@ -78,13 +78,13 @@ export function NanobotFeatureInstallDialog({
)} )}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter className="mt-7 !grid grid-cols-1 gap-3 space-x-0 sm:grid-cols-2 sm:space-x-0"> <DialogFooter className="mt-7 !grid grid-cols-1 gap-2 sm:grid-cols-2">
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
disabled={installing} disabled={installing}
className="h-11 w-full min-w-0 rounded-full bg-muted/70 px-5 text-[15px] font-semibold text-foreground shadow-none hover:bg-muted" className="h-11 w-full min-w-0 bg-muted/70 px-5 text-[15px] font-semibold text-foreground shadow-none hover:bg-muted"
> >
{tx("settings.automations.cancel", "Cancel")} {tx("settings.automations.cancel", "Cancel")}
</Button> </Button>
@@ -92,7 +92,7 @@ export function NanobotFeatureInstallDialog({
type="button" type="button"
onClick={() => feature && void onConfirm(feature)} onClick={() => feature && void onConfirm(feature)}
disabled={!feature || installing} disabled={!feature || installing}
className="h-11 w-full min-w-0 !whitespace-normal rounded-full px-5 text-center text-[15px] font-semibold" className="h-11 w-full min-w-0 !whitespace-normal px-5 text-center text-[15px] font-semibold"
> >
{installing ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null} {installing ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null}
{tx("settings.nanobotFeatures.installConfirmAction", "Install and enable")} {tx("settings.nanobotFeatures.installConfirmAction", "Install and enable")}
@@ -117,7 +117,7 @@ export function DismissibleStatusMessage({
return ( return (
<div <div
className={cn( className={cn(
"flex items-center justify-between gap-3 rounded-[12px] border py-2.5 pl-4 pr-2 text-[13px]", "flex items-center justify-between gap-3 rounded-control border py-2.5 pl-4 pr-2 text-[13px]",
isError isError
? "border-destructive/20 bg-destructive/5 text-destructive" ? "border-destructive/20 bg-destructive/5 text-destructive"
: "border-border/55 bg-muted/35 text-muted-foreground", : "border-border/55 bg-muted/35 text-muted-foreground",
@@ -153,7 +153,7 @@ export function RestartRequiredNotice({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="flex flex-col gap-3 rounded-[12px] border border-amber-500/20 bg-amber-500/8 px-4 py-3 text-[12.5px] text-amber-800 dark:text-amber-200 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-3 rounded-control border border-amber-500/20 bg-amber-500/8 px-4 py-3 text-[12.5px] text-amber-800 dark:text-amber-200 sm:flex-row sm:items-center sm:justify-between">
<span>{message}</span> <span>{message}</span>
{onRestart ? ( {onRestart ? (
<Button <Button
@@ -186,7 +186,7 @@ export function SettingsSectionTitle({ children }: { children: ReactNode }) {
export function SettingsGroup({ children }: { children: ReactNode }) { export function SettingsGroup({ children }: { children: ReactNode }) {
return ( return (
<div className="overflow-hidden rounded-[22px] bg-settings-surface"> <div className="overflow-hidden rounded-panel bg-settings-surface">
<div className="divide-y divide-border/45">{children}</div> <div className="divide-y divide-border/45">{children}</div>
</div> </div>
); );
@@ -256,7 +256,7 @@ export function AppsCatalogSettings({
onChange={(event) => onQueryChange(event.target.value)} onChange={(event) => onQueryChange(event.target.value)}
placeholder={tx("settings.apps.searchPlaceholder", "Search Apps")} placeholder={tx("settings.apps.searchPlaceholder", "Search Apps")}
className={cn( className={cn(
"h-12 rounded-[14px] pl-11 text-[15px]", "h-12 pl-11 text-[15px]",
SETTINGS_SEARCH_INPUT_CLASS, SETTINGS_SEARCH_INPUT_CLASS,
)} )}
/> />
@@ -289,7 +289,7 @@ export function AppsCatalogSettings({
/> />
) : null} ) : null}
<section className="rounded-[22px] bg-settings-surface px-3 py-3 sm:px-4"> <section className="rounded-panel bg-settings-surface px-3 py-3 sm:px-4">
<div className="flex items-center justify-between border-b border-border/45 pb-3"> <div className="flex items-center justify-between border-b border-border/45 pb-3">
<SettingsSectionTitle> <SettingsSectionTitle>
{filter === "mcp" {filter === "mcp"
@@ -412,7 +412,7 @@ function CliAppsCatalogRow({
const description = app.description || app.requires || app.entry_point || app.name; const description = app.description || app.requires || app.entry_point || app.name;
return ( return (
<article className="apps-catalog-row group flex min-w-0 items-center gap-3 rounded-[14px] px-3 py-3 transition-colors hover:bg-muted/45"> <article className="apps-catalog-row group flex min-w-0 items-center gap-3 rounded-control px-3 py-3 transition-colors hover:bg-muted/45">
<CliAppLogo app={app} showBrandLogos={showBrandLogos} /> <CliAppLogo app={app} showBrandLogos={showBrandLogos} />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="flex min-w-0 items-baseline gap-2"> <div className="flex min-w-0 items-baseline gap-2">
@@ -583,7 +583,7 @@ function McpAppsCatalogRow({
}; };
return ( return (
<article className="min-w-0 rounded-[14px] transition-colors hover:bg-muted/45"> <article className="min-w-0 rounded-control transition-colors hover:bg-muted/45">
<div className="group flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 px-3 py-3"> <div className="group flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 px-3 py-3">
<McpPresetLogo preset={preset} showBrandLogos={showBrandLogos} /> <McpPresetLogo preset={preset} showBrandLogos={showBrandLogos} />
<div className="min-w-[8rem] flex-[1_1_8rem]"> <div className="min-w-[8rem] flex-[1_1_8rem]">
@@ -747,7 +747,7 @@ function McpAppsCatalogRow({
{manualCallback ? ( {manualCallback ? (
<form <form
className="mx-3 mb-3 min-w-0 space-y-3 rounded-[14px] bg-background/55 p-3" className="mx-3 mb-3 min-w-0 space-y-3 rounded-control bg-background/55 p-3"
onSubmit={(event) => { onSubmit={(event) => {
event.preventDefault(); event.preventDefault();
onOAuthComplete(); onOAuthComplete();
@@ -825,7 +825,7 @@ function McpAppsCatalogRow({
</div> </div>
</form> </form>
) : oauthFlow && oauthPopupBlocked && oauthFlow.authorization_url ? ( ) : oauthFlow && oauthPopupBlocked && oauthFlow.authorization_url ? (
<div className="mx-3 mb-3 flex flex-col gap-2.5 rounded-[14px] bg-background/55 p-3"> <div className="mx-3 mb-3 flex flex-col gap-2.5 rounded-control bg-background/55 p-3">
<div className="flex min-w-0 items-center gap-2.5 text-[12.5px] text-muted-foreground"> <div className="flex min-w-0 items-center gap-2.5 text-[12.5px] text-muted-foreground">
<span> <span>
{mcpOAuthStatusText( {mcpOAuthStatusText(
@@ -1016,10 +1016,10 @@ function McpCustomServerPanel({
]; ];
return ( return (
<section className="overflow-hidden rounded-[16px] bg-settings-surface"> <section className="overflow-hidden rounded-floating bg-settings-surface">
<div className="flex flex-col gap-3 px-3 py-3 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-3 px-3 py-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-[11px] bg-muted text-muted-foreground"> <span className="grid h-9 w-9 shrink-0 place-items-center rounded-control bg-muted text-muted-foreground">
<Server className="h-4 w-4" aria-hidden /> <Server className="h-4 w-4" aria-hidden />
</span> </span>
<div className="min-w-0"> <div className="min-w-0">
@@ -1154,7 +1154,7 @@ function McpCustomServerPanel({
value={form.headers} value={form.headers}
onChange={(event) => update("headers", event.target.value)} onChange={(event) => update("headers", event.target.value)}
placeholder={'{"Authorization":"Bearer ..."}'} placeholder={'{"Authorization":"Bearer ..."}'}
className="min-h-[68px] resize-y rounded-[12px] bg-background/80 font-mono text-[12px]" className="min-h-[68px] resize-y bg-background/80 font-mono text-[12px]"
/> />
<p <p
id={headersHelpId} id={headersHelpId}
@@ -1202,7 +1202,7 @@ function McpCustomServerPanel({
value={form.args} value={form.args}
onChange={(event) => update("args", event.target.value)} onChange={(event) => update("args", event.target.value)}
placeholder={'["-y", "docs-mcp"]'} placeholder={'["-y", "docs-mcp"]'}
className="min-h-[68px] resize-y rounded-[12px] bg-background/80 font-mono text-[12px]" className="min-h-[68px] resize-y bg-background/80 font-mono text-[12px]"
/> />
</label> </label>
) : null} ) : null}
@@ -1214,7 +1214,7 @@ function McpCustomServerPanel({
value={form.env} value={form.env}
onChange={(event) => update("env", event.target.value)} onChange={(event) => update("env", event.target.value)}
placeholder={'{"API_KEY":"..."}'} placeholder={'{"API_KEY":"..."}'}
className="min-h-[68px] resize-y rounded-[12px] bg-background/80 font-mono text-[12px]" className="min-h-[68px] resize-y bg-background/80 font-mono text-[12px]"
/> />
</label> </label>
<label className="min-w-0"> <label className="min-w-0">
@@ -1257,7 +1257,7 @@ function McpCustomServerPanel({
value={configImport} value={configImport}
onChange={(event) => onConfigImportChange(event.target.value)} onChange={(event) => onConfigImportChange(event.target.value)}
placeholder={'{"mcpServers":{"docs":{"command":"npx","args":["-y","docs-mcp"]}}}'} placeholder={'{"mcpServers":{"docs":{"command":"npx","args":["-y","docs-mcp"]}}}'}
className="min-h-[84px] resize-y rounded-[12px] bg-background/80 font-mono text-[12px]" className="min-h-[84px] resize-y bg-background/80 font-mono text-[12px]"
/> />
</label> </label>
<Button <Button
@@ -1352,7 +1352,7 @@ function McpPresetLogo({
<span <span
className={cn( className={cn(
"grid shrink-0 place-items-center border border-border/45 bg-background", "grid shrink-0 place-items-center border border-border/45 bg-background",
compact ? "h-10 w-10 rounded-[10px]" : "h-11 w-11 rounded-[8px]", compact ? "h-10 w-10 rounded-control" : "h-11 w-11 rounded-compact",
)} )}
> >
<img <img
@@ -1372,8 +1372,8 @@ function McpPresetLogo({
className={cn( className={cn(
"grid shrink-0 place-items-center font-semibold text-white", "grid shrink-0 place-items-center font-semibold text-white",
compact compact
? "h-10 w-10 rounded-[10px] text-[12px]" ? "h-10 w-10 rounded-control text-[12px]"
: "h-11 w-11 rounded-[8px] text-[13px]", : "h-11 w-11 rounded-compact text-[13px]",
)} )}
style={{ backgroundColor: bg }} style={{ backgroundColor: bg }}
> >
@@ -1406,7 +1406,7 @@ function CliAppReadyPanel({
}; };
return ( return (
<section className="rounded-[12px] bg-settings-surface px-4 py-3"> <section className="rounded-control bg-settings-surface px-4 py-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-center"> <div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<CliAppLogo app={app} showBrandLogos={showBrandLogos} /> <CliAppLogo app={app} showBrandLogos={showBrandLogos} />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
@@ -1472,7 +1472,7 @@ function CliAppLogo({ app, showBrandLogos }: { app: CliAppInfo; showBrandLogos:
return ( return (
<span <span
className="relative grid h-11 w-11 shrink-0 place-items-center overflow-hidden rounded-[8px] border border-border/45 bg-muted text-[13px] font-semibold" className="relative grid h-11 w-11 shrink-0 place-items-center overflow-hidden rounded-compact border border-border/45 bg-muted text-[13px] font-semibold"
style={{ color: app.brand_color || "hsl(var(--muted-foreground))" }} style={{ color: app.brand_color || "hsl(var(--muted-foreground))" }}
> >
<span <span
@@ -129,14 +129,14 @@ export function AutomationsSettings({
<section className="shrink-0"> <section className="shrink-0">
<div className="mx-auto flex w-full max-w-[56rem] flex-col gap-3"> <div className="mx-auto flex w-full max-w-[56rem] flex-col gap-3">
<div className="-mx-1 overflow-x-auto px-1 pb-0.5"> <div className="-mx-1 overflow-x-auto px-1 pb-0.5">
<div className="grid w-full min-w-[36rem] grid-cols-5 gap-1 rounded-[15px] bg-muted p-1"> <div className="grid w-full min-w-[36rem] grid-cols-5 gap-1 rounded-floating bg-muted p-1">
{summaryOptions.map((option) => ( {summaryOptions.map((option) => (
<button <button
key={option.value} key={option.value}
type="button" type="button"
onClick={() => onFilterChange(option.value)} onClick={() => onFilterChange(option.value)}
className={cn( className={cn(
"inline-flex h-8 min-w-0 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[11px] px-3 text-[12px] font-medium text-muted-foreground transition-colors", "inline-flex h-8 min-w-0 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-control px-3 text-[12px] font-medium text-muted-foreground transition-colors",
filter === option.value && "bg-background text-foreground", filter === option.value && "bg-background text-foreground",
automationFilterToneClass(option.value, option.count, filter === option.value), automationFilterToneClass(option.value, option.count, filter === option.value),
)} )}
@@ -166,7 +166,7 @@ export function AutomationsSettings({
"Search task, message, linked chat, or schedule", "Search task, message, linked chat, or schedule",
)} )}
className={cn( className={cn(
"h-9 w-full rounded-[13px] pl-9 text-[13px]", "h-9 w-full rounded-control pl-9 text-[13px]",
SETTINGS_SEARCH_INPUT_CLASS, SETTINGS_SEARCH_INPUT_CLASS,
)} )}
/> />
@@ -175,7 +175,7 @@ export function AutomationsSettings({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button <button
type="button" type="button"
className="inline-flex h-9 min-w-[8.5rem] items-center justify-center gap-1.5 whitespace-nowrap rounded-[13px] border border-border/45 bg-settings-surface px-3 text-[12px] font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:w-auto" className="inline-flex h-9 min-w-[8.5rem] items-center justify-center gap-1.5 whitespace-nowrap rounded-control border border-border/45 bg-settings-surface px-3 text-[12px] font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:w-auto"
> >
<ArrowUpDown className="h-3.5 w-3.5" aria-hidden /> <ArrowUpDown className="h-3.5 w-3.5" aria-hidden />
<span>{sortLabel[sort]}</span> <span>{sortLabel[sort]}</span>
@@ -197,19 +197,19 @@ export function AutomationsSettings({
) : null} ) : null}
{error ? ( {error ? (
<div className="flex items-center gap-2 rounded-[18px] border border-destructive/20 bg-destructive/5 px-4 py-3 text-[13px] text-destructive"> <div className="flex items-center gap-2 rounded-floating border border-destructive/20 bg-destructive/5 px-4 py-3 text-[13px] text-destructive">
<CircleAlert className="h-4 w-4 shrink-0" aria-hidden /> <CircleAlert className="h-4 w-4 shrink-0" aria-hidden />
<span>{error}</span> <span>{error}</span>
</div> </div>
) : null} ) : null}
{loading && !payload ? ( {loading && !payload ? (
<div className="flex h-44 items-center justify-center rounded-[22px] bg-settings-surface text-[13px] text-muted-foreground"> <div className="flex h-44 items-center justify-center rounded-panel bg-settings-surface text-[13px] text-muted-foreground">
<Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden />
{tx("settings.automations.loading", "Loading automations...")} {tx("settings.automations.loading", "Loading automations...")}
</div> </div>
) : filtered.length && selectedJob ? ( ) : filtered.length && selectedJob ? (
<section className="grid min-h-0 overflow-hidden rounded-[22px] bg-settings-surface xl:grid-cols-[minmax(16rem,18rem)_minmax(0,1fr)] xl:items-stretch"> <section className="grid min-h-0 overflow-hidden rounded-panel bg-settings-surface xl:grid-cols-[minmax(16rem,18rem)_minmax(0,1fr)] xl:items-stretch">
<aside className="flex min-h-0 flex-col overflow-hidden border-b border-border/35 bg-settings-surface xl:border-b-0 xl:border-r"> <aside className="flex min-h-0 flex-col overflow-hidden border-b border-border/35 bg-settings-surface xl:border-b-0 xl:border-r">
<div className="flex shrink-0 items-center justify-between gap-3 px-4 py-3"> <div className="flex shrink-0 items-center justify-between gap-3 px-4 py-3">
<h2 className="text-[13px] font-semibold tracking-[-0.01em] text-foreground/85"> <h2 className="text-[13px] font-semibold tracking-[-0.01em] text-foreground/85">
@@ -245,7 +245,7 @@ export function AutomationsSettings({
/> />
</section> </section>
) : ( ) : (
<div className="rounded-[22px] bg-settings-surface px-5 py-12 text-center text-[13px] text-muted-foreground"> <div className="rounded-panel bg-settings-surface px-5 py-12 text-center text-[13px] text-muted-foreground">
<div> <div>
{jobs.length {jobs.length
? tx("settings.automations.noMatches", "No automations match this view.") ? tx("settings.automations.noMatches", "No automations match this view.")
@@ -313,7 +313,7 @@ function AutomationListItem({
aria-pressed={selected} aria-pressed={selected}
onClick={onSelect} onClick={onSelect}
className={cn( className={cn(
"group grid w-full grid-cols-[minmax(0,1fr)_auto] gap-3 rounded-[18px] px-3 py-3.5 text-left transition-colors", "group grid w-full grid-cols-[minmax(0,1fr)_auto] gap-3 rounded-floating px-3 py-3.5 text-left transition-colors",
selected selected
? "bg-background/80 text-foreground" ? "bg-background/80 text-foreground"
: "text-muted-foreground hover:bg-background/55 hover:text-foreground", : "text-muted-foreground hover:bg-background/55 hover:text-foreground",
@@ -433,7 +433,7 @@ function AutomationDetailPanel({
<div className="grid min-h-0 min-w-0 flex-1 overflow-hidden lg:grid-cols-[minmax(0,1fr)_14.5rem]"> <div className="grid min-h-0 min-w-0 flex-1 overflow-hidden lg:grid-cols-[minmax(0,1fr)_14.5rem]">
<div className="min-h-0 min-w-0 space-y-3 overflow-y-auto overscroll-contain p-4 sm:p-5"> <div className="min-h-0 min-w-0 space-y-3 overflow-y-auto overscroll-contain p-4 sm:p-5">
<section className="rounded-[20px] bg-background/55 px-4 py-3.5"> <section className="rounded-floating bg-background/55 px-4 py-3.5">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<div className="text-[11px] font-medium leading-none text-muted-foreground/75"> <div className="text-[11px] font-medium leading-none text-muted-foreground/75">
{messageLabel} {messageLabel}
@@ -506,7 +506,7 @@ function AutomationDetailPanel({
</div> </div>
{job.state.last_error ? ( {job.state.last_error ? (
<div className="rounded-[16px] border border-destructive/20 bg-destructive/8 px-3 py-2 text-[12px] leading-5 text-destructive"> <div className="rounded-floating border border-destructive/20 bg-destructive/8 px-3 py-2 text-[12px] leading-5 text-destructive">
{job.state.last_error} {job.state.last_error}
</div> </div>
) : null} ) : null}
@@ -520,7 +520,7 @@ function AutomationDetailPanel({
> >
{schedule} {schedule}
</AutomationDetail> </AutomationDetail>
<div className="rounded-[18px] bg-background/55 p-3"> <div className="rounded-floating bg-background/55 p-3">
<div className="grid gap-3"> <div className="grid gap-3">
{created ? ( {created ? (
<div> <div>
@@ -672,7 +672,7 @@ function AutomationDetail({
children: ReactNode; children: ReactNode;
}) { }) {
return ( return (
<div className="min-w-0 rounded-[17px] bg-background/55 px-3 py-3"> <div className="min-w-0 rounded-floating bg-background/55 px-3 py-3">
<div className="text-[11px] font-medium leading-none text-muted-foreground/75"> <div className="text-[11px] font-medium leading-none text-muted-foreground/75">
{label} {label}
</div> </div>
@@ -757,7 +757,7 @@ export function AutomationEditDialog({
{job ? ( {job ? (
<DialogContent <DialogContent
aria-describedby={undefined} aria-describedby={undefined}
className="w-[min(calc(100vw-2rem),34rem)] rounded-[26px]" className="w-[min(calc(100vw-2rem),34rem)]"
> >
<form className="space-y-5" onSubmit={submit}> <form className="space-y-5" onSubmit={submit}>
<DialogHeader> <DialogHeader>
@@ -772,7 +772,6 @@ export function AutomationEditDialog({
<Input <Input
value={draft.name} value={draft.name}
onChange={(event) => setDraft((prev) => ({ ...prev, name: event.target.value }))} onChange={(event) => setDraft((prev) => ({ ...prev, name: event.target.value }))}
className="h-10 rounded-[12px]"
/> />
</label> </label>
@@ -784,7 +783,7 @@ export function AutomationEditDialog({
<Textarea <Textarea
value={draft.message} value={draft.message}
onChange={(event) => setDraft((prev) => ({ ...prev, message: event.target.value }))} onChange={(event) => setDraft((prev) => ({ ...prev, message: event.target.value }))}
className="min-h-[160px] resize-none rounded-[12px] text-[13px] leading-5" className="min-h-[160px] resize-none text-[13px] leading-5"
/> />
</label> </label>
) : null} ) : null}
@@ -821,7 +820,6 @@ export function AutomationEditDialog({
onChange={(event) => onChange={(event) =>
setDraft((prev) => ({ ...prev, everyValue: event.target.value })) setDraft((prev) => ({ ...prev, everyValue: event.target.value }))
} }
className="h-10 rounded-[12px]"
/> />
</label> </label>
<label className="block space-y-1.5"> <label className="block space-y-1.5">
@@ -837,7 +835,7 @@ export function AutomationEditDialog({
})) }))
} }
className={cn( className={cn(
"h-10 w-full rounded-[12px] border border-input bg-background px-3 text-[13px] text-foreground transition-colors", "h-10 w-full rounded-control border border-input bg-background px-3 text-[13px] text-foreground transition-colors",
formControlFocusClassName, formControlFocusClassName,
)} )}
> >
@@ -861,7 +859,7 @@ export function AutomationEditDialog({
value={draft.cronExpr} value={draft.cronExpr}
onChange={(event) => setDraft((prev) => ({ ...prev, cronExpr: event.target.value }))} onChange={(event) => setDraft((prev) => ({ ...prev, cronExpr: event.target.value }))}
placeholder="0 9 * * *" placeholder="0 9 * * *"
className="h-10 rounded-[12px] font-mono text-[13px]" className="font-mono text-[13px]"
/> />
</label> </label>
<label className="block space-y-1.5"> <label className="block space-y-1.5">
@@ -872,7 +870,7 @@ export function AutomationEditDialog({
value={draft.tz} value={draft.tz}
onChange={(event) => setDraft((prev) => ({ ...prev, tz: event.target.value }))} onChange={(event) => setDraft((prev) => ({ ...prev, tz: event.target.value }))}
placeholder="Asia/Shanghai" placeholder="Asia/Shanghai"
className="h-10 rounded-[12px] text-[13px]" className="text-[13px]"
/> />
</label> </label>
</div> </div>
@@ -887,13 +885,12 @@ export function AutomationEditDialog({
type="datetime-local" type="datetime-local"
value={draft.atLocal} value={draft.atLocal}
onChange={(event) => setDraft((prev) => ({ ...prev, atLocal: event.target.value }))} onChange={(event) => setDraft((prev) => ({ ...prev, atLocal: event.target.value }))}
className="h-10 rounded-[12px]"
/> />
</label> </label>
) : null} ) : null}
{validation ? ( {validation ? (
<div className="rounded-[12px] bg-destructive/8 px-3 py-2 text-[12px] text-destructive"> <div className="rounded-control bg-destructive/8 px-3 py-2 text-[12px] text-destructive">
{validation} {validation}
</div> </div>
) : null} ) : null}
@@ -905,11 +902,10 @@ export function AutomationEditDialog({
variant="ghost" variant="ghost"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
disabled={saving} disabled={saving}
className="rounded-full"
> >
{tx("settings.automations.cancel", "Cancel")} {tx("settings.automations.cancel", "Cancel")}
</Button> </Button>
<Button type="submit" disabled={Boolean(validation) || saving} className="rounded-full"> <Button type="submit" disabled={Boolean(validation) || saving}>
{saving ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null} {saving ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null}
{tx("settings.automations.save", "Save")} {tx("settings.automations.save", "Save")}
</Button> </Button>
@@ -937,7 +933,7 @@ export function AutomationDeleteDialog({
t(key, { defaultValue: fallback, ...(values ?? {}) }); t(key, { defaultValue: fallback, ...(values ?? {}) });
return ( return (
<Dialog open={Boolean(job)} onOpenChange={onOpenChange}> <Dialog open={Boolean(job)} onOpenChange={onOpenChange}>
<DialogContent className="w-[min(calc(100vw-2rem),26rem)] rounded-[26px]"> <DialogContent className="w-[min(calc(100vw-2rem),26rem)]">
<DialogHeader> <DialogHeader>
<DialogTitle>{tx("settings.automations.deleteTitle", "Delete automation")}</DialogTitle> <DialogTitle>{tx("settings.automations.deleteTitle", "Delete automation")}</DialogTitle>
<DialogDescription> <DialogDescription>
@@ -954,7 +950,6 @@ export function AutomationDeleteDialog({
variant="ghost" variant="ghost"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
disabled={deleting} disabled={deleting}
className="rounded-full"
> >
{tx("settings.automations.cancel", "Cancel")} {tx("settings.automations.cancel", "Cancel")}
</Button> </Button>
@@ -963,7 +958,6 @@ export function AutomationDeleteDialog({
variant="destructive" variant="destructive"
onClick={() => job && void onConfirm(job)} onClick={() => job && void onConfirm(job)}
disabled={!job || deleting} disabled={!job || deleting}
className="rounded-full"
> >
{deleting ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null} {deleting ? <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden /> : null}
{tx("settings.automations.delete", "Delete")} {tx("settings.automations.delete", "Delete")}
@@ -147,19 +147,19 @@ export function ChannelsSettings({
onChange={(event) => onQueryChange(event.target.value)} onChange={(event) => onQueryChange(event.target.value)}
placeholder={tx("settings.channels.searchPlaceholder", "Search channels")} placeholder={tx("settings.channels.searchPlaceholder", "Search channels")}
className={cn( className={cn(
"h-12 rounded-[14px] pl-11 text-[15px]", "h-12 rounded-control pl-11 text-[15px]",
SETTINGS_SEARCH_INPUT_CLASS, SETTINGS_SEARCH_INPUT_CLASS,
)} )}
/> />
</div> </div>
<div className="flex shrink-0 flex-wrap gap-1.5 rounded-[14px] bg-muted/55 p-1"> <div className="flex shrink-0 flex-wrap gap-1.5 rounded-control bg-muted/55 p-1">
{filterOptions.map((option) => ( {filterOptions.map((option) => (
<button <button
key={option.value} key={option.value}
type="button" type="button"
onClick={() => setFilter(option.value)} onClick={() => setFilter(option.value)}
className={cn( className={cn(
"rounded-[11px] px-3 py-1.5 text-[12px] font-medium transition-colors", "rounded-control px-3 py-1.5 text-[12px] font-medium transition-colors",
filter === option.value filter === option.value
? "bg-background text-foreground" ? "bg-background text-foreground"
: "text-muted-foreground hover:text-foreground", : "text-muted-foreground hover:text-foreground",
@@ -153,7 +153,6 @@ export function McpManagementDialog({
showCloseButton={false} showCloseButton={false}
className={cn( className={cn(
"flex h-[min(34rem,calc(100dvh-2rem))] w-[calc(100vw-2rem)] max-w-[42rem] flex-col gap-0 overflow-hidden p-0", "flex h-[min(34rem,calc(100dvh-2rem))] w-[calc(100vw-2rem)] max-w-[42rem] flex-col gap-0 overflow-hidden p-0",
"rounded-[22px]",
)} )}
> >
<div className="flex shrink-0 items-center gap-3 border-b border-border/45 px-5 py-3.5 sm:px-6"> <div className="flex shrink-0 items-center gap-3 border-b border-border/45 px-5 py-3.5 sm:px-6">
@@ -282,7 +281,7 @@ function OverviewPanel({
const previewTools = (preset.tool_names ?? []).slice(0, 4); const previewTools = (preset.tool_names ?? []).slice(0, 4);
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<section className="rounded-[16px] border border-border/55 px-4 py-3.5"> <section className="rounded-floating border border-border/55 px-4 py-3.5">
<h3 className="text-[13px] font-semibold text-foreground"> <h3 className="text-[13px] font-semibold text-foreground">
{tx("settings.mcp.about", "About this MCP")} {tx("settings.mcp.about", "About this MCP")}
</h3> </h3>
@@ -314,7 +313,7 @@ function OverviewPanel({
</dl> </dl>
{previewTools.length ? ( {previewTools.length ? (
<section className="rounded-[16px] bg-muted/45 p-4"> <section className="rounded-floating bg-muted/45 p-4">
<h3 className="text-[13px] font-semibold text-foreground"> <h3 className="text-[13px] font-semibold text-foreground">
{tx("settings.mcp.toolPreview", "Tools")} {tx("settings.mcp.toolPreview", "Tools")}
</h3> </h3>
@@ -375,7 +374,7 @@ function ToolsPanel({
return ( return (
<div <div
className={cn( className={cn(
"flex flex-col gap-3 rounded-[16px] border px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between", "flex flex-col gap-3 rounded-floating border px-4 py-3.5 sm:flex-row sm:items-center sm:justify-between",
preset.error ? "border-destructive/25 bg-destructive/5" : "border-border/55 bg-muted/25", preset.error ? "border-destructive/25 bg-destructive/5" : "border-border/55 bg-muted/25",
)} )}
> >
@@ -444,7 +443,7 @@ function ToolsPanel({
</div> </div>
</div> </div>
<div className="overflow-hidden rounded-[16px] border border-border/55"> <div className="overflow-hidden rounded-floating border border-border/55">
{filteredTools.length ? filteredTools.map((toolName) => { {filteredTools.length ? filteredTools.map((toolName) => {
const selected = selectedTools.has(toolName); const selected = selectedTools.has(toolName);
return ( return (
@@ -463,7 +462,7 @@ function ToolsPanel({
<span <span
aria-hidden aria-hidden
className={cn( className={cn(
"pointer-events-none absolute inset-0 grid place-items-center rounded-[7px] border transition-colors", "pointer-events-none absolute inset-0 grid place-items-center rounded-compact border transition-colors",
selected selected
? "border-foreground bg-foreground text-background" ? "border-foreground bg-foreground text-background"
: "border-border bg-background text-transparent", : "border-border bg-background text-transparent",
@@ -529,7 +528,7 @@ function ConnectionPanel({
<h3 id={`mcp-connection-${preset.name}`} className="sr-only"> <h3 id={`mcp-connection-${preset.name}`} className="sr-only">
{tx("settings.mcp.connectionDetails", "Connection details")} {tx("settings.mcp.connectionDetails", "Connection details")}
</h3> </h3>
<div className="rounded-[16px] bg-muted/40 px-4 py-3.5"> <div className="rounded-floating bg-muted/40 px-4 py-3.5">
{preset.connection_summary ? ( {preset.connection_summary ? (
<div className="min-w-0"> <div className="min-w-0">
<p className="text-[12.5px] font-medium text-muted-foreground">{connectionLabel}</p> <p className="text-[12.5px] font-medium text-muted-foreground">{connectionLabel}</p>
@@ -580,7 +579,7 @@ function ConnectionPanel({
</section> </section>
{preset.error ? ( {preset.error ? (
<div role="alert" className="rounded-[14px] bg-destructive/10 px-3.5 py-3 text-[12.5px] leading-5 text-destructive"> <div role="alert" className="rounded-control bg-destructive/10 px-3.5 py-3 text-[12.5px] leading-5 text-destructive">
{preset.error} {preset.error}
</div> </div>
) : null} ) : null}
@@ -661,7 +660,7 @@ function ConnectionPanel({
function MetricCard({ label, value }: { label: string; value: string }) { function MetricCard({ label, value }: { label: string; value: string }) {
return ( return (
<div className="min-w-0 rounded-[14px] bg-muted/45 px-3.5 py-3"> <div className="min-w-0 rounded-control bg-muted/45 px-3.5 py-3">
<dt className="text-[12px] font-medium text-muted-foreground">{label}</dt> <dt className="text-[12px] font-medium text-muted-foreground">{label}</dt>
<dd className="mt-1 truncate text-[14px] font-semibold text-foreground">{value}</dd> <dd className="mt-1 truncate text-[14px] font-semibold text-foreground">{value}</dd>
</div> </div>
@@ -1112,7 +1112,7 @@ function CliRunRow({ run, active, app }: { run: CliRunSummary; active: boolean;
<span <span
data-testid={`activity-cli-logo-${run.name.toLowerCase()}`} data-testid={`activity-cli-logo-${run.name.toLowerCase()}`}
className={cn( className={cn(
"grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px] border text-[6.5px] font-semibold text-white", "grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-mark border text-[6.5px] font-semibold text-white",
rowActive && "animate-pulse", rowActive && "animate-pulse",
)} )}
style={{ style={{
@@ -1190,7 +1190,7 @@ function McpRunRow({ run, active, preset }: { run: McpRunSummary; active: boolea
<span <span
data-testid={`activity-mcp-logo-${run.presetName.toLowerCase()}`} data-testid={`activity-mcp-logo-${run.presetName.toLowerCase()}`}
className={cn( className={cn(
"grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-[4px] border text-[6.5px] font-semibold text-white", "grid h-4 w-4 shrink-0 place-items-center overflow-hidden rounded-mark border text-[6.5px] font-semibold text-white",
rowActive && "animate-pulse", rowActive && "animate-pulse",
)} )}
style={{ style={{
+1 -1
View File
@@ -203,7 +203,7 @@ export function PromptRail({
aria-hidden aria-hidden
data-testid={previewVisible ? "prompt-rail-preview" : undefined} data-testid={previewVisible ? "prompt-rail-preview" : undefined}
className={cn( className={cn(
"pointer-events-none absolute left-10 z-30 w-[34rem] max-w-[calc(100vw-4rem)] -translate-y-1/2 rounded-[20px] px-4 py-3 text-left", "pointer-events-none absolute left-10 z-30 w-[34rem] max-w-[calc(100vw-4rem)] -translate-y-1/2 rounded-panel px-4 py-3 text-left",
floatingSurfaceElevationClassName, floatingSurfaceElevationClassName,
"transition-[opacity,transform] duration-150", "transition-[opacity,transform] duration-150",
previewVisible previewVisible
@@ -41,12 +41,12 @@ export function SessionInfoPopover({ sessionKey, token, title }: SessionInfoPopo
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { jobs, loading, loadFailed, now } = useSessionAutomationJobs(open, token, sessionKey); const { jobs, loading, loadFailed, now } = useSessionAutomationJobs(open, token, sessionKey);
const automationContent = loading ? ( const automationContent = loading ? (
<div className="flex items-center gap-2 rounded-[16px] bg-muted/45 px-3 py-3 text-[12.5px] text-muted-foreground"> <div className="flex items-center gap-2 rounded-floating bg-muted/45 px-3 py-3 text-[12.5px] text-muted-foreground">
<RefreshCcw className="h-3.5 w-3.5 animate-spin" /> <RefreshCcw className="h-3.5 w-3.5 animate-spin" />
{t("thread.sessionInfo.loading")} {t("thread.sessionInfo.loading")}
</div> </div>
) : loadFailed ? ( ) : loadFailed ? (
<div className="flex items-center gap-2 rounded-[16px] bg-destructive/10 px-3 py-3 text-[12.5px] text-destructive"> <div className="flex items-center gap-2 rounded-floating bg-destructive/10 px-3 py-3 text-[12.5px] text-destructive">
<CircleAlert className="h-3.5 w-3.5" /> <CircleAlert className="h-3.5 w-3.5" />
{t("thread.sessionInfo.loadFailed")} {t("thread.sessionInfo.loadFailed")}
</div> </div>
@@ -57,7 +57,7 @@ export function SessionInfoPopover({ sessionKey, token, title }: SessionInfoPopo
))} ))}
</div> </div>
) : ( ) : (
<div className="rounded-[16px] bg-muted/35 px-3 py-3 text-[12.5px] leading-relaxed text-muted-foreground"> <div className="rounded-floating bg-muted/35 px-3 py-3 text-[12.5px] leading-relaxed text-muted-foreground">
{t("thread.sessionInfo.empty")} {t("thread.sessionInfo.empty")}
</div> </div>
); );
@@ -124,7 +124,7 @@ function AutomationRow({ job, now }: { job: SessionAutomationJob; now: number })
: "bg-muted-foreground/35"; : "bg-muted-foreground/35";
return ( return (
<div className="rounded-[16px] px-3 py-2.5 transition-colors hover:bg-muted/40"> <div className="rounded-floating px-3 py-2.5 transition-colors hover:bg-muted/40">
<div className="flex items-start gap-2.5"> <div className="flex items-start gap-2.5">
<span className={cn("mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full", statusClass)} /> <span className={cn("mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full", statusClass)} />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
@@ -2246,8 +2246,8 @@ export function ThreadComposer({
className={cn( className={cn(
"thread-composer-surface group/composer relative mx-auto flex w-full flex-col overflow-visible transition-all duration-200", "thread-composer-surface group/composer relative mx-auto flex w-full flex-col overflow-visible transition-all duration-200",
isHero isHero
? "max-w-[58rem] rounded-[28px] bg-muted/30 focus-within:bg-muted/50 dark:bg-card dark:focus-within:bg-white/[0.06]" ? "max-w-[58rem] rounded-prominent bg-muted/30 focus-within:bg-muted/50 dark:bg-card dark:focus-within:bg-white/[0.06]"
: "max-w-[49.5rem] rounded-[22px] bg-muted/30 focus-within:bg-muted/50 dark:bg-card dark:focus-within:bg-white/[0.06]", : "max-w-[49.5rem] rounded-panel bg-muted/30 focus-within:bg-muted/50 dark:bg-card dark:focus-within:bg-white/[0.06]",
interactionDisabled && "opacity-60", interactionDisabled && "opacity-60",
sessionDragPreview && "ring-1 ring-primary/25", sessionDragPreview && "ring-1 ring-primary/25",
isDragging && "ring-2 ring-primary/40 motion-reduce:ring-0 motion-reduce:border-primary", isDragging && "ring-2 ring-primary/40 motion-reduce:ring-0 motion-reduce:border-primary",
@@ -2610,7 +2610,7 @@ function QueuedPromptStack({
role="group" role="group"
data-state="enter" data-state="enter"
className={cn( className={cn(
"composer-status-strip relative z-20 mx-3 mt-3 overflow-hidden rounded-[18px]", "composer-status-strip relative z-20 mx-3 mt-3 overflow-hidden rounded-floating",
"border border-black/[0.05] bg-popover/90 p-1.5", "border border-black/[0.05] bg-popover/90 p-1.5",
"shadow-[0_10px_28px_rgba(15,23,42,0.07)] backdrop-blur-md", "shadow-[0_10px_28px_rgba(15,23,42,0.07)] backdrop-blur-md",
"dark:border-white/[0.08] dark:bg-popover/90 dark:shadow-[0_14px_34px_rgba(0,0,0,0.30)]", "dark:border-white/[0.08] dark:bg-popover/90 dark:shadow-[0_14px_34px_rgba(0,0,0,0.30)]",
@@ -2688,7 +2688,7 @@ function QueuedPromptRow({
}} }}
onDragEnd={onDragEnd} onDragEnd={onDragEnd}
className={cn( className={cn(
"queued-prompt-row group/queued flex min-h-8 items-center gap-1.5 rounded-[12px] px-2 py-0.5", "queued-prompt-row group/queued flex min-h-8 items-center gap-1.5 rounded-control px-2 py-0.5",
"text-[13px] transition-colors hover:bg-muted/55 dark:hover:bg-white/[0.055]", "text-[13px] transition-colors hover:bg-muted/55 dark:hover:bg-white/[0.055]",
isHero && "text-[13.5px]", isHero && "text-[13.5px]",
)} )}
@@ -2973,7 +2973,7 @@ function MentionCandidateLogo({
return ( return (
<span <span
className={cn( className={cn(
"flex h-5 w-5 shrink-0 items-center justify-center overflow-hidden rounded-[5px]", "flex h-5 w-5 shrink-0 items-center justify-center overflow-hidden rounded-compact",
selected ? "bg-background/55" : "bg-transparent", selected ? "bg-background/55" : "bg-transparent",
)} )}
> >
@@ -2991,7 +2991,7 @@ function MentionCandidateLogo({
} }
return ( return (
<span <span
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-[5px] text-[7.5px] font-semibold text-white" className="flex h-5 w-5 shrink-0 items-center justify-center rounded-compact text-[7.5px] font-semibold text-white"
style={{ backgroundColor: color }} style={{ backgroundColor: color }}
> >
{candidate.initials} {candidate.initials}
@@ -3135,7 +3135,7 @@ function AttachmentChip({
return ( return (
<div <div
className={cn( className={cn(
"group relative flex items-center gap-2 rounded-[12px] border px-2 py-1.5", "group relative flex items-center gap-2 rounded-control border px-2 py-1.5",
"transition-colors motion-reduce:transition-none", "transition-colors motion-reduce:transition-none",
tone, tone,
)} )}
@@ -221,7 +221,7 @@ export function WorkspaceProjectPicker({
"flex min-h-[48px] w-full cursor-default gap-3 px-3 py-2.5 focus:bg-muted/55", "flex min-h-[48px] w-full cursor-default gap-3 px-3 py-2.5 focus:bg-muted/55",
)} )}
> >
<span className="grid h-8 w-8 shrink-0 place-items-center rounded-[12px] bg-muted text-foreground/80"> <span className="grid h-8 w-8 shrink-0 place-items-center rounded-control bg-muted text-foreground/80">
<Folder className="h-4 w-4" /> <Folder className="h-4 w-4" />
</span> </span>
<span className="min-w-0 flex-1"> <span className="min-w-0 flex-1">
@@ -328,7 +328,7 @@ export function WorkspaceAccessMenu({
aria-label={accessAriaLabel} aria-label={accessAriaLabel}
title={accessLabel} title={accessLabel}
className={cn( className={cn(
"thread-composer-access touch-target min-w-0 max-w-[min(12.5rem,42vw)] whitespace-nowrap rounded-[10px] border border-transparent font-semibold shadow-none", "thread-composer-access touch-target min-w-0 max-w-[min(12.5rem,42vw)] whitespace-nowrap rounded-control border border-transparent font-semibold shadow-none",
isHero ? "h-8 px-2.5 text-[12px]" : "h-9 px-3 text-[12.5px]", isHero ? "h-8 px-2.5 text-[12px]" : "h-9 px-3 text-[12.5px]",
isFull isFull
? "bg-transparent text-orange-600 hover:bg-orange-500/8 dark:text-orange-300 dark:hover:bg-orange-400/10" ? "bg-transparent text-orange-600 hover:bg-orange-500/8 dark:text-orange-300 dark:hover:bg-orange-400/10"
@@ -61,7 +61,7 @@ function WebFavicon({ host, active }: { host: string; active: boolean }) {
<img <img
src={logoUrl} src={logoUrl}
alt="" alt=""
className={`h-4 w-4 shrink-0 rounded-[3px] object-contain${active ? " animate-pulse" : ""}`} className={`h-4 w-4 shrink-0 rounded-mark object-contain${active ? " animate-pulse" : ""}`}
decoding="async" decoding="async"
loading="lazy" loading="lazy"
referrerPolicy="no-referrer" referrerPolicy="no-referrer"
+2 -2
View File
@@ -37,7 +37,7 @@ const AlertDialogContent = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
modalSurfaceClassName, modalSurfaceClassName,
"grid w-full max-w-lg origin-center gap-4 rounded-[22px] p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "grid w-full max-w-lg origin-center gap-4 rounded-modal p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
className, className,
)} )}
{...props} {...props}
@@ -64,7 +64,7 @@ const AlertDialogFooter = ({
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className, className,
)} )}
{...props} {...props}
+3 -3
View File
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", "inline-flex items-center justify-center whitespace-nowrap rounded-control text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{ {
variants: { variants: {
variant: { variant: {
@@ -21,8 +21,8 @@ const buttonVariants = cva(
}, },
size: { size: {
default: "h-10 px-4 py-2", default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3", sm: "h-9 px-3",
lg: "h-11 rounded-md px-8", lg: "h-11 px-8",
icon: "h-9 w-9", icon: "h-9 w-9",
}, },
}, },
+2 -2
View File
@@ -45,7 +45,7 @@ const DialogContent = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
modalSurfaceClassName, modalSurfaceClassName,
"grid w-full max-w-lg origin-center gap-4 rounded-[22px] p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "grid w-full max-w-lg origin-center gap-4 rounded-modal p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
className, className,
)} )}
{...props} {...props}
@@ -84,7 +84,7 @@ const DialogFooter = ({
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className, className,
)} )}
{...props} {...props}
+2 -2
View File
@@ -2,7 +2,7 @@ export const floatingSurfaceElevationClassName =
"bg-popover text-popover-foreground shadow-[0_8px_24px_rgba(15,23,42,0.10)] dark:shadow-[0_12px_28px_rgba(0,0,0,0.32)]"; "bg-popover text-popover-foreground shadow-[0_8px_24px_rgba(15,23,42,0.10)] dark:shadow-[0_12px_28px_rgba(0,0,0,0.32)]";
export const floatingSurfaceVisualClassName = export const floatingSurfaceVisualClassName =
`rounded-[18px] p-1.5 ${floatingSurfaceElevationClassName}`; `rounded-floating p-1.5 ${floatingSurfaceElevationClassName}`;
export const modalOverlayClassName = export const modalOverlayClassName =
"fixed inset-0 z-50 bg-black/45 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"; "fixed inset-0 z-50 bg-black/45 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0";
@@ -17,7 +17,7 @@ export const floatingSurfaceMotionClassName =
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"; "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0";
export const floatingItemClassName = export const floatingItemClassName =
"relative flex min-h-8 select-none items-center gap-2 rounded-[12px] px-2.5 py-2 text-[13px] outline-none transition-colors [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0"; "relative flex min-h-8 select-none items-center gap-2 rounded-control px-2.5 py-2 text-[13px] outline-none transition-colors [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0";
export const floatingItemFocusClassName = export const floatingItemFocusClassName =
"focus:bg-foreground/[0.055] focus:text-foreground dark:focus:bg-white/[0.08]"; "focus:bg-foreground/[0.055] focus:text-foreground dark:focus:bg-white/[0.08]";
+1 -1
View File
@@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input <input
type={type} type={type}
className={cn( className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", "flex h-10 w-full rounded-control border border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
formControlFocusClassName, formControlFocusClassName,
className, className,
)} )}
+1 -1
View File
@@ -10,7 +10,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
return ( return (
<textarea <textarea
className={cn( className={cn(
"flex min-h-[60px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", "flex min-h-[60px] w-full rounded-control border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
formControlFocusClassName, formControlFocusClassName,
className, className,
)} )}
+1 -1
View File
@@ -18,7 +18,7 @@ const TooltipContent = React.forwardRef<
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
floatingSurfaceElevationClassName, floatingSurfaceElevationClassName,
"z-50 overflow-hidden rounded-[10px] px-3 py-1.5 text-xs animate-in fade-in-0 zoom-in-95", "z-50 overflow-hidden rounded-control px-3 py-1.5 text-xs animate-in fade-in-0 zoom-in-95",
className, className,
)} )}
{...props} {...props}
+11 -2
View File
@@ -38,6 +38,15 @@
--temporary-foreground: 17 88% 32%; --temporary-foreground: 17 88% 32%;
--temporary-border: 17 88% 40%; --temporary-border: 17 88% 40%;
--radius: 0.4375rem; --radius: 0.4375rem;
/* Shared shape scale: marks, compact content, controls, and progressively larger surfaces. */
--radius-mark: 0.25rem;
--radius-compact: 0.5rem;
--radius-control: 0.75rem;
--radius-floating: 1.125rem;
--radius-panel: 1.375rem;
--radius-modal: 1.375rem;
--radius-prominent: 1.75rem;
--radius-pill: 9999px;
--sidebar: 40 8% 96.8%; --sidebar: 40 8% 96.8%;
--sidebar-foreground: 0 0% 3.9%; --sidebar-foreground: 0 0% 3.9%;
--sidebar-selected: 40 1% 89.4%; --sidebar-selected: 40 1% 89.4%;
@@ -121,7 +130,7 @@
*::-webkit-scrollbar-thumb { *::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb); background-color: var(--scrollbar-thumb);
border-radius: 9999px; border-radius: var(--radius-pill);
} }
*::-webkit-scrollbar-thumb:hover { *::-webkit-scrollbar-thumb:hover {
@@ -567,7 +576,7 @@
} }
.scrollbar-thin::-webkit-scrollbar-thumb { .scrollbar-thin::-webkit-scrollbar-thumb {
background-color: hsl(var(--muted-foreground) / 0.4); background-color: hsl(var(--muted-foreground) / 0.4);
border-radius: 9999px; border-radius: var(--radius-pill);
} }
.scrollbar-track-transparent { .scrollbar-track-transparent {
scrollbar-gutter: stable; scrollbar-gutter: stable;
+1 -1
View File
@@ -493,7 +493,7 @@ describe("ChatList", () => {
expect(tabSurface).toContainElement(paneList); expect(tabSurface).toContainElement(paneList);
const activePane = within(tabGroup).getByRole("button", { name: "Research pane" }); const activePane = within(tabGroup).getByRole("button", { name: "Research pane" });
expect(activePane).toHaveAttribute("aria-current", "true"); expect(activePane).toHaveAttribute("aria-current", "true");
expect(activePane.closest("[data-sidebar-pane]")).toHaveClass("rounded-[0.65rem]"); expect(activePane.closest("[data-sidebar-pane]")).toHaveClass("rounded-control");
expect(activePane.querySelector("[data-sidebar-selection-track]")) expect(activePane.querySelector("[data-sidebar-selection-track]"))
.toHaveAttribute("data-active", "true"); .toHaveAttribute("data-active", "true");
expect(screen.getByRole("button", { expect(screen.getByRole("button", {
+1 -1
View File
@@ -54,7 +54,7 @@ describe("CodeBlock", () => {
expect(screen.getByTestId("plain-code-fallback")).toHaveClass("py-4", "pl-5", "pr-14"); expect(screen.getByTestId("plain-code-fallback")).toHaveClass("py-4", "pl-5", "pr-14");
const container = screen.getByTestId("plain-code-fallback").closest(".not-prose"); const container = screen.getByTestId("plain-code-fallback").closest(".not-prose");
expect(container).toHaveClass("relative", "rounded-[18px]", "bg-secondary/70"); expect(container).toHaveClass("relative", "rounded-floating", "bg-secondary/70");
expect(container).not.toHaveClass("border"); expect(container).not.toHaveClass("border");
expect(container).toHaveAttribute("data-language", "ts"); expect(container).toHaveAttribute("data-language", "ts");
+3 -3
View File
@@ -108,7 +108,7 @@ describe("MessageBubble", () => {
const pill = screen.getByText("hello"); const pill = screen.getByText("hello");
expect(row).toHaveClass("ml-auto", "flex"); expect(row).toHaveClass("ml-auto", "flex");
expect(pill).toHaveClass("ml-auto", "w-fit", "rounded-[18px]"); expect(pill).toHaveClass("ml-auto", "w-fit", "rounded-floating");
expect(screen.getByRole("button", { name: "Copy" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Copy" })).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Fork" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Fork" })).not.toBeInTheDocument();
}); });
@@ -286,7 +286,7 @@ describe("MessageBubble", () => {
expect(command.getAttribute("style")).toContain("var(--inline-token-highlight)"); expect(command.getAttribute("style")).toContain("var(--inline-token-highlight)");
expect(command.className).not.toMatch(/(?:^|\s)(?:bg-|border|ring|rounded)/); expect(command.className).not.toMatch(/(?:^|\s)(?:bg-|border|ring|rounded)/);
expect(command.parentElement).toHaveTextContent("/model gpt-5"); expect(command.parentElement).toHaveTextContent("/model gpt-5");
expect(command.parentElement).toHaveClass("rounded-[18px]", "bg-secondary/70"); expect(command.parentElement).toHaveClass("rounded-floating", "bg-secondary/70");
}); });
it("keeps unknown and invalid slash commands as plain message text", () => { it("keeps unknown and invalid slash commands as plain message text", () => {
@@ -934,7 +934,7 @@ describe("MessageBubble", () => {
const { container } = render(<MessageBubble message={message} />); const { container } = render(<MessageBubble message={message} />);
const imageButton = screen.getByRole("button", { name: /view image/i }); const imageButton = screen.getByRole("button", { name: /view image/i });
expect(imageButton).toHaveClass("w-[min(100%,34rem)]", "rounded-[20px]"); expect(imageButton).toHaveClass("w-[min(100%,34rem)]", "rounded-panel");
expect(imageButton).toHaveClass( expect(imageButton).toHaveClass(
"border", "border",
"border-border/60", "border-border/60",
+1 -1
View File
@@ -165,7 +165,7 @@ describe("Settings models", () => {
expect(editor).toHaveClass( expect(editor).toHaveClass(
"slide-in-from-top-1", "slide-in-from-top-1",
"lg:max-w-6xl", "lg:max-w-6xl",
"rounded-[18px]", "rounded-floating",
); );
expect(within(editor).getByDisplayValue("Primary")).toBeInTheDocument(); expect(within(editor).getByDisplayValue("Primary")).toBeInTheDocument();
const deleteButton = within(editor).getByRole("button", { name: "Delete" }); const deleteButton = within(editor).getByRole("button", { name: "Delete" });
+1 -1
View File
@@ -554,7 +554,7 @@ describe("ThreadComposer", () => {
expect(input.className).toContain("min-h-[50px]"); expect(input.className).toContain("min-h-[50px]");
expect(input.className).toContain("text-[16px]"); expect(input.className).toContain("text-[16px]");
expect(input.parentElement?.parentElement?.className).toContain("max-w-[49.5rem]"); expect(input.parentElement?.parentElement?.className).toContain("max-w-[49.5rem]");
expect(input.parentElement?.parentElement?.className).toContain("rounded-[22px]"); expect(input.parentElement?.parentElement?.className).toContain("rounded-panel");
expect(input.parentElement?.parentElement?.className).not.toContain("shadow-"); expect(input.parentElement?.parentElement?.className).not.toContain("shadow-");
expect(screen.getByRole("button", { name: "Attach files" }).className).toContain("bg-card"); expect(screen.getByRole("button", { name: "Attach files" }).className).toContain("bg-card");
expect(screen.getByRole("button", { name: "Send message" }).className).toContain("bg-foreground"); expect(screen.getByRole("button", { name: "Send message" }).className).toContain("bg-foreground");
+83
View File
@@ -0,0 +1,83 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import {
AlertDialog,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogTitle,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
describe("UI shape system", () => {
it("gives standard controls one shared radius", () => {
render(
<>
<Button>Continue</Button>
<Input aria-label="Name" />
<Textarea aria-label="Description" />
</>,
);
expect(screen.getByRole("button", { name: "Continue" })).toHaveClass("rounded-control");
expect(screen.getByRole("textbox", { name: "Name" })).toHaveClass("rounded-control");
expect(screen.getByRole("textbox", { name: "Description" })).toHaveClass(
"rounded-control",
);
});
it("gives dialogs and alert dialogs one shared modal radius", () => {
const dialog = render(
<Dialog open>
<DialogContent showCloseButton={false}>
<DialogTitle>Edit name</DialogTitle>
<DialogDescription>Choose a new name.</DialogDescription>
</DialogContent>
</Dialog>,
);
expect(screen.getByRole("dialog", { name: "Edit name" })).toHaveClass("rounded-modal");
dialog.unmount();
render(
<AlertDialog open>
<AlertDialogContent>
<AlertDialogTitle>Delete item?</AlertDialogTitle>
<AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
</AlertDialogContent>
</AlertDialog>,
);
expect(screen.getByRole("alertdialog", { name: "Delete item?" })).toHaveClass(
"rounded-modal",
);
});
it("keeps floating surfaces and their items on the shared radius scale", () => {
render(
<DropdownMenu open>
<DropdownMenuTrigger>Open menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Rename</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>,
);
expect(screen.getByRole("menu")).toHaveClass("rounded-floating");
expect(screen.getByRole("menuitem", { name: "Rename" })).toHaveClass(
"rounded-control",
);
});
});
+7
View File
@@ -51,6 +51,13 @@ export default {
lg: "var(--radius)", lg: "var(--radius)",
md: "calc(var(--radius) - 2px)", md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)", sm: "calc(var(--radius) - 4px)",
mark: "var(--radius-mark)",
compact: "var(--radius-compact)",
control: "var(--radius-control)",
floating: "var(--radius-floating)",
panel: "var(--radius-panel)",
modal: "var(--radius-modal)",
prominent: "var(--radius-prominent)",
}, },
colors: { colors: {
background: "hsl(var(--background))", background: "hsl(var(--background))",