import type { GatewaySessionInfo } from "../../../../gateway/index.js";
export type SidebarRowKind = "header" | "session";
export type SidebarRow =
| { kind: "header"; groupKey: string; label: string; count: number; collapsed: boolean }
| { kind: "session"; session: GatewaySessionInfo; groupKey: string };
export type SessionGroup = {
groupKey: string;
label: string;
sessions: GatewaySessionInfo[];
};
export function groupSessionsByProject(sessions: GatewaySessionInfo[]): SessionGroup[] {
const map = new Map<string, GatewaySessionInfo[]>();
for (const s of sessions) {
const key = s.cwd ?? "(unknown)";
const list = map.get(key);
if (list) list.push(s);
else map.set(key, [s]);
}
const groups: SessionGroup[] = [];
for (const [key, list] of map) {
const parts = key.split("/");
const label = parts[parts.length - 1] ?? key;
groups.push({ groupKey: key, label, sessions: list });
}
groups.sort((a, b) => {
const aTime = Math.max(...a.sessions.map((s) => s.lastModified ?? 0));
const bTime = Math.max(...b.sessions.map((s) => s.lastModified ?? 0));
return bTime - aTime;
});
return groups;
}
const STATUS_LABELS: Record<string, string> = {
running: "Running",
waiting: "Waiting",
idle: "Idle",
done: "Done",
error: "Error",
background: "Background",
};
export function groupSessionsByStatus(sessions: GatewaySessionInfo[]): SessionGroup[] {
const map = new Map<string, GatewaySessionInfo[]>();
for (const s of sessions) {
const key = s.tag ?? "idle";
const list = map.get(key);
if (list) list.push(s);
else map.set(key, [s]);
}
const order = ["running", "waiting", "background", "idle", "done", "error"];
const groups: SessionGroup[] = [];
for (const [key, list] of map) {
groups.push({
groupKey: `status:${key}`,
label: STATUS_LABELS[key] ?? key,
sessions: list,
});
}
groups.sort((a, b) => {
const ak = a.groupKey.replace("status:", "");
const bk = b.groupKey.replace("status:", "");
return (order.indexOf(ak) === -1 ? 99 : order.indexOf(ak)) - (order.indexOf(bk) === -1 ? 99 : order.indexOf(bk));
});
return groups;
}
export function groupSessions(sessions: GatewaySessionInfo[], by: "project" | "status"): SessionGroup[] {
return by === "project" ? groupSessionsByProject(sessions) : groupSessionsByStatus(sessions);
}
export function computeSmartCollapse(
groups: SessionGroup[],
activeSessionKey: string,
): Set<string> {
if (groups.length <= 1) return new Set();
const collapsed = new Set<string>();
for (const g of groups) {
const hasActive = g.sessions.some(
(s) => (s.sessionKey ?? s.sessionId) === activeSessionKey,
);
if (!hasActive) collapsed.add(g.groupKey);
}
return collapsed;
}
export function flattenSidebarRows(
groups: SessionGroup[],
collapsed: Set<string>,
): SidebarRow[] {
if (groups.length === 1) {
return groups[0]!.sessions.map((s) => ({
kind: "session" as const,
session: s,
groupKey: groups[0]!.groupKey,
}));
}
const rows: SidebarRow[] = [];
for (const g of groups) {
const isCollapsed = collapsed.has(g.groupKey);
rows.push({
kind: "header",
groupKey: g.groupKey,
label: g.label,
count: g.sessions.length,
collapsed: isCollapsed,
});
if (!isCollapsed) {
for (const s of g.sessions) {
rows.push({ kind: "session", session: s, groupKey: g.groupKey });
}
}
}
return rows;
}
const STATUS_ICONS: Record<string, string> = {
running: "✻",
waiting: "✽",
idle: "∙",
done: "✓",
error: "✗",
background: "■",
};
export function sessionStatusIcon(session: GatewaySessionInfo): string {
const tag = session.tag ?? "idle";
return STATUS_ICONS[tag] ?? "∙";
}
export function sessionDisplayTitle(session: GatewaySessionInfo, maxLen: number): string {
const raw = session.customTitle || session.aiTitle || session.firstPrompt || session.summary || session.sessionId;
if (raw.length <= maxLen) return raw;
return raw.slice(0, maxLen - 1) + "…";
}
export function relativeTime(ts: number): string {
const diff = Date.now() - ts;
if (diff < 0) return "now";
const seconds = Math.floor(diff / 1000);
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h`;
const days = Math.floor(hours / 24);
return `${days}d`;
}