Files
pi/packages/coding-agent/src/tui/session-selector.ts
T
Mario Zechner 6b48e73607 Fix session selector: handle Ctrl+C and viewport width
- Add SIGINT handler to allow Ctrl+C to exit during session selection
- Remove hardcoded 60-char truncation, let SelectList handle dynamic truncation
- Simplify date format (5m, 2h, 3d instead of "5m ago")
- Make metadata more compact (5msg instead of "5 msgs")
2025-11-12 09:21:17 +01:00

109 lines
2.9 KiB
TypeScript

import { type Component, Container, type SelectItem, SelectList, Text } from "@mariozechner/pi-tui";
import chalk from "chalk";
import type { SessionManager } from "../session-manager.js";
/**
* Dynamic border component that adjusts to viewport width
*/
class DynamicBorder implements Component {
render(width: number): string[] {
return [chalk.blue("─".repeat(Math.max(1, width)))];
}
}
interface SessionItem {
path: string;
id: string;
created: Date;
modified: Date;
messageCount: number;
firstMessage: string;
}
/**
* Component that renders a session selector
*/
export class SessionSelectorComponent extends Container {
private selectList: SelectList;
constructor(sessionManager: SessionManager, onSelect: (sessionPath: string) => void, onCancel: () => void) {
super();
// Load all sessions
const sessions = sessionManager.loadAllSessions();
if (sessions.length === 0) {
this.addChild(new DynamicBorder());
this.addChild(new Text(chalk.gray(" No previous sessions found"), 0, 0));
this.addChild(new DynamicBorder());
this.selectList = new SelectList([], 0);
this.addChild(this.selectList);
// Auto-cancel if no sessions
setTimeout(() => onCancel(), 100);
return;
}
// Format sessions as select items
const items: SelectItem[] = sessions.map((session) => {
// Format dates
const formatDate = (date: Date): string => {
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMs / 3600000);
const diffDays = Math.floor(diffMs / 86400000);
if (diffMins < 1) return "now";
if (diffMins < 60) return `${diffMins}m`;
if (diffHours < 24) return `${diffHours}h`;
if (diffDays === 1) return "1d";
if (diffDays < 7) return `${diffDays}d`;
// Fallback to date string
return date.toLocaleDateString();
};
// Normalize first message to single line
const normalizedMessage = session.firstMessage.replace(/\n/g, " ").trim();
// Build description with metadata (single line, compact format)
const modified = formatDate(session.modified);
const msgCount = `${session.messageCount}msg`;
// Keep description compact: "modified • count"
const description = `${modified}${msgCount}`;
return {
value: session.path,
label: normalizedMessage, // Let SelectList handle truncation based on actual width
description,
};
});
// Add top border
this.addChild(new DynamicBorder());
this.addChild(new Text(chalk.bold("Select a session to resume:"), 1, 0));
// Create selector
this.selectList = new SelectList(items, Math.min(10, items.length));
this.selectList.onSelect = (item) => {
onSelect(item.value);
};
this.selectList.onCancel = () => {
onCancel();
};
this.addChild(this.selectList);
// Add bottom border
this.addChild(new DynamicBorder());
}
getSelectList(): SelectList {
return this.selectList;
}
}