mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
- 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")
109 lines
2.9 KiB
TypeScript
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;
|
|
}
|
|
}
|