@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
108 lines • 4.96 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { Box, Text, useInput } from 'ink';
import SelectInput from 'ink-select-input';
import { useEffect, useState } from 'react';
import { useResponsiveTerminal } from '../hooks/useTerminalWidth.js';
import { sessionManager } from '../session/session-manager.js';
export function formatTimeAgo(dateString) {
const date = new Date(dateString);
const now = new Date();
const diffInMs = now.getTime() - date.getTime();
const diffInMinutes = diffInMs / (1000 * 60);
const diffInHours = diffInMinutes / 60;
const diffInDays = diffInHours / 24;
if (diffInMinutes < 5) {
return 'just now';
}
else if (diffInMinutes < 60) {
const minutes = Math.floor(diffInMinutes);
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
}
else if (diffInHours < 24) {
const hours = Math.floor(diffInHours);
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
}
else if (diffInDays < 7) {
const days = Math.floor(diffInDays);
return `${days} day${days > 1 ? 's' : ''} ago`;
}
else {
const weeks = Math.floor(diffInDays / 7);
return `${weeks} week${weeks > 1 ? 's' : ''} ago`;
}
}
export function formatMessageCount(count) {
return `${count} message${count !== 1 ? 's' : ''}`;
}
const SessionSelector = ({ onSelect, onCancel, showAll, }) => {
const [sessions, setSessions] = useState([]);
const [loading, setLoading] = useState(true);
const [hasOtherSessions, setHasOtherSessions] = useState(false);
const { actualWidth, truncate } = useResponsiveTerminal();
useEffect(() => {
const loadSessions = async () => {
try {
const filter = showAll ? undefined : { workingDirectory: process.cwd() };
const sessionList = await sessionManager.listSessions(filter);
// Sort by lastAccessedAt descending (most recent first)
const sortedSessions = sessionList.sort((a, b) => new Date(b.lastAccessedAt).getTime() -
new Date(a.lastAccessedAt).getTime());
setSessions(sortedSessions);
// Check if there are sessions in other projects
if (!showAll && sortedSessions.length === 0) {
const allSessions = await sessionManager.listSessions();
setHasOtherSessions(allSessions.length > 0);
}
}
catch (error) {
console.error('Failed to load sessions:', error);
}
finally {
setLoading(false);
}
};
loadSessions();
}, [showAll]);
useInput((_input, key) => {
if (key.escape) {
if (!loading) {
onCancel();
}
return;
}
if (!loading && sessions.length === 0) {
onCancel();
}
});
if (loading) {
return (_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsx(Text, { children: "Loading sessions..." }) }));
}
if (sessions.length === 0) {
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [hasOtherSessions ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "No sessions for this project." }), _jsx(Text, { dimColor: true, children: "Use /resume --all to see all sessions." })] })) : (_jsx(Text, { children: "No saved sessions found." })), _jsx(Text, { dimColor: true, children: "Press any key to continue..." })] }));
}
const items = sessions.map((session, index) => {
const prefix = `[${index + 1}] `;
const suffix = ` (${formatMessageCount(session.messageCount)}) - ${formatTimeAgo(session.lastAccessedAt)}`;
// 4 accounts for the `> ` selector indicator + margin
const maxTitleLength = actualWidth - prefix.length - suffix.length - 4;
const truncatedTitle = maxTitleLength > 10
? truncate(session.title, maxTitleLength)
: session.title;
return {
label: `${prefix}${truncatedTitle}${suffix}`,
value: session.id,
};
});
const handleSelect = (item) => {
const selectedSession = sessions.find(s => s.id === item.value);
if (selectedSession) {
onSelect(selectedSession);
}
else {
onCancel();
}
};
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, children: "Recent Sessions:" }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: items, onSelect: handleSelect, limit: Math.min(items.length, 10) }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to navigate \u2022 Enter to select \u2022 Esc to cancel" }) })] }));
};
export default SessionSelector;
//# sourceMappingURL=session-selector.js.map