authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-22 12:22:20-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-22 12:22:20-07:00
log83bbfa7e87e16a3c21413fe55524c1d05ce88dab
treed4d6fc2ec60a35b3a97f1db47f30676c40585814
parent1c2f933892cce833823fcfbf3099a51813ba67fb
signature Signed by PGP key B5690EEEBB952194

refactor(web): sidebar alerts use the standard variants; one keycap (#13043)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

12 files changed, 52 insertions(+), 71 deletions(-)

apps/web/src/components/CommandPalette.tsx+2-2
...@@ -2874,7 +2874,7 @@ function OpenCommandPaletteDialog(props: {...@@ -2874,7 +2874,7 @@ function OpenCommandPaletteDialog(props: {
2874 }2874 }
2875 >2875 >
2876 <span>{isRemoteProjectPending ? "Working" : remoteProjectButtonLabel}</span>2876 <span>{isRemoteProjectPending ? "Working" : remoteProjectButtonLabel}</span>
2877 <KbdGroup className="pointer-events-none -me-0.5 items-center gap-1">2877 <KbdGroup className="pointer-events-none -me-0.5">
2878 <Kbd>Enter</Kbd>2878 <Kbd>Enter</Kbd>
2879 </KbdGroup>2879 </KbdGroup>
2880 </TooltipTrigger>2880 </TooltipTrigger>
...@@ -2917,7 +2917,7 @@ function OpenCommandPaletteDialog(props: {...@@ -2917,7 +2917,7 @@ function OpenCommandPaletteDialog(props: {
2917 <span>2917 <span>
2918 {isCloneDestinationStep && isRemoteProjectPending ? "Cloning" : submitActionLabel}2918 {isCloneDestinationStep && isRemoteProjectPending ? "Cloning" : submitActionLabel}
2919 </span>2919 </span>
2920 <KbdGroup className="pointer-events-none -me-0.5 items-center gap-1">2920 <KbdGroup className="pointer-events-none -me-0.5">
2921 <Kbd>{hasHighlightedBrowseItem ? `${submitModifierLabel} Enter` : "Enter"}</Kbd>2921 <Kbd>{hasHighlightedBrowseItem ? `${submitModifierLabel} Enter` : "Enter"}</Kbd>
2922 </KbdGroup>2922 </KbdGroup>
2923 </TooltipTrigger>2923 </TooltipTrigger>
apps/web/src/components/CommandPaletteContent.tsx+4-4
...@@ -52,7 +52,7 @@ export function CommandPaletteContent({...@@ -52,7 +52,7 @@ export function CommandPaletteContent({
52 <CommandPanel className={panelClassName}>{children}</CommandPanel>52 <CommandPanel className={panelClassName}>{children}</CommandPanel>
53 <CommandFooter className="gap-3 max-sm:flex-col max-sm:items-start">53 <CommandFooter className="gap-3 max-sm:flex-col max-sm:items-start">
54 <div className="flex items-center gap-3">54 <div className="flex items-center gap-3">
55 <KbdGroup className="items-center gap-1.5">55 <KbdGroup>
56 <Kbd>56 <Kbd>
57 <ArrowUpIcon />57 <ArrowUpIcon />
58 </Kbd>58 </Kbd>
...@@ -62,18 +62,18 @@ export function CommandPaletteContent({...@@ -62,18 +62,18 @@ export function CommandPaletteContent({
62 <span>Navigate</span>62 <span>Navigate</span>
63 </KbdGroup>63 </KbdGroup>
64 {footerActionLabel !== undefined ? (64 {footerActionLabel !== undefined ? (
65 <KbdGroup className="items-center gap-1.5">65 <KbdGroup>
66 <Kbd>Enter</Kbd>66 <Kbd>Enter</Kbd>
67 <span>{footerActionLabel}</span>67 <span>{footerActionLabel}</span>
68 </KbdGroup>68 </KbdGroup>
69 ) : null}69 ) : null}
70 {showBackHint ? (70 {showBackHint ? (
71 <KbdGroup className="items-center gap-1.5">71 <KbdGroup>
72 <Kbd>Backspace</Kbd>72 <Kbd>Backspace</Kbd>
73 <span>Back</span>73 <span>Back</span>
74 </KbdGroup>74 </KbdGroup>
75 ) : null}75 ) : null}
76 <KbdGroup className="items-center gap-1.5">76 <KbdGroup>
77 <Kbd>Esc</Kbd>77 <Kbd>Esc</Kbd>
78 <span>{escapeLabel}</span>78 <span>{escapeLabel}</span>
79 </KbdGroup>79 </KbdGroup>
apps/web/src/components/LegacySidebar.tsx+5-14
...@@ -2673,18 +2673,13 @@ function LocalSecondaryStatus() {...@@ -2673,18 +2673,13 @@ function LocalSecondaryStatus() {
2673 return (2673 return (
2674 <SidebarGroup>2674 <SidebarGroup>
2675 {connecting.length > 0 ? (2675 {connecting.length > 0 ? (
2676 <Alert2676 <Alert variant="sidebar">
2677 variant="default"
2678 className="rounded-2xl border-border/40 bg-accent/40 text-muted-foreground"
2679 >
2680 <Spinner />2677 <Spinner />
2681 <AlertTitle className="text-xs font-medium text-foreground">2678 <AlertTitle>Connecting {connecting.join(", ")}</AlertTitle>
2682 Connecting {connecting.join(", ")}
2683 </AlertTitle>
2684 </Alert>2679 </Alert>
2685 ) : null}2680 ) : null}
2686 {failed.length > 0 ? (2681 {failed.length > 0 ? (
2687 <Alert variant="warning" className="rounded-2xl border-warning/40 bg-warning/8">2682 <Alert variant="warning">
2688 <TriangleAlertIcon />2683 <TriangleAlertIcon />
2689 <AlertTitle>Couldn't connect {failed.map((entry) => entry.label).join(", ")}</AlertTitle>2684 <AlertTitle>Couldn't connect {failed.map((entry) => entry.label).join(", ")}</AlertTitle>
2690 <AlertDescription>2685 <AlertDescription>
...@@ -2980,11 +2975,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(...@@ -2980,11 +2975,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
2980 >2975 >
2981 <SearchIcon />2976 <SearchIcon />
2982 <span className="flex-1 truncate">Search</span>2977 <span className="flex-1 truncate">Search</span>
2983 {commandPaletteShortcutLabel ? (2978 {commandPaletteShortcutLabel ? <Kbd>{commandPaletteShortcutLabel}</Kbd> : null}
2984 <Kbd className="h-4 min-w-0 rounded-sm px-1.5 text-[10px]">
2985 {commandPaletteShortcutLabel}
2986 </Kbd>
2987 ) : null}
2988 </CommandDialogTrigger>2979 </CommandDialogTrigger>
2989 </SidebarMenuItem>2980 </SidebarMenuItem>
2990 </SidebarMenu>2981 </SidebarMenu>
...@@ -2993,7 +2984,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(...@@ -2993,7 +2984,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
2993 >2984 >
2994 {showArm64IntelBuildWarning && arm64IntelBuildWarningDescription ? (2985 {showArm64IntelBuildWarning && arm64IntelBuildWarningDescription ? (
2995 <SidebarGroup>2986 <SidebarGroup>
2996 <Alert variant="warning" className="rounded-2xl border-warning/40 bg-warning/8">2987 <Alert variant="warning">
2997 <TriangleAlertIcon />2988 <TriangleAlertIcon />
2998 <AlertTitle>Intel build on Apple Silicon</AlertTitle>2989 <AlertTitle>Intel build on Apple Silicon</AlertTitle>
2999 <AlertDescription>{arm64IntelBuildWarningDescription}</AlertDescription>2990 <AlertDescription>{arm64IntelBuildWarningDescription}</AlertDescription>
apps/web/src/components/chat/ModelListRow.tsx+1-3
...@@ -98,9 +98,7 @@ export const ModelListRow = memo(function ModelListRow(props: {...@@ -98,9 +98,7 @@ export const ModelListRow = memo(function ModelListRow(props: {
98 {props.showSelection && props.isSelected ? (98 {props.showSelection && props.isSelected ? (
99 <CheckIcon className="size-3.5" aria-hidden="true" />99 <CheckIcon className="size-3.5" aria-hidden="true" />
100 ) : null}100 ) : null}
101 {props.jumpLabel ? (101 {props.jumpLabel ? <Kbd>{props.jumpLabel}</Kbd> : null}
102 <Kbd className="h-4 min-w-0 rounded-sm px-1.5 text-[10px]">{props.jumpLabel}</Kbd>
103 ) : null}
104 <Tooltip>102 <Tooltip>
105 <TooltipTrigger103 <TooltipTrigger
106 render={104 render={
apps/web/src/components/chat/ProviderStatusBanner.tsx+24-35
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1import { type ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";1import { type ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
2import { memo } from "react";2import { memo } from "react";
3import { InfoIcon, XIcon } from "lucide-react";3import { InfoIcon, XIcon } from "lucide-react";
4import { cn } from "~/lib/utils";4import { Alert, AlertAction, AlertDescription, AlertTitle } from "../ui/alert";
5import { Button } from "../ui/button";5import { Button, InlineButton } from "../ui/button";
6import { formatProviderDriverKindLabel } from "../../providerModels";6import { formatProviderDriverKindLabel } from "../../providerModels";
7import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";7import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
88
...@@ -83,48 +83,37 @@ export const ProviderStatusBanner = memo(function ProviderStatusBanner({...@@ -83,48 +83,37 @@ export const ProviderStatusBanner = memo(function ProviderStatusBanner({
8383
84 return (84 return (
85 <div className="pointer-events-auto mx-auto w-fit max-w-[calc(100%-2rem)] pt-3">85 <div className="pointer-events-auto mx-auto w-fit max-w-[calc(100%-2rem)] pt-3">
86 <div86 <Alert
87 className={cn(87 variant={status.status === "warning" ? "warning" : "error"}
88 "alert-glass relative inline-flex items-center gap-3 rounded-xl border py-3 ps-3.5 pe-10 text-card-foreground text-sm",88 surface="glass"
89 status.status === "warning"89 controlAlignment="first-line"
90 ? "border-warning/32 [&_svg]:text-warning"
91 : "border-destructive/32 text-destructive-foreground [&_svg]:text-destructive",
92 )}
93 data-variant={status.status === "warning" ? "warning" : "error"}
94 role="alert"
95 >90 >
96 <InfoIcon className="size-4 shrink-0" aria-hidden />91 <InfoIcon />
97 <div className="flex min-w-0 flex-col gap-1">92 <AlertTitle>{title}</AlertTitle>
98 <div className="font-medium">{title}</div>93 <AlertDescription>
99 <Tooltip>94 <Tooltip>
100 <TooltipTrigger95 <TooltipTrigger render={<div className="line-clamp-3" />}>{message}</TooltipTrigger>
101 render={<div className="line-clamp-3 text-muted-foreground">{message}</div>}
102 />
103 <TooltipPopup side="top" className="whitespace-pre-wrap">96 <TooltipPopup side="top" className="whitespace-pre-wrap">
104 {message}97 {message}
105 </TooltipPopup>98 </TooltipPopup>
106 </Tooltip>99 </Tooltip>
107 {onOpenProviderSetup && hasProviderSetup(status) ? (100 {onOpenProviderSetup && hasProviderSetup(status) ? (
108 <Button101 <InlineButton onClick={() => onOpenProviderSetup(status.instanceId)}>
109 className="self-start px-0 text-foreground"
110 onClick={() => onOpenProviderSetup(status.instanceId)}
111 size="xs"
112 variant="link"
113 >
114 Open provider setup102 Open provider setup
115 </Button>103 </InlineButton>
116 ) : null}104 ) : null}
117 </div>105 </AlertDescription>
118 <Button106 <AlertAction>
119 aria-label={`Dismiss ${providerName} provider ${status.status}`}107 <Button
120 className="absolute top-2 right-2 size-6"108 aria-label={`Dismiss ${providerName} provider ${status.status}`}
121 onClick={onDismiss}109 onClick={onDismiss}
122 size="icon-xs"110 size="icon-xs"
123 variant="ghost-muted"111 variant="ghost-muted"
124 >112 >
125 <XIcon aria-hidden className="size-3.5" />113 <XIcon />
126 </Button>114 </Button>
127 </div>115 </AlertAction>
116 </Alert>
128 </div>117 </div>
129 );118 );
130});119});
apps/web/src/components/chat/ThreadErrorBanner.tsx+1-6
...@@ -43,12 +43,7 @@ export const ThreadErrorBanner = memo(function ThreadErrorBanner({...@@ -43,12 +43,7 @@ export const ThreadErrorBanner = memo(function ThreadErrorBanner({
43 if (!error) return null;43 if (!error) return null;
44 return (44 return (
45 <div className="pointer-events-auto mx-auto w-fit max-w-[min(48rem,calc(100%-2rem))] pt-3">45 <div className="pointer-events-auto mx-auto w-fit max-w-[min(48rem,calc(100%-2rem))] pt-3">
46 <Alert46 <Alert variant="error" surface="glass" controlAlignment="first-line">
47 variant="error"
48 controlAlignment="first-line"
49 className="alert-glass"
50 data-variant="error"
51 >
52 <CircleAlertIcon />47 <CircleAlertIcon />
53 <AlertDescription>48 <AlertDescription>
54 <Tooltip>49 <Tooltip>
apps/web/src/components/desktop/SnapShotShortcutKeys.tsx+1-1
...@@ -16,7 +16,7 @@ export function SnapShotShortcutKeys({...@@ -16,7 +16,7 @@ export function SnapShotShortcutKeys({
16 const seen = seenLabels.get(label) ?? 0;16 const seen = seenLabels.get(label) ?? 0;
17 seenLabels.set(label, seen + 1);17 seenLabels.set(label, seen + 1);
18 return (18 return (
19 <Kbd aria-hidden className="min-w-6 justify-center px-1.5" key={`${label}-${seen}`}>19 <Kbd aria-hidden key={`${label}-${seen}`}>
20 {label}20 {label}
21 </Kbd>21 </Kbd>
22 );22 );
apps/web/src/components/settings/KeybindingsSettings.tsx+2-2
...@@ -79,9 +79,9 @@ function KeybindingPill({ value }: { value: string }) {...@@ -79,9 +79,9 @@ function KeybindingPill({ value }: { value: string }) {
79 return { part, key: seen === 0 ? part : `${part}-${seen}` };79 return { part, key: seen === 0 ? part : `${part}-${seen}` };
80 });80 });
81 return (81 return (
82 <KbdGroup className="bg-transparent p-0 shadow-none">82 <KbdGroup>
83 {parts.map(({ part, key }) => (83 {parts.map(({ part, key }) => (
84 <Kbd key={key} className="min-w-6 justify-center px-1.5">84 <Kbd key={key}>
85 {part === "mod"85 {part === "mod"
86 ? navigator.platform.toLowerCase().includes("mac")86 ? navigator.platform.toLowerCase().includes("mac")
87 ? "⌘"87 ? "⌘"
apps/web/src/components/settings/SettingsSidebarNav.tsx+1-1
...@@ -275,7 +275,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {...@@ -275,7 +275,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
275 <XIcon className="size-3" />275 <XIcon className="size-3" />
276 </Button>276 </Button>
277 ) : (277 ) : (
278 <Kbd className="h-4 min-w-0 rounded-sm px-1.5 text-[10px]">/</Kbd>278 <Kbd>/</Kbd>
279 )}279 )}
280 </div>280 </div>
281 {isSearching && results.length === 0 ? (281 {isSearching && results.length === 0 ? (
apps/web/src/components/sidebar/SidebarUpdatePill.tsx+1-1
...@@ -100,7 +100,7 @@ function SidebarUpdateArchitectureWarningContent() {...@@ -100,7 +100,7 @@ function SidebarUpdateArchitectureWarningContent() {
100 if (!visible || !description) return null;100 if (!visible || !description) return null;
101101
102 return (102 return (
103 <Alert variant="warning" className="rounded-2xl border-warning/40 bg-warning/8 text-xs">103 <Alert variant="warning">
104 <TriangleAlertIcon />104 <TriangleAlertIcon />
105 <AlertTitle>Intel build on Apple Silicon</AlertTitle>105 <AlertTitle>Intel build on Apple Silicon</AlertTitle>
106 <AlertDescription>{description}</AlertDescription>106 <AlertDescription>{description}</AlertDescription>
apps/web/src/components/ui/alert.tsx+9-1
...@@ -6,9 +6,15 @@ import { cn } from "~/lib/utils";...@@ -6,9 +6,15 @@ import { cn } from "~/lib/utils";
66
7const alertVariants = cva("relative rounded-xl border px-3.5 py-3 text-card-foreground text-sm", {7const alertVariants = cva("relative rounded-xl border px-3.5 py-3 text-card-foreground text-sm", {
8 defaultVariants: {8 defaultVariants: {
9 surface: "default",
9 variant: "default",10 variant: "default",
10 },11 },
11 variants: {12 variants: {
13 // "glass" floats the alert over content; alert-glass tints from data-variant.
14 surface: {
15 default: "",
16 glass: "alert-glass",
17 },
12 variant: {18 variant: {
13 default: "bg-transparent dark:bg-input/32 [&_svg]:text-muted-foreground",19 default: "bg-transparent dark:bg-input/32 [&_svg]:text-muted-foreground",
14 sidebar:20 sidebar:
...@@ -45,6 +51,7 @@ function alertChildSlot(child: React.ReactElement): string | undefined {...@@ -45,6 +51,7 @@ function alertChildSlot(child: React.ReactElement): string | undefined {
45function Alert({51function Alert({
46 className,52 className,
47 variant,53 variant,
54 surface,
48 controlAlignment = "center",55 controlAlignment = "center",
49 children,56 children,
50 ...props57 ...props
...@@ -73,8 +80,9 @@ function Alert({...@@ -73,8 +80,9 @@ function Alert({
7380
74 return (81 return (
75 <div82 <div
76 className={cn(alertVariants({ variant }), className)}83 className={cn(alertVariants({ surface, variant }), className)}
77 data-slot="alert"84 data-slot="alert"
85 data-variant={variant ?? "default"}
78 role="alert"86 role="alert"
79 {...props}87 {...props}
80 >88 >
scripts/lint-restyle-ceiling.ts+1-1
...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";
8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the
9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you
10// migrate a file, and delete this script when the rule becomes an error.10// migrate a file, and delete this script when the rule becomes an error.
11export const RESTYLE_CEILING = 668;11export const RESTYLE_CEILING = 628;
1212
13const RULE = "shadcn(no-restyle)";13const RULE = "shadcn(no-restyle)";
1414