mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 00:35:32 +08:00
420f4c8c23
OpenClaw gateway injects `[message_id: UUID]` metadata at the end of every message, wasting display space. Strip this suffix from both title and summary fields. Also change session title display from single-line truncate to line-clamp-2, so longer titles (e.g. OpenClaw's timestamp-prefixed messages) can show more meaningful content across two lines.
111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import { ChevronRight, Clock } from "lucide-react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip";
|
|
import { cn } from "@/lib/utils";
|
|
import { ProviderIcon } from "@/components/ProviderIcon";
|
|
import type { SessionMeta } from "@/types";
|
|
import {
|
|
formatRelativeTime,
|
|
formatSessionTitle,
|
|
getProviderIconName,
|
|
getProviderLabel,
|
|
getSessionKey,
|
|
highlightText,
|
|
} from "./utils";
|
|
|
|
interface SessionItemProps {
|
|
session: SessionMeta;
|
|
isSelected: boolean;
|
|
selectionMode: boolean;
|
|
isChecked: boolean;
|
|
isCheckDisabled?: boolean;
|
|
searchQuery?: string;
|
|
onSelect: (key: string) => void;
|
|
onToggleChecked: (checked: boolean) => void;
|
|
}
|
|
|
|
export function SessionItem({
|
|
session,
|
|
isSelected,
|
|
selectionMode,
|
|
isChecked,
|
|
isCheckDisabled = false,
|
|
searchQuery,
|
|
onSelect,
|
|
onToggleChecked,
|
|
}: SessionItemProps) {
|
|
const { t } = useTranslation();
|
|
const title = formatSessionTitle(session);
|
|
const lastActive = session.lastActiveAt || session.createdAt || undefined;
|
|
const sessionKey = getSessionKey(session);
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"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",
|
|
)}
|
|
>
|
|
{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 line-clamp-2 flex-1">
|
|
{searchQuery ? highlightText(title, searchQuery) : 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>
|
|
);
|
|
}
|