Normalize desktop icon sizing (#999)

This commit is contained in:
klopez4212
2026-06-15 20:43:49 +01:00
committed by GitHub
parent 7a2e35521c
commit 19656fff46
84 changed files with 253 additions and 206 deletions

View File

@@ -2,8 +2,10 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
import { TopbarSearch } from "@/features/search/ui/TopbarSearch";
import type { Channel, SearchHit } from "@/shared/api/types";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
import { SidebarTrigger, useSidebar } from "@/shared/ui/sidebar";
import { Skeleton } from "@/shared/ui/skeleton";
type AppTopChromeProps = {
canGoBack: boolean;
@@ -16,6 +18,7 @@ type AppTopChromeProps = {
onOpenResult: (hit: SearchHit) => void;
searchHidden?: boolean;
searchFocusRequest: number;
searchLoading?: boolean;
};
function GlobalTopDivider() {
@@ -39,6 +42,7 @@ function CenterColumnTopbarSearch({
onOpenChannel,
onOpenResult,
searchFocusRequest,
searchLoading = false,
}: Pick<
AppTopChromeProps,
| "channels"
@@ -46,8 +50,11 @@ function CenterColumnTopbarSearch({
| "onOpenChannel"
| "onOpenResult"
| "searchFocusRequest"
| "searchLoading"
>) {
const { isResizing, state } = useSidebar();
const searchClassName =
"pointer-events-auto w-[220px] max-w-full md:w-[300px] lg:w-[360px] xl:w-[420px] 2xl:w-[480px]";
return (
<div
@@ -59,20 +66,30 @@ function CenterColumnTopbarSearch({
right: 0,
}}
>
<TopbarSearch
channels={channels}
className="pointer-events-auto w-[220px] max-w-full md:w-[300px] lg:w-[360px] xl:w-[420px] 2xl:w-[480px]"
currentPubkey={currentPubkey}
focusRequest={searchFocusRequest}
onOpenChannel={onOpenChannel}
onOpenResult={onOpenResult}
/>
{searchLoading ? (
<div
aria-hidden="true"
className={cn("h-7", searchClassName)}
data-testid="topbar-search-loading"
>
<Skeleton className="h-full w-full rounded-lg" />
</div>
) : (
<TopbarSearch
channels={channels}
className={searchClassName}
currentPubkey={currentPubkey}
focusRequest={searchFocusRequest}
onOpenChannel={onOpenChannel}
onOpenResult={onOpenResult}
/>
)}
</div>
);
}
const TOP_CHROME_ICON_BUTTON_CLASS =
"rounded-[4px] text-muted-foreground/70 hover:bg-border/45 hover:text-foreground";
"h-7 w-7 rounded-[4px] text-muted-foreground/70 hover:bg-border/45 hover:text-foreground [&_svg]:size-4";
export function AppTopChrome({
canGoBack,
@@ -85,6 +102,7 @@ export function AppTopChrome({
onOpenResult,
searchHidden = false,
searchFocusRequest,
searchLoading = false,
}: AppTopChromeProps) {
return (
<>
@@ -94,7 +112,7 @@ export function AppTopChrome({
data-tauri-drag-region
/>
<GlobalTopDivider />
<div className="fixed left-[80px] top-[9px] z-45 flex items-center gap-0.5">
<div className="fixed left-[80px] top-[6px] z-45 flex items-center gap-0.5">
<SidebarTrigger className={TOP_CHROME_ICON_BUTTON_CLASS} />
<Button
aria-label="Go back"
@@ -126,6 +144,7 @@ export function AppTopChrome({
onOpenChannel={onOpenChannel}
onOpenResult={onOpenResult}
searchFocusRequest={searchFocusRequest}
searchLoading={searchLoading}
/>
)}
</>

View File

@@ -85,7 +85,7 @@ export function MemoryRefreshButton({
>
<RefreshCw
className={cn(
iconClassName ?? "h-3.5 w-3.5",
iconClassName ?? "h-4 w-4",
query.isFetching && "animate-spin",
)}
/>
@@ -169,7 +169,7 @@ function MemoryErrorState({
role="alert"
>
<div className="flex items-start gap-2">
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0 text-destructive" />
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-destructive" />
<div className="space-y-1">
<div className="font-medium text-destructive">
Couldn't load memory
@@ -196,7 +196,7 @@ function MemoryStaleErrorBanner({ onRetry }: { onRetry: () => void }) {
className="mb-2 flex items-center gap-2 rounded-md border border-warning/30 bg-warning/5 px-2 py-1.5 text-xs"
data-testid="agent-memory-stale-error"
>
<AlertTriangle className="h-3 w-3 shrink-0 text-warning" />
<AlertTriangle className="h-4 w-4 shrink-0 text-warning" />
<span className="flex-1 text-muted-foreground">Refresh failed.</span>
<button
className="font-medium text-warning hover:underline"
@@ -511,7 +511,7 @@ function MemoryEntryAccordion({
ref={titleRef}
>
{hasDanglingRefs ? (
<AlertTriangle className="mr-1 inline-block h-3.5 w-3.5 align-[-2px] text-warning" />
<AlertTriangle className="mr-1 inline-block h-4 w-4 align-[-2px] text-warning" />
) : null}
<MemorySlugTitle slug={entry.slug} />
</div>

View File

@@ -81,7 +81,7 @@ export function ToolItem({
</span>
) : null}
<ToolTimestamp item={item} />
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
</summary>
<ToolDetailBlocks
@@ -272,7 +272,7 @@ function BuzzToolInlineAction({
{action.avatar}
<span className="shrink-0">{action.label}</span>
<span className="truncate">{action.value}</span>
<ArrowUpRight className="h-3 w-3 shrink-0" />
<ArrowUpRight className="h-4 w-4 shrink-0" />
</button>
);
}

View File

@@ -27,7 +27,7 @@ export function AgentSessionTranscriptList({
if (items.length === 0) {
return (
<div className="flex min-h-56 flex-col items-center justify-center px-6 py-10 text-center">
<Radio className="mx-auto h-5 w-5 text-muted-foreground" />
<Radio className="mx-auto h-4 w-4 text-muted-foreground" />
<p className="mt-3 text-sm font-medium">No ACP activity yet</p>
<p className="mt-1 text-sm text-muted-foreground">{emptyDescription}</p>
</div>
@@ -126,7 +126,7 @@ function MessageItem({
{isAssistant ? (
<div className="mb-0.5 flex items-center gap-1 text-xs">
<span className="flex h-5 w-5 items-center justify-center">
<Bot className="h-3.5 w-3.5 text-muted-foreground" />
<Bot className="h-4 w-4 text-muted-foreground" />
</span>
<span className="font-normal text-foreground">{agentName}</span>
<TranscriptTimestamp timestamp={item.timestamp} />
@@ -163,7 +163,7 @@ function ThoughtItem({
<Brain className="h-4 w-4" />
<span className="truncate text-sm font-medium">{item.title}</span>
<TranscriptTimestamp timestamp={item.timestamp} />
<ChevronDown className="h-3.5 w-3.5 shrink-0 transition-transform group-open:rotate-180" />
<ChevronDown className="h-4 w-4 shrink-0 transition-transform group-open:rotate-180" />
</summary>
<div className="py-2 pl-5 text-sm leading-6 text-muted-foreground">
<Markdown compact content={item.text.trim() || " "} />
@@ -186,7 +186,7 @@ function MetadataItem({
{item.sections.length} section{item.sections.length === 1 ? "" : "s"}
</span>
<TranscriptTimestamp timestamp={item.timestamp} />
<ChevronDown className="h-3.5 w-3.5 shrink-0 transition-transform group-open:rotate-180" />
<ChevronDown className="h-4 w-4 shrink-0 transition-transform group-open:rotate-180" />
</summary>
<div className="space-y-3 py-2 pl-5">
{item.sections.map((section) => (
@@ -196,7 +196,7 @@ function MetadataItem({
>
<summary className="inline-flex max-w-full cursor-pointer list-none items-center gap-1.5 text-xs font-medium text-foreground/80">
<span className="truncate">{section.title}</span>
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform group-open/section:rotate-180" />
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform group-open/section:rotate-180" />
</summary>
<pre className="mt-2 max-h-56 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/50 px-3 py-2 font-mono text-[11px] leading-5 text-muted-foreground">
{section.body.trim() || "No metadata."}

View File

@@ -213,12 +213,12 @@ export function BatchImportDialog({
onClick={() => setSkippedExpanded((prev) => !prev)}
type="button"
>
<AlertTriangle className="h-3.5 w-3.5" />
<AlertTriangle className="h-4 w-4" />
{skipped.length} file{skipped.length !== 1 ? "s" : ""} skipped
{skippedExpanded ? (
<ChevronDown className="h-3.5 w-3.5" />
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-3.5 w-3.5" />
<ChevronRight className="h-4 w-4" />
)}
</button>
{skippedExpanded ? (

View File

@@ -20,7 +20,7 @@ export function CopyButton({
type="button"
variant="outline"
>
<Copy className="h-3.5 w-3.5" />
<Copy className="h-4 w-4" />
<span>{label ?? "Copy"}</span>
</Button>
);

View File

@@ -3,7 +3,6 @@ import {
CircleAlert,
CircleDot,
Clock3,
Loader2,
TerminalSquare,
XCircle,
} from "lucide-react";
@@ -14,6 +13,7 @@ import type { ManagedAgent } from "@/shared/api/types";
import { cn } from "@/shared/lib/cn";
import { Badge } from "@/shared/ui/badge";
import { Skeleton } from "@/shared/ui/skeleton";
import { Spinner } from "@/shared/ui/spinner";
import { AgentSessionTranscriptList } from "./AgentSessionTranscriptList";
import { RawEventRail } from "./RawEventRail";
import type {
@@ -229,7 +229,7 @@ function ObserverStatusBadge({ state }: { state: ConnectionState }) {
state === "open"
? { label: "Live", Icon: CircleDot, variant: "default" as const }
: state === "connecting"
? { label: "Connecting", Icon: Loader2, variant: "secondary" as const }
? { label: "Connecting", variant: "secondary" as const }
: state === "error"
? {
label: "Unavailable",
@@ -239,12 +239,15 @@ function ObserverStatusBadge({ state }: { state: ConnectionState }) {
: state === "closed"
? { label: "Closed", Icon: Clock3, variant: "secondary" as const }
: { label: "Idle", Icon: Clock3, variant: "secondary" as const };
const StatusIcon = display.Icon;
return (
<Badge className="gap-1.5" variant={display.variant}>
<display.Icon
className={cn("h-3.5 w-3.5", state === "connecting" && "animate-spin")}
/>
{StatusIcon ? (
<StatusIcon aria-hidden className="h-4 w-4" />
) : (
<Spinner aria-hidden className="h-4 w-4 border-2" />
)}
{display.label}
</Badge>
);
@@ -253,7 +256,7 @@ function ObserverStatusBadge({ state }: { state: ConnectionState }) {
function EmptyObserverState() {
return (
<div className="mt-4 flex min-h-48 flex-col items-center justify-center px-6 py-8 text-center">
<TerminalSquare className="mx-auto h-5 w-5 text-muted-foreground" />
<TerminalSquare className="mx-auto h-4 w-4 text-muted-foreground" />
<p className="mt-3 text-sm font-medium">Observer not attached</p>
<p className="mt-1 text-sm text-muted-foreground">
The live feed is available for local agents started after this update.

View File

@@ -93,7 +93,7 @@ export function ModelPicker({
variant="ghost"
>
<span className="truncate">{displayLabel}</span>
<ChevronDown className="h-3 w-3 text-muted-foreground" />
<ChevronDown className="h-4 w-4 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
@@ -103,7 +103,7 @@ export function ModelPicker({
>
{loading ? (
<div className="flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground">
<Spinner className="h-3.5 w-3.5" />
<Spinner className="h-4 w-4 border-2" />
Loading models...
</div>
) : error ? (

View File

@@ -20,7 +20,7 @@ export function PersonaCatalogSelectionBadge({
: "border border-border/70 bg-background/85 text-muted-foreground",
)}
>
{isActive ? <Check className="h-3 w-3" /> : null}
{isActive ? <Check className="h-4 w-4" /> : null}
{isActive
? personaCatalogCopy.selectedState
: personaCatalogCopy.availableState}

View File

@@ -497,12 +497,12 @@ export function PersonaDialog({
: undefined
}
>
<Upload className="h-3.5 w-3.5" />
<Upload className="h-4 w-4" />
<span className="max-w-[16rem] truncate">
{importButtonLabel}
</span>
{isImportingUpdate ? (
<RefreshCw className="h-3.5 w-3.5 animate-spin" />
<RefreshCw className="h-4 w-4 animate-spin" />
) : null}
</button>
</>

View File

@@ -46,7 +46,7 @@ export function PersonaIdentity({
className="flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground transition-colors hover:text-foreground"
type="button"
>
<Info className="h-3.5 w-3.5" />
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">

View File

@@ -258,7 +258,7 @@ function AllowlistPicker({
onClick={() => onRemove(pubkey)}
type="button"
>
<X className="h-3 w-3" />
<X className="h-4 w-4" />
</button>
</div>
))}

View File

@@ -475,12 +475,12 @@ export function TeamDialog({
: undefined
}
>
<Upload className="h-3.5 w-3.5" />
<Upload className="h-4 w-4" />
<span className="max-w-[16rem] truncate">
{importButtonLabel}
</span>
{isImportingUpdate ? (
<RefreshCw className="h-3.5 w-3.5 animate-spin" />
<RefreshCw className="h-4 w-4 animate-spin" />
) : null}
</button>
</>

View File

@@ -130,7 +130,7 @@ export function TeamImportDialog({
{preview ? (
<div className="space-y-4">
<div className="flex items-center gap-3 rounded-lg border border-border/60 bg-card/80 px-4 py-3">
<Users className="h-5 w-5 shrink-0 text-muted-foreground" />
<Users className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="min-w-0 flex-1">
<p className="text-sm font-semibold tracking-tight">
{preview.name}

View File

@@ -108,7 +108,7 @@ export function TeamsSection({
onClick={onInstallFromDirectory}
type="button"
>
<FolderOpen className="h-3.5 w-3.5" />
<FolderOpen className="h-4 w-4" />
Install from directory
</button>
<CreateNewButton
@@ -161,7 +161,7 @@ export function TeamsSection({
<Tooltip>
<TooltipTrigger asChild>
<span className="flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground">
<Link className="h-3.5 w-3.5" />
<Link className="h-4 w-4" />
</span>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">
@@ -184,7 +184,7 @@ export function TeamsSection({
className="flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground transition-colors hover:text-foreground"
type="button"
>
<Info className="h-3.5 w-3.5" />
<Info className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">

View File

@@ -530,7 +530,7 @@ export function AddChannelBotDialog({
variant="ghost"
>
<span className="truncate">{runtimeTriggerLabel}</span>
<ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
<ChevronDown className="h-4 w-4 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent

View File

@@ -54,7 +54,7 @@ function SelectionChipButton({
? "bg-primary/20 text-primary ring-1 ring-primary/20"
: "bg-background/80 text-muted-foreground ring-1 ring-border/70",
)}
iconClassName="h-3.5 w-3.5"
iconClassName="h-4 w-4"
label={label}
/>
) : null}
@@ -193,7 +193,7 @@ export function AddChannelBotPersonasSection({
<ProfileAvatar
avatarUrl={persona.avatarUrl}
className="h-7 w-7 text-[10px] bg-primary-foreground/20 text-primary-foreground"
iconClassName="h-3.5 w-3.5"
iconClassName="h-4 w-4"
label={persona.displayName}
/>
<p className="font-medium">{persona.displayName}</p>

View File

@@ -102,7 +102,7 @@ export function AgentSessionThreadPanel({
type="button"
variant="outline"
>
<Octagon className="h-3 w-3" />
<Octagon className="h-4 w-4" />
Stop
</Button>
</TooltipTrigger>

View File

@@ -201,7 +201,7 @@ export function BotActivityComposerAction({
{isInline ? <Shimmer>{visibleStatusLabel}</Shimmer> : "working"}
</span>
{isInline ? null : (
<Loader2 className="h-3.5 w-3.5 shrink-0 animate-spin opacity-70" />
<Loader2 className="h-4 w-4 shrink-0 animate-spin opacity-70" />
)}
</button>
</PopoverTrigger>
@@ -244,7 +244,7 @@ export function BotActivityComposerAction({
displayName={agent.name}
/>
<span className="min-w-0 flex-1 truncate">{agent.name}</span>
<Loader2 className="h-3.5 w-3.5 shrink-0 animate-spin text-muted-foreground/70" />
<Loader2 className="h-4 w-4 shrink-0 animate-spin text-muted-foreground/70" />
</button>
);
})}

View File

@@ -79,7 +79,7 @@ function MetadataPill({
}) {
return (
<div className="inline-flex items-center gap-2 rounded-full border border-border/80 bg-muted/40 px-3 py-1 text-xs font-medium text-muted-foreground">
<Icon className="h-3.5 w-3.5" />
<Icon className="h-4 w-4" />
<span>{label}</span>
</div>
);

View File

@@ -234,7 +234,7 @@ export function ChannelMemberInviteCard({
}}
type="button"
>
<X className="h-3 w-3" />
<X className="h-4 w-4" />
</button>
</div>
))}

View File

@@ -60,7 +60,7 @@ export function ChannelScreenHeader({
size="sm"
variant="default"
>
<LogIn className="mr-1.5 h-3.5 w-3.5" />
<LogIn className="mr-1.5 h-4 w-4" />
{isJoining ? "Joining…" : "Join"}
</Button>
) : (

View File

@@ -81,7 +81,7 @@ export function QuickBotBar({ personas, pending, onAdd }: QuickBotBarProps) {
)}
{isThisPending ? (
<div className="absolute inset-0 flex items-center justify-center rounded-lg bg-background/70">
<Spinner className="h-3.5 w-3.5 text-primary" />
<Spinner className="h-4 w-4 border-2 text-primary" />
</div>
) : null}
</button>

View File

@@ -32,8 +32,8 @@ type ChatHeaderProps = {
statusBadge?: React.ReactNode;
};
const HEADER_ICON_CLASS = "h-[14px] w-[14px] text-muted-foreground";
const CHANNEL_HASH_ICON_CLASS = "h-[14px] w-[14px] translate-y-px";
const HEADER_ICON_CLASS = "h-4 w-4 text-muted-foreground";
const CHANNEL_HASH_ICON_CLASS = "h-4 w-4 translate-y-px";
function ChannelIcon({
channelType,
@@ -98,12 +98,12 @@ export function ChatHeader({
const header = (
<header
className={cn(
"pointer-events-auto relative z-30 flex min-w-0 shrink-0 cursor-default select-none items-center gap-2.5 bg-transparent pl-4 pr-2 transition-[margin,padding] duration-200 ease-linear sm:pr-3",
"pointer-events-auto relative z-30 flex min-w-0 shrink-0 cursor-default select-none items-center gap-2.5 bg-transparent px-4 transition-[margin,padding] duration-200 ease-linear sm:px-6",
density === "compact"
? belowSystemChrome
? "min-h-8 py-1.5"
: "min-h-8 py-0"
: "min-h-11 py-1.5 sm:pl-6",
: "min-h-11 py-1.5",
overlaysContent && !belowSystemChrome && "-mb-11",
)}
data-testid="chat-header"

View File

@@ -16,13 +16,9 @@ type DeleteActionMenuProps = {
iconSize?: "sm" | "md";
};
export function DeleteActionMenu({
label,
onConfirm,
iconSize = "md",
}: DeleteActionMenuProps) {
export function DeleteActionMenu({ label, onConfirm }: DeleteActionMenuProps) {
const [isOpen, setIsOpen] = React.useState(false);
const iconClass = iconSize === "sm" ? "h-3.5 w-3.5" : "h-4 w-4";
const iconClass = "h-4 w-4";
return (
<div className="ml-auto opacity-0 transition-opacity group-hover:opacity-100">

View File

@@ -5,6 +5,7 @@ import { Plus } from "lucide-react";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
import { Spinner } from "@/shared/ui/spinner";
type ForumComposerCompactLayoutProps = {
editor: Editor | null;
@@ -45,12 +46,12 @@ export function ForumComposerCompactLayout({
variant="ghost"
>
{isSending ? (
<span
<Spinner
aria-hidden
className="h-3.5 w-3.5 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent"
className="h-4 w-4 border-2 text-primary-foreground"
/>
) : (
<Plus aria-hidden className="h-3.5 w-3.5" />
<Plus aria-hidden className="h-4 w-4" />
)}
</Button>
</div>

View File

@@ -125,7 +125,7 @@ export function ForumPostCard({
{summary && summary.replyCount > 0 ? (
<div className="mt-3 flex items-center gap-1.5 text-xs text-muted-foreground">
<MessageSquare className="h-3.5 w-3.5" />
<MessageSquare className="h-4 w-4" />
<span>
{summary.replyCount}{" "}
{summary.replyCount === 1 ? "reply" : "replies"}

View File

@@ -264,10 +264,7 @@ export function InboxDetailPane({
type="button"
>
{hasChannelContext ? (
<Hash
className="h-[14px] w-[14px] shrink-0"
color="gray"
/>
<Hash className="h-4 w-4 shrink-0" color="gray" />
) : null}
<span className="min-w-0 translate-y-px truncate">
{contextLabel}
@@ -279,10 +276,7 @@ export function InboxDetailPane({
title={item.fullTimestampLabel}
>
{hasChannelContext ? (
<Hash
className="h-[14px] w-[14px] shrink-0"
color="gray"
/>
<Hash className="h-4 w-4 shrink-0" color="gray" />
) : null}
<span className="min-w-0 translate-y-px truncate">
{contextLabel}

View File

@@ -59,7 +59,7 @@ export function InboxListPane({
<div className="px-5 py-1">
<div className="flex min-w-0 items-center justify-between gap-3">
<div className="flex min-w-0 items-center gap-[6px]">
<Inbox className="h-[14px] w-[14px] shrink-0 text-muted-foreground" />
<Inbox className="h-4 w-4 shrink-0 text-muted-foreground" />
<h2 className="translate-y-px truncate text-sm font-semibold leading-5 tracking-tight">
Inbox
</h2>
@@ -73,7 +73,7 @@ export function InboxListPane({
variant="outline"
>
<span>{activeFilter?.label ?? "All"}</span>
<ChevronDown className="h-3 w-3" />
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="min-w-40">

View File

@@ -67,7 +67,7 @@ export function RecentNotesSection({
size="sm"
/>
{isAgent ? (
<Bot className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full bg-background p-0.5 text-muted-foreground" />
<Bot className="absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full bg-background p-0.5 text-muted-foreground" />
) : null}
</div>
<div className="min-w-0 flex-1">

View File

@@ -24,7 +24,7 @@ export function HeadphonesNotice({ onDismiss }: { onDismiss: () => void }) {
data-testid="huddle-headphones-notice"
className="flex items-center gap-1.5 rounded bg-amber-500/10 px-2 py-1 text-xs text-amber-700 dark:text-amber-300"
>
<Headphones className="h-3 w-3" />
<Headphones className="h-4 w-4" />
<span className="max-w-[260px] truncate">
Headphones recommended — echo cancellation lands in the next release.
</span>

View File

@@ -304,7 +304,7 @@ export function HuddleBar({ className }: HuddleBarProps) {
size="icon"
variant="ghost"
>
<Plus className="h-3.5 w-3.5" />
<Plus className="h-4 w-4" />
</Button>
</div>

View File

@@ -67,7 +67,7 @@ export function MicControls({
size="icon"
variant="secondary"
>
<ChevronUp className="h-3 w-3" />
<ChevronUp className="h-4 w-4" />
</Button>
</PopoverTrigger>
</div>
@@ -151,7 +151,7 @@ export function SpeakerControls({
size="icon"
variant="secondary"
>
<ChevronUp className="h-3 w-3" />
<ChevronUp className="h-4 w-4" />
</Button>
</PopoverTrigger>
</div>
@@ -192,7 +192,7 @@ export function DeviceList({
type="button"
>
<Check
className={cn("h-3 w-3 shrink-0", selectedId && "invisible")}
className={cn("h-4 w-4 shrink-0", selectedId && "invisible")}
/>
System default
</button>
@@ -207,7 +207,7 @@ export function DeviceList({
type="button"
>
<Check
className={cn("h-3 w-3 shrink-0", !isSelected && "invisible")}
className={cn("h-4 w-4 shrink-0", !isSelected && "invisible")}
/>
<span className="truncate">{d.label}</span>
</button>

View File

@@ -244,7 +244,7 @@ export function MeshComputeSettingsCard() {
<summary className="flex cursor-pointer items-center gap-1.5 text-sm font-medium text-foreground">
<ChevronDown
className={cn(
"h-3.5 w-3.5 text-muted-foreground transition-transform",
"h-4 w-4 text-muted-foreground transition-transform",
advancedOpen ? "rotate-0" : "-rotate-90",
)}
/>

View File

@@ -166,7 +166,7 @@ export function RelayMeshAgentSection({
) : null}
{overrides.length > 0 ? (
<p className="flex items-start gap-1.5 rounded bg-warning-bg/60 px-2 py-1.5 text-xs text-warning">
<AlertCircle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
<span>
Using Relay mesh overrides this agent's {overrides.join(", ")}.
</span>

View File

@@ -135,7 +135,7 @@ export const ComposerAttachments = React.memo(function ComposerAttachments({
className="group relative"
>
<div className="flex h-5 max-w-[10rem] items-center gap-1 rounded border border-border/70 bg-muted px-1.5">
<FileText className="h-3 w-3 shrink-0 text-muted-foreground" />
<FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="truncate text-[10px] text-muted-foreground">
{label}
</span>
@@ -186,7 +186,7 @@ export const ComposerAttachments = React.memo(function ComposerAttachments({
)}
<div className="absolute inset-0 bg-black/15" />
<div className="absolute flex h-5 w-5 items-center justify-center rounded-full bg-black/55 backdrop-blur-sm">
<Play className="h-3 w-3 fill-white text-white" />
<Play className="h-4 w-4 fill-white text-white" />
</div>
</div>
) : (
@@ -231,7 +231,7 @@ export const ComposerAttachments = React.memo(function ComposerAttachments({
/>
)}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white/80 transition-colors hover:bg-black/70 hover:text-white focus:outline-hidden focus:ring-2 focus:ring-white/30">
<X className="h-5 w-5" />
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>

View File

@@ -86,7 +86,7 @@ export default function DiffMessage({
type="button"
variant="ghost"
>
<Maximize2 className="h-3.5 w-3.5" />
<Maximize2 className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Expand diff</TooltipContent>

View File

@@ -46,7 +46,7 @@ export default function DiffMessageExpanded({
type="button"
variant={viewType === "unified" ? "secondary" : "ghost"}
>
<Rows3 className="h-3.5 w-3.5" />
<Rows3 className="h-4 w-4" />
Unified
</Button>
<Button
@@ -58,7 +58,7 @@ export default function DiffMessageExpanded({
type="button"
variant={viewType === "split" ? "secondary" : "ghost"}
>
<SplitSquareVertical className="h-3.5 w-3.5" />
<SplitSquareVertical className="h-4 w-4" />
Split
</Button>
</div>

View File

@@ -212,13 +212,13 @@ export const FormattingToolbar = React.memo(function FormattingToolbar({
"hover:bg-muted hover:text-foreground",
"focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring",
"disabled:pointer-events-none disabled:opacity-50",
"[&_svg]:pointer-events-none [&_svg]:size-3.5 [&_svg]:shrink-0",
"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
item.active
? "bg-primary text-primary-foreground"
: "bg-transparent text-muted-foreground",
)}
>
<item.icon className="h-3.5 w-3.5" />
<item.icon className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent>

View File

@@ -98,7 +98,7 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({
>
<Bot
aria-hidden="true"
className="h-3 w-3"
className="h-4 w-4"
data-testid="mention-agent-icon"
/>
{agentLabel}

View File

@@ -117,7 +117,7 @@ export function TypingIndicatorRow({
: "h-5 w-5 text-[8px]",
)}
iconClassName={
isActivityVariant ? "h-2.5 w-2.5" : "h-3 w-3"
isActivityVariant ? "h-2.5 w-2.5" : "h-4 w-4"
}
/>
</div>

View File

@@ -167,7 +167,7 @@ function AvatarStepActions({
{isSaving || isUploadingAvatar ? (
<Spinner
aria-label={isSaving ? "Saving profile" : "Uploading avatar"}
className="h-4 w-4"
className="h-4 w-4 border-2"
/>
) : (
"Next"

View File

@@ -89,7 +89,7 @@ export function MembershipDenied({
<Badge variant="warning">Membership required</Badge>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-destructive/10">
<ShieldX className="h-5 w-5 text-destructive" />
<ShieldX className="h-4 w-4 text-destructive" />
</div>
<h1 className="text-2xl font-semibold tracking-tight text-foreground">
Not a member yet
@@ -172,7 +172,7 @@ export function MembershipDenied({
className="flex items-start gap-2 rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-xs"
data-testid="membership-denied-npub-preview"
>
<Check className="mt-0.5 h-3.5 w-3.5 shrink-0 text-primary" />
<Check className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
<div className="min-w-0 space-y-0.5">
<p className="font-medium text-foreground">
This will use this Nostr identity:
@@ -197,7 +197,10 @@ export function MembershipDenied({
type="submit"
>
{isImportingKey ? (
<Spinner aria-label="Importing key" className="h-4 w-4" />
<Spinner
aria-label="Importing key"
className="h-4 w-4 border-2"
/>
) : (
"Import key"
)}
@@ -236,7 +239,7 @@ export function MembershipDenied({
}}
type="button"
>
<KeyRound className="h-3 w-3" />
<KeyRound className="h-4 w-4" />
Use a different key
</button>
) : null}

View File

@@ -222,7 +222,7 @@ export function NostrKeyImportForm({
className="flex items-start gap-2 rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-xs"
data-testid="nostr-import-npub-preview"
>
<Check className="mt-0.5 h-3.5 w-3.5 shrink-0 text-primary" />
<Check className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
<div className="min-w-0 space-y-0.5">
<p className="font-medium text-foreground">
This will use this Nostr identity:

View File

@@ -92,6 +92,7 @@ export function ProfileStep({
) : null}
<input
aria-label="Name"
autoCapitalize="none"
autoComplete="off"
autoCorrect="off"
className={cn(
@@ -128,7 +129,7 @@ export function ProfileStep({
type="button"
>
{isSaving ? (
<Spinner aria-label="Saving profile" className="h-4 w-4" />
<Spinner aria-label="Saving profile" className="h-4 w-4 border-2" />
) : (
"Next"
)}

View File

@@ -106,7 +106,7 @@ function RuntimeStatus({
className="flex h-8 shrink-0 items-center justify-center"
role="status"
>
<Spinner className="h-4 w-4 text-foreground" />
<Spinner className="h-4 w-4 border-2 text-foreground" />
</div>
);
}

View File

@@ -1,9 +1,10 @@
import * as React from "react";
import { Camera, Link2, Loader2, Upload, X } from "lucide-react";
import { Camera, Link2, Upload, X } from "lucide-react";
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
import { useAvatarUpload } from "@/features/profile/useAvatarUpload";
import { Input } from "@/shared/ui/input";
import { Spinner } from "@/shared/ui/spinner";
type AvatarUploadProps = {
avatarUrl: string;
@@ -90,7 +91,7 @@ export function AvatarUpload({
title="Remove photo"
type="button"
>
<X className="h-3 w-3" />
<X className="h-4 w-4" />
</button>
) : (
<div className="absolute -bottom-1 -right-1 flex h-8 w-8 items-center justify-center rounded-full border border-background bg-primary text-primary-foreground shadow-xs">
@@ -127,9 +128,12 @@ export function AvatarUpload({
type="button"
>
{isUploading ? (
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
<Spinner
aria-hidden
className="h-4 w-4 border-2 text-muted-foreground"
/>
) : (
<Upload className="h-5 w-5 text-muted-foreground" />
<Upload className="h-4 w-4 text-muted-foreground" />
)}
<span className="text-xs text-muted-foreground">
{isUploading ? (

View File

@@ -1,6 +1,6 @@
import emojiData from "@emoji-mart/data";
import Picker from "@emoji-mart/react";
import { Link2, Loader2, UploadCloud } from "lucide-react";
import { Link2, UploadCloud } from "lucide-react";
import { motion } from "motion/react";
import * as React from "react";
@@ -579,7 +579,10 @@ export function ProfileAvatarEditor({
data-testid={`${testIdPrefix}-drop-mask`}
/>
{isUploading ? (
<Loader2 className="relative h-8 w-8 animate-spin text-muted-foreground" />
<Spinner
aria-hidden
className="relative h-8 w-8 text-muted-foreground"
/>
) : (
<UploadCloud
className={cn(
@@ -612,6 +615,8 @@ export function ProfileAvatarEditor({
<div className="flex h-16 items-center gap-3 rounded-xl bg-muted px-5 transition-colors duration-[250ms] ease-out focus-within:bg-muted/80">
<Link2 className="h-4 w-4 text-muted-foreground" />
<input
autoCapitalize="none"
autoCorrect="off"
className="min-w-0 flex-1 bg-transparent text-sm font-medium text-foreground outline-none placeholder:text-muted-foreground"
data-testid={`${testIdPrefix}-url`}
disabled={isInputDisabled}
@@ -636,6 +641,7 @@ export function ProfileAvatarEditor({
}
}}
placeholder="Paste a URL (Slack profile, etc.)"
spellCheck={false}
type="url"
value={urlDraft}
/>
@@ -960,7 +966,10 @@ export function ProfileAvatarEditor({
type="button"
>
{donePending ? (
<Spinner aria-label="Saving avatar" className="h-4 w-4" />
<Spinner
aria-label="Saving avatar"
className="h-4 w-4 border-2"
/>
) : (
"Done"
)}

View File

@@ -393,7 +393,7 @@ function ProfileHeroDescription({ about }: { about: string }) {
type="button"
>
more
<ChevronDown className="h-3 w-3" />
<ChevronDown className="h-4 w-4" />
</button>
) : null}
{expanded ? (
@@ -404,7 +404,7 @@ function ProfileHeroDescription({ about }: { about: string }) {
type="button"
>
less
<ChevronUp className="h-3 w-3" />
<ChevronUp className="h-4 w-4" />
</button>
) : null}
</div>

View File

@@ -284,7 +284,7 @@ export function UserProfilePopover({
}}
type="button"
>
<Activity className="h-3.5 w-3.5 text-muted-foreground" />
<Activity className="h-4 w-4 text-muted-foreground" />
View activity log
</button>
) : null}

View File

@@ -30,7 +30,7 @@ function CloneUrlRow({ url }: { url: string }) {
return (
<div className="flex items-center gap-2 rounded-lg border border-border/60 bg-muted/30 px-3 py-2">
<GitFork className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<GitFork className="h-4 w-4 shrink-0 text-muted-foreground" />
<code className="min-w-0 flex-1 truncate text-xs">{url}</code>
<Button
className="h-6 w-6 shrink-0"
@@ -39,9 +39,9 @@ function CloneUrlRow({ url }: { url: string }) {
variant="ghost"
>
{copied ? (
<Check className="h-3 w-3 text-green-500" />
<Check className="h-4 w-4 text-green-500" />
) : (
<Copy className="h-3 w-3" />
<Copy className="h-4 w-4" />
)}
</Button>
</div>
@@ -89,7 +89,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
size="sm"
variant="ghost"
>
<ArrowLeft className="mr-1.5 h-3.5 w-3.5" />
<ArrowLeft className="mr-1.5 h-4 w-4" />
Back to Projects
</Button>
</div>
@@ -111,7 +111,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
size="sm"
variant="outline"
>
<ArrowLeft className="mr-1.5 h-3.5 w-3.5" />
<ArrowLeft className="mr-1.5 h-4 w-4" />
Back to Projects
</Button>
</div>
@@ -136,7 +136,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
size="sm"
variant="ghost"
>
<ArrowLeft className="h-3.5 w-3.5" />
<ArrowLeft className="h-4 w-4" />
Back to Projects
</Button>
</div>
@@ -144,7 +144,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
<div className="mx-auto w-full max-w-2xl space-y-6">
<section className="space-y-2">
<div className="flex items-center gap-2">
<FolderGit2 className="h-5 w-5 text-muted-foreground" />
<FolderGit2 className="h-4 w-4 text-muted-foreground" />
<h2 className="text-lg font-semibold">{project.name}</h2>
</div>
{project.description ? (
@@ -178,7 +178,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
rel="noopener noreferrer"
target="_blank"
>
<ExternalLink className="h-3.5 w-3.5" />
<ExternalLink className="h-4 w-4" />
{project.webUrl}
</a>
</section>
@@ -188,7 +188,7 @@ export function ProjectDetailScreen({ projectId }: ProjectDetailScreenProps) {
<section className="space-y-2">
<h3 className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
<span className="flex items-center gap-1.5">
<Users className="h-3.5 w-3.5" />
<Users className="h-4 w-4" />
Contributors ({project.contributors.length})
</span>
</h3>

View File

@@ -86,19 +86,19 @@ export function ProjectsView() {
<div className="flex flex-wrap items-center gap-3 text-xs text-muted-foreground/70">
{project.cloneUrls.length > 0 ? (
<span className="flex items-center gap-1">
<GitFork className="h-3 w-3" />
<GitFork className="h-4 w-4" />
{project.cloneUrls[0]}
</span>
) : null}
{project.contributors.length > 0 ? (
<span className="flex items-center gap-1">
<Users className="h-3 w-3" />
<Users className="h-4 w-4" />
{project.contributors.length}
</span>
) : null}
{project.webUrl ? (
<span className="flex items-center gap-1">
<ExternalLink className="h-3 w-3" />
<ExternalLink className="h-4 w-4" />
Web
</span>
) : null}

View File

@@ -66,7 +66,7 @@ export function AgentActivityCard({
type="button"
>
<UserAvatar avatarUrl={avatarUrl} displayName={displayName} />
<Bot className="absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-background p-0.5 text-muted-foreground" />
<Bot className="absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full bg-background p-0.5 text-muted-foreground" />
</button>
</UserProfilePopover>
<div className="min-w-0 flex-1">
@@ -87,9 +87,9 @@ export function AgentActivityCard({
type="button"
>
{expanded ? (
<ChevronDown className="h-3.5 w-3.5" />
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-3.5 w-3.5" />
<ChevronRight className="h-4 w-4" />
)}
{group.notes.length} updates
</button>

View File

@@ -171,7 +171,7 @@ export function NoteCard({
displayName={displayName}
/>
{isAgent ? (
<Bot className="absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-background p-0.5 text-muted-foreground" />
<Bot className="absolute -bottom-0.5 -right-0.5 h-4 w-4 rounded-full bg-background p-0.5 text-muted-foreground" />
) : null}
</button>
</UserProfilePopover>

View File

@@ -349,7 +349,7 @@ export function PulseView({ currentPubkey }: PulseViewProps) {
className="absolute right-1.5 top-1/2 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full bg-foreground/10 text-foreground transition-colors hover:bg-foreground/15 dark:bg-white/85 dark:text-black dark:hover:bg-white"
type="button"
>
<Search className="h-3.5 w-3.5" />
<Search className="h-4 w-4" />
</button>
</div>
</div>

View File

@@ -121,7 +121,7 @@ function MemberRow({
size="sm"
variant="ghost"
>
<MoreHorizontal className="h-3.5 w-3.5" />
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Actions</span>
</Button>
</DropdownMenuTrigger>
@@ -231,7 +231,7 @@ export function RelayMembersCard({
onClick={() => setAddDialogOpen(true)}
size="sm"
>
<Plus className="h-3.5 w-3.5" />
<Plus className="h-4 w-4" />
Add Member
</Button>
) : null}

View File

@@ -165,10 +165,10 @@ function RelayMemberRow({
<div className="min-w-0 flex-1 space-y-0.5">
<div className="flex flex-wrap items-center gap-1.5">
{member.role === "owner" ? (
<Crown className="h-3.5 w-3.5 text-amber-500" />
<Crown className="h-4 w-4 text-amber-500" />
) : null}
{member.role === "admin" ? (
<Shield className="h-3.5 w-3.5 text-blue-500" />
<Shield className="h-4 w-4 text-blue-500" />
) : null}
<span className="truncate text-sm font-medium">{displayName}</span>
{isSelf ? (
@@ -461,10 +461,13 @@ export function RelayMembersSettingsCard({
<div className="relative">
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
autoCapitalize="none"
autoCorrect="off"
className="w-full rounded-lg border border-border/70 bg-background/70 py-2 pl-9 pr-3 text-sm placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
data-testid="relay-members-search"
onChange={(event) => setSearch(event.target.value)}
placeholder="Search members by name, npub, or role…"
spellCheck={false}
type="text"
value={search}
/>

View File

@@ -1,6 +1,8 @@
import { Loader2, RefreshCcw, RotateCw } from "lucide-react";
import type { ComponentType } from "react";
import { RefreshCcw, RotateCw } from "lucide-react";
import { Button } from "@/shared/ui/button";
import { Spinner } from "@/shared/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { useUpdaterContext } from "./hooks/UpdaterProvider";
@@ -9,13 +11,18 @@ import type { UpdateStatus } from "./hooks/use-updater";
const indicatorButtonClass =
"relative text-muted-foreground/80 hover:bg-muted/60 hover:text-foreground";
type IndicatorIcon = ComponentType<{
"aria-hidden"?: boolean;
className?: string;
}>;
const variants: Record<
"available" | "downloading" | "installing" | "ready",
{
Icon: typeof RefreshCcw;
Icon: IndicatorIcon;
iconClassName?: string;
label: string;
badgeColor: string;
spin?: boolean;
}
> = {
available: {
@@ -24,16 +31,16 @@ const variants: Record<
badgeColor: "bg-primary",
},
downloading: {
Icon: Loader2,
Icon: Spinner,
iconClassName: "h-4 w-4 border-2",
label: "Downloading update\u2026",
badgeColor: "bg-primary",
spin: true,
},
installing: {
Icon: Loader2,
Icon: Spinner,
iconClassName: "h-4 w-4 border-2",
label: "Installing update\u2026",
badgeColor: "bg-primary",
spin: true,
},
ready: {
Icon: RotateCw,
@@ -62,7 +69,7 @@ export function UpdateIndicator({ className }: { className?: string }) {
return null;
}
const { Icon, label, badgeColor, spin } = variant;
const { Icon, iconClassName = "h-4 w-4", label, badgeColor } = variant;
const isActionable = status.state === "available" || status.state === "ready";
const handleClick =
status.state === "ready"
@@ -87,7 +94,7 @@ export function UpdateIndicator({ className }: { className?: string }) {
type="button"
variant="ghost"
>
<Icon className={spin ? "animate-spin" : undefined} />
<Icon aria-hidden className={iconClassName} />
<span
className={`absolute right-1 top-1 h-1.5 w-1.5 rounded-full ${badgeColor} animate-pulse`}
/>

View File

@@ -120,7 +120,7 @@ export function ChannelTemplatesSettingsCard() {
type="button"
variant="outline"
>
<Plus className="mr-1.5 h-3.5 w-3.5" />
<Plus className="mr-1.5 h-4 w-4" />
Create
</Button>
</div>
@@ -229,13 +229,13 @@ function TemplateRow({
<div className="mt-1 flex items-center gap-3 text-xs text-muted-foreground">
{agentCount > 0 ? (
<span className="flex items-center gap-1">
<Users className="h-3 w-3" />
<Users className="h-4 w-4" />
{agentCount} {agentCount === 1 ? "agent" : "agents"}
</span>
) : null}
{template.canvasTemplate ? (
<span className="flex items-center gap-1">
<MessageSquare className="h-3 w-3" />
<MessageSquare className="h-4 w-4" />
canvas
</span>
) : null}
@@ -255,11 +255,11 @@ function TemplateRow({
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={onEdit}>
<Pencil className="mr-2 h-3.5 w-3.5" />
<Pencil className="mr-2 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={onDuplicate}>
<Copy className="mr-2 h-3.5 w-3.5" />
<Copy className="mr-2 h-4 w-4" />
Duplicate
</DropdownMenuItem>
{!template.isBuiltin ? (
@@ -267,7 +267,7 @@ function TemplateRow({
className="text-destructive focus:text-destructive"
onClick={onDelete}
>
<Trash2 className="mr-2 h-3.5 w-3.5" />
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
) : null}

View File

@@ -56,9 +56,9 @@ function InstallActions({
variant="outline"
>
{isInstalling ? (
<RefreshCw className="h-3.5 w-3.5 animate-spin" />
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<Download className="h-3.5 w-3.5" />
<Download className="h-4 w-4" />
)}
{isInstalling ? "Installing..." : "Install"}
</Button>
@@ -68,7 +68,7 @@ function InstallActions({
onClick={() => void openUrl(runtime.installInstructionsUrl)}
type="button"
>
<ExternalLink className="h-3 w-3" />
<ExternalLink className="h-4 w-4" />
View instructions
</button>
</div>

View File

@@ -225,7 +225,7 @@ function PairingDialog({
type="button"
>
<code className="min-w-0 flex-1 break-all">{qrUri}</code>
<Copy className="h-3.5 w-3.5 shrink-0" />
<Copy className="h-4 w-4 shrink-0" />
</button>
</div>
@@ -332,7 +332,7 @@ export function MobilePairingCard({
<SettingsOptionGroup>
<SettingsOptionRow className="gap-3">
<Smartphone className="h-5 w-5 shrink-0 text-muted-foreground" />
<Smartphone className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">Pair Mobile Device</p>
<p className="text-sm font-normal text-muted-foreground">

View File

@@ -227,7 +227,7 @@ export function NotificationSettingsCard({
>
{showComingSoon ? (
<>
<ChevronUp className="h-3.5 w-3.5" />
<ChevronUp className="h-4 w-4" />
Show less
</>
) : (

View File

@@ -57,7 +57,7 @@ function IdentityRow({
title={`Copy ${label}`}
type="button"
>
<Copy className="h-3.5 w-3.5 shrink-0" />
<Copy className="h-4 w-4 shrink-0" />
Copy
</button>
) : null}
@@ -97,7 +97,7 @@ function EditProfileMetadataButton({
title={accessibleLabel}
type="button"
>
<Icon className="h-3.5 w-3.5 shrink-0" />
<Icon className="h-4 w-4 shrink-0" />
{actionLabel}
</button>
);
@@ -577,7 +577,7 @@ export function ProfileSettingsCard({
this device.
</p>
</div>
<ChevronDown className="h-5 w-5 shrink-0 text-muted-foreground transition-[color,transform] duration-150 ease-out group-open:rotate-180 group-hover/identity:text-foreground group-focus-visible/identity:text-foreground" />
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-[color,transform] duration-150 ease-out group-open:rotate-180 group-hover/identity:text-foreground group-focus-visible/identity:text-foreground" />
</summary>
<div
className="border-t border-border/55 divide-y divide-border/55"

View File

@@ -193,9 +193,12 @@ function ThemeSettingsCard() {
<div className="relative mb-3">
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<input
autoCapitalize="none"
autoCorrect="off"
className="w-full rounded-lg border border-border/70 bg-background/70 py-2 pl-9 pr-3 text-sm placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
onChange={(e) => setSearch(e.target.value)}
placeholder="Search themes..."
spellCheck={false}
type="text"
value={search}
/>
@@ -269,7 +272,7 @@ function ThemeSettingsCard() {
type="button"
>
{accentColor === color.value && (
<Check className={cn("h-3.5 w-3.5", checkClassName)} />
<Check className={cn("h-4 w-4", checkClassName)} />
)}
</button>
);

View File

@@ -135,9 +135,9 @@ export function SoundPicker({
variant="ghost"
>
{isPlaying ? (
<Pause className="h-3 w-3" />
<Pause className="h-4 w-4" />
) : (
<Play className="h-3 w-3" />
<Play className="h-4 w-4" />
)}
</Button>
</span>

View File

@@ -255,7 +255,7 @@ function SectionHeaderActions({
title="Mark all as read"
type="button"
>
<CheckCheck className="h-3.5 w-3.5" />
<CheckCheck className="h-4 w-4" />
</button>
) : null}
{onBrowse ? (
@@ -548,7 +548,7 @@ export function CustomChannelSection({
>
<GripVertical
className={cn(
"h-3 w-3 shrink-0 text-sidebar-foreground/30",
"h-4 w-4 shrink-0 text-sidebar-foreground/30",
SECTION_ACTION_VISIBILITY_CLASS,
)}
aria-hidden="true"
@@ -580,7 +580,7 @@ export function CustomChannelSection({
title="Mark all as read"
type="button"
>
<CheckCheck className="h-3.5 w-3.5" />
<CheckCheck className="h-4 w-4" />
</button>
) : null}
<button
@@ -592,7 +592,7 @@ export function CustomChannelSection({
}}
type="button"
>
<Pencil className="h-3.5 w-3.5" />
<Pencil className="h-4 w-4" />
</button>
<button
aria-label="Delete section"
@@ -603,7 +603,7 @@ export function CustomChannelSection({
}}
type="button"
>
<Trash2 className="h-3.5 w-3.5" />
<Trash2 className="h-4 w-4" />
</button>
</div>
</div>

View File

@@ -3,7 +3,7 @@ import type * as React from "react";
import { Button } from "@/shared/ui/button";
const MORE_UNREAD_BUTTON_CLASS =
"h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-[11px] font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-3.5";
"h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-[11px] font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-4";
export function MoreUnreadButton({
bottomClassName = "bottom-0",

View File

@@ -202,7 +202,7 @@ export function ChannelMenuButton({
{isMuted ? (
<BellOff
className={cn(
"ml-auto h-3 w-3 shrink-0",
"ml-auto h-4 w-4 shrink-0",
isActive
? "text-sidebar-active-foreground/60"
: "text-sidebar-foreground/40",

View File

@@ -99,7 +99,7 @@ export function ChannelCombobox({
<span className="truncate">
{selected ? formatChannelLabel(selected) : "Select a channel..."}
</span>
<ChevronsUpDown className="ml-2 h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 text-muted-foreground" />
</button>
</PopoverTrigger>
<PopoverContent
@@ -107,10 +107,11 @@ export function ChannelCombobox({
className="w-(--radix-popover-trigger-width) p-0"
>
<div className="flex items-center gap-2 border-b border-border px-3 py-2">
<Search className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<Search className="h-4 w-4 shrink-0 text-muted-foreground" />
<input
autoCapitalize="off"
autoCapitalize="none"
autoComplete="off"
autoCorrect="off"
ref={(el) => el?.focus()}
className="flex-1 bg-transparent text-sm outline-hidden placeholder:text-muted-foreground"
onChange={(e) => {
@@ -119,6 +120,7 @@ export function ChannelCombobox({
}}
onKeyDown={handleKeyDown}
placeholder="Search channels..."
spellCheck={false}
value={query}
/>
</div>
@@ -142,7 +144,7 @@ export function ChannelCombobox({
>
<Check
className={cn(
"h-3.5 w-3.5 shrink-0",
"h-4 w-4 shrink-0",
channel.id === value ? "opacity-100" : "opacity-0",
)}
/>

View File

@@ -54,7 +54,7 @@ export function WorkflowApprovalCard({ approval }: WorkflowApprovalCardProps) {
}
size="sm"
>
<Check className="mr-1 h-3 w-3" />
<Check className="mr-1 h-4 w-4" />
Approve
</Button>
<Button
@@ -70,7 +70,7 @@ export function WorkflowApprovalCard({ approval }: WorkflowApprovalCardProps) {
size="sm"
variant="destructive"
>
<X className="mr-1 h-3 w-3" />
<X className="mr-1 h-4 w-4" />
Deny
</Button>
</div>

View File

@@ -89,7 +89,7 @@ export function WorkflowCard({
{channelName ? <span>{channelName}</span> : null}
{triggerSummary ? <span>{triggerSummary}</span> : null}
<span className="flex items-center gap-1">
<Clock className="h-3 w-3" />
<Clock className="h-4 w-4" />
{new Date(workflow.updatedAt * 1000).toLocaleDateString()}
</span>
</div>

View File

@@ -93,7 +93,7 @@ export function WorkflowDetailPanel({
size="sm"
variant="outline"
>
<Pencil className="mr-1 h-3 w-3" />
<Pencil className="mr-1 h-4 w-4" />
Edit
</Button>
) : null}
@@ -103,7 +103,7 @@ export function WorkflowDetailPanel({
size="sm"
variant="outline"
>
<Play className="mr-1 h-3 w-3" />
<Play className="mr-1 h-4 w-4" />
{triggerMutation.isPending ? "Triggering..." : "Trigger"}
</Button>
<Button

View File

@@ -219,7 +219,7 @@ export function WorkflowFormBuilder({
type="button"
variant="ghost"
>
<Code className="h-3.5 w-3.5" />
<Code className="h-4 w-4" />
{mode === "form" ? "Edit as YAML" : "Back to form"}
</Button>
</div>
@@ -335,7 +335,7 @@ export function WorkflowFormBuilder({
type="button"
variant="outline"
>
<Plus className="h-3.5 w-3.5" />
<Plus className="h-4 w-4" />
Add step
</Button>
</div>

View File

@@ -326,7 +326,7 @@ export function WorkflowStepCard({
type="button"
variant="ghost"
>
<Trash2 className="h-3.5 w-3.5 text-muted-foreground" />
<Trash2 className="h-4 w-4 text-muted-foreground" />
</Button>
</div>

View File

@@ -48,7 +48,7 @@ export function WorkflowWebhookHeadersEditor({
type="button"
variant="outline"
>
<Plus className="h-3.5 w-3.5" />
<Plus className="h-4 w-4" />
Add header
</Button>
</div>
@@ -109,7 +109,7 @@ export function WorkflowWebhookHeadersEditor({
type="button"
variant="ghost"
>
<Trash2 className="h-3.5 w-3.5 text-muted-foreground" />
<Trash2 className="h-4 w-4 text-muted-foreground" />
</Button>
</div>
))}

View File

@@ -25,7 +25,7 @@ export function FormSelect({
>
{children}
</select>
<ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
</div>
);
}

View File

@@ -121,7 +121,7 @@ export function WorkspaceSwitcher({
data-testid="relay-connection-warning"
role="img"
>
<WifiOff className={isProfileVariant ? "h-3 w-3" : "h-4 w-4"} />
<WifiOff className={isProfileVariant ? "h-4 w-4" : "h-4 w-4"} />
</span>
</TooltipTrigger>
<TooltipContent side={isProfileVariant ? "top" : "bottom"}>
@@ -154,8 +154,8 @@ export function WorkspaceSwitcher({
<ChevronDown
className={
isProfileVariant
? "h-3 w-3 shrink-0 text-sidebar-foreground/45"
: "h-3.5 w-3.5 shrink-0 text-sidebar-foreground/50"
? "h-4 w-4 shrink-0 text-sidebar-foreground/45"
: "h-4 w-4 shrink-0 text-sidebar-foreground/50"
}
/>
)}
@@ -209,7 +209,7 @@ export function WorkspaceSwitcher({
>
<span className="flex h-4 w-4 shrink-0 items-center justify-center">
{activeWorkspace?.id === workspace.id ? (
<Check className="h-3.5 w-3.5 text-primary" />
<Check className="h-4 w-4 text-primary" />
) : null}
</span>
<span className="min-w-0 flex-1 truncate">
@@ -226,7 +226,7 @@ export function WorkspaceSwitcher({
}}
type="button"
>
<MoreHorizontal className="h-3.5 w-3.5" />
<MoreHorizontal className="h-4 w-4" />
</button>
</div>
))}
@@ -296,7 +296,7 @@ export function WorkspaceSwitcher({
>
<span className="flex h-4 w-4 shrink-0 items-center justify-center">
{activeWorkspace?.id === workspace.id ? (
<Check className="h-3.5 w-3.5 text-primary" />
<Check className="h-4 w-4 text-primary" />
) : null}
</span>
<span className="min-w-0 flex-1 truncate">{workspace.name}</span>
@@ -311,7 +311,7 @@ export function WorkspaceSwitcher({
}}
type="button"
>
<MoreHorizontal className="h-3.5 w-3.5" />
<MoreHorizontal className="h-4 w-4" />
</button>
</DropdownMenuItem>
))}

View File

@@ -1627,7 +1627,7 @@ function VideoReviewDialog({
type="button"
onClick={() => setIsEmojiPickerOpen((open) => !open)}
>
<SmilePlus className="pointer-events-none h-5 w-5" />
<SmilePlus className="pointer-events-none h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent>More reactions</TooltipContent>
@@ -1900,7 +1900,7 @@ function VideoReviewCommentBody({
</p>
{reactions.some((reaction) => reaction.reactedByCurrentUser) ? (
<Check
className="ml-auto h-3.5 w-3.5 shrink-0 text-primary"
className="ml-auto h-4 w-4 shrink-0 text-primary"
aria-hidden
/>
) : null}

View File

@@ -16,7 +16,9 @@ export function ImportStatusIcon({
}) {
switch (status) {
case "importing":
return <Spinner className="h-4 w-4 shrink-0 text-muted-foreground" />;
return (
<Spinner className="h-4 w-4 shrink-0 border-2 text-muted-foreground" />
);
case "done":
return <Check className="h-4 w-4 shrink-0 text-green-500" />;
case "error":

View File

@@ -520,7 +520,7 @@ function MarkdownCodeBlock({
type="button"
variant="ghost"
>
<Copy className="h-3.5 w-3.5" />
<Copy className="h-4 w-4" />
<span className="sr-only">Copy code block</span>
</Button>
</TooltipTrigger>
@@ -579,7 +579,7 @@ function FileCard({
className="my-1 inline-flex max-w-sm items-center gap-3 rounded-xl border border-border/70 bg-muted/40 px-3 py-2 text-left no-underline transition-colors hover:bg-muted/70"
>
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-background text-muted-foreground">
<FileText className="h-5 w-5" />
<FileText className="h-4 w-4" />
</span>
<span className="min-w-0 flex-1">
<span className="block truncate text-sm font-medium text-foreground">

View File

@@ -751,8 +751,8 @@ test("narrow thread view collapses channel header actions into a menu", async ({
throw new Error("Expected header action menu and thread panel bounds");
}
const menuGapPx = threadPanelBox.x - (menuBox.x + menuBox.width);
expect(menuGapPx).toBeGreaterThanOrEqual(10);
expect(menuGapPx).toBeLessThanOrEqual(14);
expect(menuGapPx).toBeGreaterThanOrEqual(22);
expect(menuGapPx).toBeLessThanOrEqual(26);
await menuTrigger.click();

View File

@@ -258,7 +258,7 @@ export function RepoBlobPage() {
<BackLink repoId={repoId} />
<div className="mt-4 flex flex-wrap items-center gap-3">
<FileText className="h-5 w-5 text-muted-foreground" />
<FileText className="h-4 w-4 text-muted-foreground" />
<h1 className="min-w-0 truncate font-mono text-sm">{filepath}</h1>
<div className="ml-auto flex items-center gap-2">
{view &&

View File

@@ -22,12 +22,12 @@ export function RepoRefsSection({
<>
<div className="flex items-center gap-1.5">
<Badge variant="secondary">
<GitBranch className="mr-1 h-3 w-3" />
<GitBranch className="mr-1 h-4 w-4" />
{refs.head.ref}
</Badge>
{refs.head.sha && (
<Badge variant="outline" className="font-mono text-xs">
<Hash className="mr-0.5 h-3 w-3" />
<Hash className="mr-0.5 h-4 w-4" />
{refs.head.sha.slice(0, 7)}
</Badge>
)}
@@ -36,13 +36,13 @@ export function RepoRefsSection({
</>
)}
<span className="flex items-center gap-1">
<GitBranch className="h-3.5 w-3.5" />
<GitBranch className="h-4 w-4" />
{refs.branches.length}{" "}
{refs.branches.length === 1 ? "branch" : "branches"}
</span>
<span className="text-muted-foreground/60">&middot;</span>
<span className="flex items-center gap-1">
<Tag className="h-3.5 w-3.5" />
<Tag className="h-4 w-4" />
{refs.tags.length} {refs.tags.length === 1 ? "tag" : "tags"}
</span>
</div>

View File

@@ -104,7 +104,7 @@ export function ReposPage() {
<div className="flex w-full gap-8 px-4 py-8">
<div className="min-w-0 flex-1">
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold">
<BookMarked className="h-5 w-5" /> Repositories
<BookMarked className="h-4 w-4" /> Repositories
</h2>
<div className="divide-y">
{["a", "b", "c", "d", "e"].map((key) => (
@@ -131,7 +131,7 @@ export function ReposPage() {
</div>
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold">
<BookMarked className="h-5 w-5" /> Repositories
<BookMarked className="h-4 w-4" /> Repositories
</h2>
{/* Search + Sort bar */}