🗂️ refactor: implement Phase 2 folder-pattern compliance

- Move dialogs to src/components/dialogs/ folder:
  - delete-session-dialog.tsx
  - delete-all-archived-sessions-dialog.tsx
  - new-project-modal.tsx
  - workspace-picker-modal.tsx
- Update all imports to reference new dialog locations
- Create barrel export (index.ts) for board-view/components/kanban-card/
- Create barrel exports (index.ts) for all 11 settings-view subfolders:
  - api-keys/, api-keys/hooks/, appearance/, audio/, cli-status/
  - components/, config/, danger-zone/, feature-defaults/
  - keyboard-shortcuts/, shared/

This is Phase 2 of folder-pattern.md compliance: organizing dialogs
and establishing consistent barrel export patterns across all view subfolders.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-21 19:43:17 +01:00
parent 6365cc137c
commit e47b34288b
20 changed files with 304 additions and 406 deletions

View File

@@ -1,10 +1,9 @@
import { useState, useEffect } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { HotkeyButton } from "@/components/ui/hotkey-button";
import { Input } from "@/components/ui/input";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { HotkeyButton } from '@/components/ui/hotkey-button';
import { Input } from '@/components/ui/input';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
Plus,
MessageSquare,
@@ -15,66 +14,66 @@ import {
X,
ArchiveRestore,
Loader2,
} from "lucide-react";
import { cn } from "@/lib/utils";
import type { SessionListItem } from "@/types/electron";
import { useKeyboardShortcutsConfig } from "@/hooks/use-keyboard-shortcuts";
import { getElectronAPI } from "@/lib/electron";
import { DeleteSessionDialog } from "@/components/delete-session-dialog";
import { DeleteAllArchivedSessionsDialog } from "@/components/delete-all-archived-sessions-dialog";
} from 'lucide-react';
import { cn } from '@/lib/utils';
import type { SessionListItem } from '@/types/electron';
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
import { getElectronAPI } from '@/lib/electron';
import { DeleteSessionDialog } from '@/components/dialogs/delete-session-dialog';
import { DeleteAllArchivedSessionsDialog } from '@/components/dialogs/delete-all-archived-sessions-dialog';
// 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 {
@@ -101,19 +100,15 @@ export function SessionManager({
}: SessionManagerProps) {
const shortcuts = useKeyboardShortcutsConfig();
const [sessions, setSessions] = useState<SessionListItem[]>([]);
const [activeTab, setActiveTab] = useState<"active" | "archived">("active");
const [activeTab, setActiveTab] = useState<'active' | 'archived'>('active');
const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
const [editingName, setEditingName] = useState("");
const [editingName, setEditingName] = useState('');
const [isCreating, setIsCreating] = useState(false);
const [newSessionName, setNewSessionName] = useState("");
const [runningSessions, setRunningSessions] = useState<Set<string>>(
new Set()
);
const [newSessionName, setNewSessionName] = useState('');
const [runningSessions, setRunningSessions] = useState<Set<string>>(new Set());
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [sessionToDelete, setSessionToDelete] =
useState<SessionListItem | null>(null);
const [isDeleteAllArchivedDialogOpen, setIsDeleteAllArchivedDialogOpen] =
useState(false);
const [sessionToDelete, setSessionToDelete] = useState<SessionListItem | null>(null);
const [isDeleteAllArchivedDialogOpen, setIsDeleteAllArchivedDialogOpen] = useState(false);
// Check running state for all sessions
const checkRunningSessions = async (sessionList: SessionListItem[]) => {
@@ -131,10 +126,7 @@ 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);
}
}
@@ -180,14 +172,10 @@ export function SessionManager({
const sessionName = newSessionName.trim() || generateRandomSessionName();
const result = await api.sessions.create(
sessionName,
projectPath,
projectPath
);
const result = await api.sessions.create(sessionName, projectPath, projectPath);
if (result.success && result.session?.id) {
setNewSessionName("");
setNewSessionName('');
setIsCreating(false);
await loadSessions();
onSelectSession(result.session.id);
@@ -201,11 +189,7 @@ export function SessionManager({
const sessionName = generateRandomSessionName();
const result = await api.sessions.create(
sessionName,
projectPath,
projectPath
);
const result = await api.sessions.create(sessionName, projectPath, projectPath);
if (result.success && result.session?.id) {
await loadSessions();
@@ -234,7 +218,7 @@ export function SessionManager({
if (result.success) {
setEditingSessionId(null);
setEditingName("");
setEditingName('');
await loadSessions();
}
};
@@ -243,7 +227,7 @@ export function SessionManager({
const handleArchiveSession = async (sessionId: string) => {
const api = getElectronAPI();
if (!api?.sessions) {
console.error("[SessionManager] Sessions API not available");
console.error('[SessionManager] Sessions API not available');
return;
}
@@ -256,10 +240,10 @@ export function SessionManager({
}
await loadSessions();
} else {
console.error("[SessionManager] Archive failed:", result.error);
console.error('[SessionManager] Archive failed:', result.error);
}
} catch (error) {
console.error("[SessionManager] Archive error:", error);
console.error('[SessionManager] Archive error:', error);
}
};
@@ -267,7 +251,7 @@ export function SessionManager({
const handleUnarchiveSession = async (sessionId: string) => {
const api = getElectronAPI();
if (!api?.sessions) {
console.error("[SessionManager] Sessions API not available");
console.error('[SessionManager] Sessions API not available');
return;
}
@@ -276,10 +260,10 @@ export function SessionManager({
if (result.success) {
await loadSessions();
} else {
console.error("[SessionManager] Unarchive failed:", result.error);
console.error('[SessionManager] Unarchive failed:', result.error);
}
} catch (error) {
console.error("[SessionManager] Unarchive error:", error);
console.error('[SessionManager] Unarchive error:', error);
}
};
@@ -324,8 +308,7 @@ 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 rounded-none">
@@ -337,8 +320,8 @@ export function SessionManager({
size="sm"
onClick={() => {
// Switch to active tab if on archived tab
if (activeTab === "archived") {
setActiveTab("active");
if (activeTab === 'archived') {
setActiveTab('active');
}
handleQuickCreateSession();
}}
@@ -354,9 +337,7 @@ 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">
@@ -372,10 +353,7 @@ 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">
@@ -385,10 +363,10 @@ export function SessionManager({
value={newSessionName}
onChange={(e) => setNewSessionName(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") handleCreateSession();
if (e.key === "Escape") {
if (e.key === 'Enter') handleCreateSession();
if (e.key === 'Escape') {
setIsCreating(false);
setNewSessionName("");
setNewSessionName('');
}
}}
autoFocus
@@ -401,7 +379,7 @@ export function SessionManager({
variant="ghost"
onClick={() => {
setIsCreating(false);
setNewSessionName("");
setNewSessionName('');
}}
>
<X className="w-4 h-4" />
@@ -411,7 +389,7 @@ export function SessionManager({
)}
{/* Delete All Archived button - shown at the top of archived sessions */}
{activeTab === "archived" && archivedSessions.length > 0 && (
{activeTab === 'archived' && archivedSessions.length > 0 && (
<div className="pb-2 border-b mb-2">
<Button
variant="destructive"
@@ -431,9 +409,9 @@ export function SessionManager({
<div
key={session.id}
className={cn(
"p-3 border rounded-lg cursor-pointer transition-colors hover:bg-accent/50",
currentSessionId === session.id && "bg-primary/10 border-primary",
session.isArchived && "opacity-60"
'p-3 border rounded-lg cursor-pointer transition-colors hover:bg-accent/50',
currentSessionId === session.id && 'bg-primary/10 border-primary',
session.isArchived && 'opacity-60'
)}
onClick={() => !session.isArchived && onSelectSession(session.id)}
data-testid={`session-item-${session.id}`}
@@ -446,10 +424,10 @@ export function SessionManager({
value={editingName}
onChange={(e) => setEditingName(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") handleRenameSession(session.id);
if (e.key === "Escape") {
if (e.key === 'Enter') handleRenameSession(session.id);
if (e.key === 'Escape') {
setEditingSessionId(null);
setEditingName("");
setEditingName('');
}
}}
onClick={(e) => e.stopPropagation()}
@@ -472,7 +450,7 @@ export function SessionManager({
onClick={(e) => {
e.stopPropagation();
setEditingSessionId(null);
setEditingName("");
setEditingName('');
}}
className="h-7"
>
@@ -483,16 +461,14 @@ 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) ||
{(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) ||
{((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...
@@ -500,9 +476,7 @@ export function SessionManager({
)}
</div>
{session.preview && (
<p className="text-xs text-muted-foreground truncate">
{session.preview}
</p>
<p className="text-xs text-muted-foreground truncate">{session.preview}</p>
)}
<div className="flex items-center gap-2 mt-2">
<span className="text-xs text-muted-foreground">
@@ -519,10 +493,7 @@ export function SessionManager({
{/* Actions */}
{!session.isArchived && (
<div
className="flex gap-1"
onClick={(e) => e.stopPropagation()}
>
<div className="flex gap-1" onClick={(e) => e.stopPropagation()}>
<Button
size="sm"
variant="ghost"
@@ -547,10 +518,7 @@ export function SessionManager({
)}
{session.isArchived && (
<div
className="flex gap-1"
onClick={(e) => e.stopPropagation()}
>
<div className="flex gap-1" onClick={(e) => e.stopPropagation()}>
<Button
size="sm"
variant="ghost"
@@ -578,14 +546,12 @@ 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"
? "Create your first session to get started"
: "Archive sessions to see them here"}
{activeTab === 'active'
? 'Create your first session to get started'
: 'Archive sessions to see them here'}
</p>
</div>
)}