import React, { useState } from "react";
/**
* SessionItem — a single row in the sessions sidebar.
*
* Shows status dot (pulsing/static), title, timestamp, message count.
* Claude-Code-on-Web parity: active=amber pulse, completed=green,
* failed=red, waiting=blue.
*/
export default function SessionItem({ session, isActive, onSelect, onDelete }) {
const [hovering, setHovering] = useState(false);
const status = session.status || "active";
const dotColor = {
active: "#F59E0B",
completed: "#10B981",
failed: "#EF4444",
waiting: "#3B82F6",
paused: "#6B7280",
}[status] || "#6B7280";
const isPulsing = status === "active";
const timeAgo = formatTimeAgo(session.updated_at);
// Prefer name (set from first user prompt) over generic fallback
const title =
session.name ||
(session.branch ? `${session.branch}` : `Session ${session.id?.slice(0, 8)}`);
return (
setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
{/* Status dot */}
{/* Content */}
{title}
{timeAgo}
{session.mode && (
{session.mode === "github" ? "GH" : session.mode === "local-git" ? "Git" : "Dir"}
)}
{session.message_count > 0 && (
{session.message_count} msgs
)}
{/* Delete button (on hover) */}
{hovering && (
)}
);
}
function formatTimeAgo(isoStr) {
if (!isoStr) return "";
try {
const date = new Date(isoStr);
const now = new Date();
const diffMs = now - date;
const diffMin = Math.floor(diffMs / 60000);
if (diffMin < 1) return "just now";
if (diffMin < 60) return `${diffMin}m ago`;
const diffHr = Math.floor(diffMin / 60);
if (diffHr < 24) return `${diffHr}h ago`;
const diffDay = Math.floor(diffHr / 24);
return `${diffDay}d ago`;
} catch {
return "";
}
}
const styles = {
row: {
display: "flex",
alignItems: "center",
gap: 8,
padding: "8px 10px",
borderRadius: 6,
cursor: "pointer",
transition: "background-color 0.15s",
position: "relative",
marginBottom: 2,
animation: "session-fade-in 0.25s ease-out",
},
dot: {
width: 8,
height: 8,
borderRadius: "50%",
flexShrink: 0,
},
content: {
flex: 1,
minWidth: 0,
overflow: "hidden",
},
title: {
fontSize: 12,
fontWeight: 500,
color: "#E4E4E7",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
},
meta: {
fontSize: 10,
color: "#71717A",
marginTop: 2,
display: "flex",
alignItems: "center",
gap: 6,
},
badge: {
fontSize: 9,
background: "#27272A",
padding: "1px 5px",
borderRadius: 8,
color: "#A1A1AA",
},
deleteBtn: {
position: "absolute",
right: 6,
top: 6,
width: 18,
height: 18,
borderRadius: 3,
border: "none",
background: "rgba(239, 68, 68, 0.15)",
color: "#EF4444",
fontSize: 14,
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
lineHeight: 1,
},
};