Merge branch 'main' into various-improvements

This commit is contained in:
Cody Seibert
2025-12-10 14:39:07 -05:00
25 changed files with 4430 additions and 459 deletions

View File

@@ -25,19 +25,61 @@ import {
} from "lucide-react";
import { cn } from "@/lib/utils";
import type { SessionListItem } from "@/types/electron";
import { ACTION_SHORTCUTS } from "@/hooks/use-keyboard-shortcuts";
import { useKeyboardShortcutsConfig } from "@/hooks/use-keyboard-shortcuts";
import { useAppStore } from "@/store/app-store";
// Random session name generator
const adjectives = [
"Swift", "Bright", "Clever", "Dynamic", "Eager", "Focused", "Gentle", "Happy",
"Inventive", "Jolly", "Keen", "Lively", "Mighty", "Noble", "Optimal", "Peaceful",
"Quick", "Radiant", "Smart", "Tranquil", "Unique", "Vibrant", "Wise", "Zealous"
"Swift",
"Bright",
"Clever",
"Dynamic",
"Eager",
"Focused",
"Gentle",
"Happy",
"Inventive",
"Jolly",
"Keen",
"Lively",
"Mighty",
"Noble",
"Optimal",
"Peaceful",
"Quick",
"Radiant",
"Smart",
"Tranquil",
"Unique",
"Vibrant",
"Wise",
"Zealous",
];
const nouns = [
"Agent", "Builder", "Coder", "Developer", "Explorer", "Forge", "Garden", "Helper",
"Innovator", "Journey", "Kernel", "Lighthouse", "Mission", "Navigator", "Oracle",
"Project", "Quest", "Runner", "Spark", "Task", "Unicorn", "Voyage", "Workshop"
"Agent",
"Builder",
"Coder",
"Developer",
"Explorer",
"Forge",
"Garden",
"Helper",
"Innovator",
"Journey",
"Kernel",
"Lighthouse",
"Mission",
"Navigator",
"Oracle",
"Project",
"Quest",
"Runner",
"Spark",
"Task",
"Unicorn",
"Voyage",
"Workshop",
];
function generateRandomSessionName(): string {
@@ -62,13 +104,16 @@ export function SessionManager({
isCurrentSessionThinking = false,
onQuickCreateRef,
}: SessionManagerProps) {
const shortcuts = useKeyboardShortcutsConfig();
const [sessions, setSessions] = useState<SessionListItem[]>([]);
const [activeTab, setActiveTab] = useState<"active" | "archived">("active");
const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
const [editingName, setEditingName] = useState("");
const [isCreating, setIsCreating] = useState(false);
const [newSessionName, setNewSessionName] = useState("");
const [runningSessions, setRunningSessions] = useState<Set<string>>(new Set());
const [runningSessions, setRunningSessions] = useState<Set<string>>(
new Set()
);
// Check running state for all sessions
const checkRunningSessions = async (sessionList: SessionListItem[]) => {
@@ -85,7 +130,10 @@ export function SessionManager({
}
} catch (err) {
// Ignore errors for individual session checks
console.warn(`[SessionManager] Failed to check running state for ${session.id}:`, err);
console.warn(
`[SessionManager] Failed to check running state for ${session.id}:`,
err
);
}
}
@@ -234,7 +282,8 @@ export function SessionManager({
const activeSessions = sessions.filter((s) => !s.isArchived);
const archivedSessions = sessions.filter((s) => s.isArchived);
const displayedSessions = activeTab === "active" ? activeSessions : archivedSessions;
const displayedSessions =
activeTab === "active" ? activeSessions : archivedSessions;
return (
<Card className="h-full flex flex-col">
@@ -246,10 +295,10 @@ export function SessionManager({
variant="default"
size="sm"
onClick={handleQuickCreateSession}
hotkey={ACTION_SHORTCUTS.newSession}
hotkey={shortcuts.newSession}
hotkeyActive={false}
data-testid="new-session-button"
title={`New Session (${ACTION_SHORTCUTS.newSession})`}
title={`New Session (${shortcuts.newSession})`}
>
<Plus className="w-4 h-4 mr-1" />
New
@@ -259,7 +308,9 @@ export function SessionManager({
<Tabs
value={activeTab}
onValueChange={(value) => setActiveTab(value as "active" | "archived")}
onValueChange={(value) =>
setActiveTab(value as "active" | "archived")
}
className="w-full"
>
<TabsList className="w-full">
@@ -275,7 +326,10 @@ export function SessionManager({
</Tabs>
</CardHeader>
<CardContent className="flex-1 overflow-y-auto space-y-2" data-testid="session-list">
<CardContent
className="flex-1 overflow-y-auto space-y-2"
data-testid="session-list"
>
{/* Create new session */}
{isCreating && (
<div className="p-3 border rounded-lg bg-muted/50">
@@ -330,8 +384,7 @@ export function SessionManager({
value={editingName}
onChange={(e) => setEditingName(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter")
handleRenameSession(session.id);
if (e.key === "Enter") handleRenameSession(session.id);
if (e.key === "Escape") {
setEditingSessionId(null);
setEditingName("");
@@ -368,13 +421,17 @@ export function SessionManager({
<>
<div className="flex items-center gap-2 mb-1">
{/* Show loading indicator if this session is running (either current session thinking or any session in runningSessions) */}
{((currentSessionId === session.id && isCurrentSessionThinking) || runningSessions.has(session.id)) ? (
{(currentSessionId === session.id &&
isCurrentSessionThinking) ||
runningSessions.has(session.id) ? (
<Loader2 className="w-4 h-4 text-primary animate-spin shrink-0" />
) : (
<MessageSquare className="w-4 h-4 text-muted-foreground shrink-0" />
)}
<h3 className="font-medium truncate">{session.name}</h3>
{((currentSessionId === session.id && isCurrentSessionThinking) || runningSessions.has(session.id)) && (
{((currentSessionId === session.id &&
isCurrentSessionThinking) ||
runningSessions.has(session.id)) && (
<span className="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full">
thinking...
</span>
@@ -458,7 +515,9 @@ export function SessionManager({
<div className="text-center py-8 text-muted-foreground">
<MessageSquare className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p className="text-sm">
{activeTab === "active" ? "No active sessions" : "No archived sessions"}
{activeTab === "active"
? "No active sessions"
: "No archived sessions"}
</p>
<p className="text-xs">
{activeTab === "active"