fix(sidebar): align title/folder/item start position and stabilize status icon rendering

This commit is contained in:
xintaofei
2026-04-21 17:47:35 +08:00
parent f0d530e1cb
commit 7a6e19097e
4 changed files with 73 additions and 81 deletions

View File

@@ -143,7 +143,7 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({
"relative flex h-[1.9375rem] w-full items-center gap-[0.625rem] text-left outline-none", "relative flex h-[1.9375rem] w-full items-center gap-[0.625rem] text-left outline-none",
"rounded-[0.375rem] text-sidebar-foreground", "rounded-[0.375rem] text-sidebar-foreground",
"transition-colors duration-[120ms]", "transition-colors duration-[120ms]",
"pr-[0.5rem] pl-[1.875rem]", "pr-[0.5rem] pl-7",
isSelected isSelected
? "bg-sidebar-primary/15" ? "bg-sidebar-primary/15"
: "hover:bg-[color-mix(in_oklab,var(--sidebar-accent),var(--sidebar-foreground)_2%)]" : "hover:bg-[color-mix(in_oklab,var(--sidebar-accent),var(--sidebar-foreground)_2%)]"
@@ -152,7 +152,11 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({
<span <span
aria-hidden aria-hidden
className="pointer-events-none absolute top-0 bottom-0 rounded-[0.125rem] bg-sidebar-primary/5" className="pointer-events-none absolute top-0 bottom-0 rounded-[0.125rem] bg-sidebar-primary/5"
style={{ left: "0.9375rem", width: "0.125rem" }} style={{
left: "1rem",
width: "0.125rem",
transform: "translateX(-50%)",
}}
/> />
<SidebarStatusIcon status={beadStatus} /> <SidebarStatusIcon status={beadStatus} />

View File

@@ -138,7 +138,7 @@ const FolderHeader = memo(function FolderHeader({
onClick={() => onToggle(folderId)} onClick={() => onToggle(folderId)}
className={cn( className={cn(
"flex h-[1.9375rem] w-full items-center gap-[0.5rem] cursor-pointer outline-none", "flex h-[1.9375rem] w-full items-center gap-[0.5rem] cursor-pointer outline-none",
"rounded-[0.4375rem] px-[0.625rem]", "rounded-[0.4375rem] px-2",
"text-sidebar-foreground hover:bg-[color-mix(in_oklab,var(--sidebar-accent),var(--sidebar-foreground)_2%)]", "text-sidebar-foreground hover:bg-[color-mix(in_oklab,var(--sidebar-accent),var(--sidebar-foreground)_2%)]",
"transition-[background-color,color] duration-150", "transition-[background-color,color] duration-150",
highlighted && "ring-2 ring-sidebar-primary ring-offset-1" highlighted && "ring-2 ring-sidebar-primary ring-offset-1"

View File

@@ -9,44 +9,52 @@ interface SidebarStatusIconProps {
className?: string className?: string
} }
function IconFrame({
children,
colorClass,
className,
}: {
children: React.ReactNode
colorClass: string
className?: string
}) {
return (
<div
className={cn(
"pointer-events-none absolute top-1/2",
"flex items-center justify-center",
colorClass,
className
)}
style={{
left: "1rem",
width: "0.625rem",
height: "0.625rem",
transform: "translate(-50%, -50%)",
}}
aria-hidden
>
{children}
</div>
)
}
export function SidebarStatusIcon({ export function SidebarStatusIcon({
status, status,
className, className,
}: SidebarStatusIconProps) { }: SidebarStatusIconProps) {
if (status === "running") { if (status === "running") {
return ( return (
<div <IconFrame
className={cn( colorClass="text-amber-600 dark:text-amber-400"
"pointer-events-none absolute top-1/2", className={className}
"flex items-center justify-center",
"text-amber-600 dark:text-amber-400",
className
)}
style={{
left: "0.625rem",
width: "0.75rem",
height: "0.75rem",
transform: "translateY(-50%)",
}}
aria-hidden
> >
<svg <svg
width="0.75rem" width="0.625rem"
height="0.75rem" height="0.625rem"
viewBox="0 0 12 12" viewBox="0 0 10 10"
className="absolute inset-0" preserveAspectRatio="xMidYMid meet"
> >
<circle
cx="6"
cy="6"
r="5.4"
fill="none"
stroke="currentColor"
strokeWidth="1"
opacity="0.18"
/>
</svg>
<svg width="0.625rem" height="0.625rem" viewBox="0 0 10 10">
<circle <circle
cx="5" cx="5"
cy="5" cy="5"
@@ -73,28 +81,19 @@ export function SidebarStatusIcon({
/> />
</path> </path>
</svg> </svg>
</div> </IconFrame>
) )
} }
if (status === "failed") { if (status === "failed") {
return ( return (
<div <IconFrame colorClass="text-destructive" className={className}>
className={cn( <svg
"pointer-events-none absolute top-1/2", width="0.625rem"
"flex items-center justify-center", height="0.625rem"
"text-destructive", viewBox="0 0 10 10"
className preserveAspectRatio="xMidYMid meet"
)} >
style={{
left: "0.6875rem",
width: "0.625rem",
height: "0.625rem",
transform: "translateY(-50%)",
}}
aria-hidden
>
<svg width="0.625rem" height="0.625rem" viewBox="0 0 10 10">
<circle <circle
cx="5" cx="5"
cy="5" cy="5"
@@ -111,28 +110,19 @@ export function SidebarStatusIcon({
strokeLinecap="round" strokeLinecap="round"
/> />
</svg> </svg>
</div> </IconFrame>
) )
} }
if (status === "active") { if (status === "active") {
return ( return (
<div <IconFrame colorClass="text-sidebar-primary" className={className}>
className={cn( <svg
"pointer-events-none absolute top-1/2", width="0.625rem"
"flex items-center justify-center", height="0.625rem"
"text-sidebar-primary", viewBox="0 0 10 10"
className preserveAspectRatio="xMidYMid meet"
)} >
style={{
left: "0.6875rem",
width: "0.625rem",
height: "0.625rem",
transform: "translateY(-50%)",
}}
aria-hidden
>
<svg width="0.625rem" height="0.625rem" viewBox="0 0 10 10">
<circle <circle
cx="5" cx="5"
cy="5" cy="5"
@@ -144,24 +134,22 @@ export function SidebarStatusIcon({
/> />
<circle cx="5" cy="5" r="2" fill="currentColor" /> <circle cx="5" cy="5" r="2" fill="currentColor" />
</svg> </svg>
</div> </IconFrame>
) )
} }
return ( return (
<div <IconFrame colorClass="text-sidebar-primary/40" className={className}>
className={cn( <svg
"pointer-events-none absolute top-1/2 rounded-full bg-sidebar-primary/40", width="0.45rem"
className height="0.45rem"
)} viewBox="0 0 12 12"
style={{ preserveAspectRatio="xMidYMid meet"
left: "0.8125rem", style={{ overflow: "visible" }}
width: "0.375rem", >
height: "0.375rem", <circle cx="6" cy="6" r="5" fill="currentColor" />
transform: "translateY(-50%)", </svg>
}} </IconFrame>
aria-hidden
/>
) )
} }

View File

@@ -88,7 +88,7 @@ export function Sidebar() {
return ( return (
<aside className="flex h-full min-h-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground select-none"> <aside className="flex h-full min-h-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground select-none">
<TooltipProvider> <TooltipProvider>
<div className="flex h-10 shrink-0 items-center justify-between gap-2 pl-[1.25rem] pr-2"> <div className="flex h-10 shrink-0 items-center justify-between gap-2 pl-[1.125rem] pr-2">
<div className="flex min-w-0 items-baseline gap-[0.375rem]"> <div className="flex min-w-0 items-baseline gap-[0.375rem]">
<h2 className="truncate text-[0.875rem] font-bold tracking-[-0.00625rem] text-sidebar-foreground"> <h2 className="truncate text-[0.875rem] font-bold tracking-[-0.00625rem] text-sidebar-foreground">
{t("title")} {t("title")}