feat: add bulk delete for session manager (#1693)

* feat: add bulk delete for session manager

* fix: address batch delete review issues

* fix: keep session list in sync after batch delete
This commit is contained in:
Alexlangl
2026-03-30 22:15:57 +08:00
committed by GitHub
parent 4f7ea76347
commit 8e2ffbc845
11 changed files with 960 additions and 218 deletions
+61 -34
View File
@@ -1,5 +1,6 @@
import { ChevronRight, Clock } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Checkbox } from "@/components/ui/checkbox";
import {
Tooltip,
TooltipContent,
@@ -19,13 +20,21 @@ import {
interface SessionItemProps {
session: SessionMeta;
isSelected: boolean;
selectionMode: boolean;
isChecked: boolean;
isCheckDisabled?: boolean;
onSelect: (key: string) => void;
onToggleChecked: (checked: boolean) => void;
}
export function SessionItem({
session,
isSelected,
selectionMode,
isChecked,
isCheckDisabled = false,
onSelect,
onToggleChecked,
}: SessionItemProps) {
const { t } = useTranslation();
const title = formatSessionTitle(session);
@@ -33,46 +42,64 @@ export function SessionItem({
const sessionKey = getSessionKey(session);
return (
<button
type="button"
onClick={() => onSelect(sessionKey)}
<div
className={cn(
"w-full text-left rounded-lg px-3 py-2.5 transition-all group",
"flex items-start gap-2 rounded-lg px-3 py-2.5 transition-all group",
isSelected
? "bg-primary/10 border border-primary/30"
: "hover:bg-muted/60 border border-transparent",
)}
>
<div className="flex items-center gap-2 mb-1">
<Tooltip>
<TooltipTrigger asChild>
<span className="shrink-0">
<ProviderIcon
icon={getProviderIconName(session.providerId)}
name={session.providerId}
size={18}
/>
</span>
</TooltipTrigger>
<TooltipContent>
{getProviderLabel(session.providerId, t)}
</TooltipContent>
</Tooltip>
<span className="text-sm font-medium truncate flex-1">{title}</span>
<ChevronRight
className={cn(
"size-4 text-muted-foreground/50 shrink-0 transition-transform",
isSelected && "text-primary rotate-90",
)}
/>
</div>
{selectionMode && (
<div className="shrink-0 pt-0.5">
<Checkbox
checked={isChecked}
disabled={isCheckDisabled}
aria-label={t("sessionManager.selectForBatch", {
defaultValue: "选择会话",
})}
onCheckedChange={(checked) => onToggleChecked(Boolean(checked))}
/>
</div>
)}
<button
type="button"
onClick={() => onSelect(sessionKey)}
className="min-w-0 flex-1 text-left"
>
<div className="flex items-center gap-2 mb-1">
<Tooltip>
<TooltipTrigger asChild>
<span className="shrink-0">
<ProviderIcon
icon={getProviderIconName(session.providerId)}
name={session.providerId}
size={18}
/>
</span>
</TooltipTrigger>
<TooltipContent>
{getProviderLabel(session.providerId, t)}
</TooltipContent>
</Tooltip>
<span className="text-sm font-medium truncate flex-1">{title}</span>
<ChevronRight
className={cn(
"size-4 text-muted-foreground/50 shrink-0 transition-transform",
isSelected && "text-primary rotate-90",
)}
/>
</div>
<div className="flex items-center gap-1 text-[11px] text-muted-foreground">
<Clock className="size-3" />
<span>
{lastActive ? formatRelativeTime(lastActive, t) : t("common.unknown")}
</span>
</div>
</button>
<div className="flex items-center gap-1 text-[11px] text-muted-foreground">
<Clock className="size-3" />
<span>
{lastActive
? formatRelativeTime(lastActive, t)
: t("common.unknown")}
</span>
</div>
</button>
</div>
);
}