From e47b34288b733b493de15c66d774c0bb1a906e61 Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 21 Dec 2025 19:43:17 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=82=EF=B8=8F=20refactor:=20implement?= =?UTF-8?q?=20Phase=202=20folder-pattern=20compliance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .../delete-all-archived-sessions-dialog.tsx | 10 +- .../{ => dialogs}/delete-session-dialog.tsx | 14 +- apps/ui/src/components/dialogs/index.ts | 4 + .../{ => dialogs}/new-project-modal.tsx | 191 ++++++-------- .../{ => dialogs}/workspace-picker-modal.tsx | 32 +-- apps/ui/src/components/layout/sidebar.tsx | 2 +- apps/ui/src/components/session-manager.tsx | 240 ++++++++---------- .../components/kanban-card/index.ts | 7 + .../settings-view/api-keys/hooks/index.ts | 1 + .../views/settings-view/api-keys/index.ts | 4 + .../views/settings-view/appearance/index.ts | 1 + .../views/settings-view/audio/index.ts | 1 + .../views/settings-view/cli-status/index.ts | 1 + .../views/settings-view/components/index.ts | 4 + .../views/settings-view/config/index.ts | 2 + .../views/settings-view/danger-zone/index.ts | 1 + .../settings-view/feature-defaults/index.ts | 1 + .../settings-view/keyboard-shortcuts/index.ts | 1 + .../views/settings-view/shared/index.ts | 2 + apps/ui/src/components/views/welcome-view.tsx | 191 ++++++-------- 20 files changed, 304 insertions(+), 406 deletions(-) rename apps/ui/src/components/{ => dialogs}/delete-all-archived-sessions-dialog.tsx (89%) rename apps/ui/src/components/{ => dialogs}/delete-session-dialog.tsx (78%) rename apps/ui/src/components/{ => dialogs}/new-project-modal.tsx (70%) rename apps/ui/src/components/{ => dialogs}/workspace-picker-modal.tsx (85%) create mode 100644 apps/ui/src/components/views/board-view/components/kanban-card/index.ts create mode 100644 apps/ui/src/components/views/settings-view/api-keys/hooks/index.ts create mode 100644 apps/ui/src/components/views/settings-view/api-keys/index.ts create mode 100644 apps/ui/src/components/views/settings-view/appearance/index.ts create mode 100644 apps/ui/src/components/views/settings-view/audio/index.ts create mode 100644 apps/ui/src/components/views/settings-view/cli-status/index.ts create mode 100644 apps/ui/src/components/views/settings-view/components/index.ts create mode 100644 apps/ui/src/components/views/settings-view/config/index.ts create mode 100644 apps/ui/src/components/views/settings-view/danger-zone/index.ts create mode 100644 apps/ui/src/components/views/settings-view/feature-defaults/index.ts create mode 100644 apps/ui/src/components/views/settings-view/keyboard-shortcuts/index.ts create mode 100644 apps/ui/src/components/views/settings-view/shared/index.ts diff --git a/apps/ui/src/components/delete-all-archived-sessions-dialog.tsx b/apps/ui/src/components/dialogs/delete-all-archived-sessions-dialog.tsx similarity index 89% rename from apps/ui/src/components/delete-all-archived-sessions-dialog.tsx rename to apps/ui/src/components/dialogs/delete-all-archived-sessions-dialog.tsx index 66b0bae6..358b99da 100644 --- a/apps/ui/src/components/delete-all-archived-sessions-dialog.tsx +++ b/apps/ui/src/components/dialogs/delete-all-archived-sessions-dialog.tsx @@ -1,4 +1,3 @@ - import { Dialog, DialogContent, @@ -6,9 +5,9 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Trash2 } from "lucide-react"; +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Trash2 } from 'lucide-react'; interface DeleteAllArchivedSessionsDialogProps { open: boolean; @@ -29,8 +28,7 @@ export function DeleteAllArchivedSessionsDialog({ Delete All Archived Sessions - Are you sure you want to delete all archived sessions? This action - cannot be undone. + Are you sure you want to delete all archived sessions? This action cannot be undone. {archivedCount > 0 && ( {archivedCount} session(s) will be deleted. diff --git a/apps/ui/src/components/delete-session-dialog.tsx b/apps/ui/src/components/dialogs/delete-session-dialog.tsx similarity index 78% rename from apps/ui/src/components/delete-session-dialog.tsx rename to apps/ui/src/components/dialogs/delete-session-dialog.tsx index e40cbed8..10862012 100644 --- a/apps/ui/src/components/delete-session-dialog.tsx +++ b/apps/ui/src/components/dialogs/delete-session-dialog.tsx @@ -1,6 +1,6 @@ -import { MessageSquare } from "lucide-react"; -import { DeleteConfirmDialog } from "@/components/ui/delete-confirm-dialog"; -import type { SessionListItem } from "@/types/electron"; +import { MessageSquare } from 'lucide-react'; +import { DeleteConfirmDialog } from '@/components/ui/delete-confirm-dialog'; +import type { SessionListItem } from '@/types/electron'; interface DeleteSessionDialogProps { open: boolean; @@ -38,12 +38,8 @@ export function DeleteSessionDialog({
-

- {session.name} -

-

- {session.messageCount} messages -

+

{session.name}

+

{session.messageCount} messages

)} diff --git a/apps/ui/src/components/dialogs/index.ts b/apps/ui/src/components/dialogs/index.ts index 904c7a21..4cadb26d 100644 --- a/apps/ui/src/components/dialogs/index.ts +++ b/apps/ui/src/components/dialogs/index.ts @@ -1,2 +1,6 @@ export { BoardBackgroundModal } from './board-background-modal'; +export { DeleteAllArchivedSessionsDialog } from './delete-all-archived-sessions-dialog'; +export { DeleteSessionDialog } from './delete-session-dialog'; export { FileBrowserDialog } from './file-browser-dialog'; +export { NewProjectModal } from './new-project-modal'; +export { WorkspacePickerModal } from './workspace-picker-modal'; diff --git a/apps/ui/src/components/new-project-modal.tsx b/apps/ui/src/components/dialogs/new-project-modal.tsx similarity index 70% rename from apps/ui/src/components/new-project-modal.tsx rename to apps/ui/src/components/dialogs/new-project-modal.tsx index 93eef763..042b2ad7 100644 --- a/apps/ui/src/components/new-project-modal.tsx +++ b/apps/ui/src/components/dialogs/new-project-modal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect } from 'react'; import { Dialog, DialogContent, @@ -6,13 +6,13 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { HotkeyButton } from "@/components/ui/hotkey-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Badge } from "@/components/ui/badge"; +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { HotkeyButton } from '@/components/ui/hotkey-button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Badge } from '@/components/ui/badge'; import { FolderPlus, FolderOpen, @@ -22,15 +22,12 @@ import { Loader2, Link, Folder, -} from "lucide-react"; -import { starterTemplates, type StarterTemplate } from "@/lib/templates"; -import { getElectronAPI } from "@/lib/electron"; -import { cn } from "@/lib/utils"; -import { useFileBrowser } from "@/contexts/file-browser-context"; -import { - getDefaultWorkspaceDirectory, - saveLastProjectDirectory, -} from "@/lib/workspace-config"; +} from 'lucide-react'; +import { starterTemplates, type StarterTemplate } from '@/lib/templates'; +import { getElectronAPI } from '@/lib/electron'; +import { cn } from '@/lib/utils'; +import { useFileBrowser } from '@/contexts/file-browser-context'; +import { getDefaultWorkspaceDirectory, saveLastProjectDirectory } from '@/lib/workspace-config'; interface ValidationErrors { projectName?: boolean; @@ -42,20 +39,13 @@ interface ValidationErrors { interface NewProjectModalProps { open: boolean; onOpenChange: (open: boolean) => void; - onCreateBlankProject: ( - projectName: string, - parentDir: string - ) => Promise; + onCreateBlankProject: (projectName: string, parentDir: string) => Promise; onCreateFromTemplate: ( template: StarterTemplate, projectName: string, parentDir: string ) => Promise; - onCreateFromCustomUrl: ( - repoUrl: string, - projectName: string, - parentDir: string - ) => Promise; + onCreateFromCustomUrl: (repoUrl: string, projectName: string, parentDir: string) => Promise; isCreating: boolean; } @@ -67,14 +57,13 @@ export function NewProjectModal({ onCreateFromCustomUrl, isCreating, }: NewProjectModalProps) { - const [activeTab, setActiveTab] = useState<"blank" | "template">("blank"); - const [projectName, setProjectName] = useState(""); - const [workspaceDir, setWorkspaceDir] = useState(""); + const [activeTab, setActiveTab] = useState<'blank' | 'template'>('blank'); + const [projectName, setProjectName] = useState(''); + const [workspaceDir, setWorkspaceDir] = useState(''); const [isLoadingWorkspace, setIsLoadingWorkspace] = useState(false); - const [selectedTemplate, setSelectedTemplate] = - useState(null); + const [selectedTemplate, setSelectedTemplate] = useState(null); const [useCustomUrl, setUseCustomUrl] = useState(false); - const [customUrl, setCustomUrl] = useState(""); + const [customUrl, setCustomUrl] = useState(''); const [errors, setErrors] = useState({}); const { openFileBrowser } = useFileBrowser(); @@ -89,7 +78,7 @@ export function NewProjectModal({ } }) .catch((error) => { - console.error("Failed to get default workspace directory:", error); + console.error('Failed to get default workspace directory:', error); }) .finally(() => { setIsLoadingWorkspace(false); @@ -100,11 +89,11 @@ export function NewProjectModal({ // Reset form when modal closes useEffect(() => { if (!open) { - setProjectName(""); + setProjectName(''); setSelectedTemplate(null); setUseCustomUrl(false); - setCustomUrl(""); - setActiveTab("blank"); + setCustomUrl(''); + setActiveTab('blank'); setErrors({}); } }, [open]); @@ -117,10 +106,7 @@ export function NewProjectModal({ }, [projectName, errors.projectName]); useEffect(() => { - if ( - (selectedTemplate || (useCustomUrl && customUrl)) && - errors.templateSelection - ) { + if ((selectedTemplate || (useCustomUrl && customUrl)) && errors.templateSelection) { setErrors((prev) => ({ ...prev, templateSelection: false })); } }, [selectedTemplate, useCustomUrl, customUrl, errors.templateSelection]); @@ -145,7 +131,7 @@ export function NewProjectModal({ } // Check template selection (only for template tab) - if (activeTab === "template") { + if (activeTab === 'template') { if (useCustomUrl) { if (!customUrl.trim()) { newErrors.customUrl = true; @@ -164,7 +150,7 @@ export function NewProjectModal({ // Clear errors and proceed setErrors({}); - if (activeTab === "blank") { + if (activeTab === 'blank') { await onCreateBlankProject(projectName, workspaceDir); } else if (useCustomUrl && customUrl) { await onCreateFromCustomUrl(customUrl, projectName, workspaceDir); @@ -181,7 +167,7 @@ export function NewProjectModal({ const handleSelectTemplate = (template: StarterTemplate) => { setSelectedTemplate(template); setUseCustomUrl(false); - setCustomUrl(""); + setCustomUrl(''); }; const handleToggleCustomUrl = () => { @@ -193,9 +179,8 @@ export function NewProjectModal({ const handleBrowseDirectory = async () => { const selectedPath = await openFileBrowser({ - title: "Select Base Project Directory", - description: - "Choose the parent directory where your project will be created", + title: 'Select Base Project Directory', + description: 'Choose the parent directory where your project will be created', initialPath: workspaceDir || undefined, }); if (selectedPath) { @@ -211,15 +196,12 @@ export function NewProjectModal({ // Use platform-specific path separator const pathSep = - typeof window !== "undefined" && (window as any).electronAPI - ? navigator.platform.indexOf("Win") !== -1 - ? "\\" - : "/" - : "/"; - const projectPath = - workspaceDir && projectName - ? `${workspaceDir}${pathSep}${projectName}` - : ""; + typeof window !== 'undefined' && (window as any).electronAPI + ? navigator.platform.indexOf('Win') !== -1 + ? '\\' + : '/' + : '/'; + const projectPath = workspaceDir && projectName ? `${workspaceDir}${pathSep}${projectName}` : ''; return ( @@ -228,9 +210,7 @@ export function NewProjectModal({ data-testid="new-project-modal" > - - Create New Project - + Create New Project Start with a blank project or choose from a starter template. @@ -241,13 +221,9 @@ export function NewProjectModal({
setProjectName(e.target.value)} className={cn( - "bg-input text-foreground placeholder:text-muted-foreground", + 'bg-input text-foreground placeholder:text-muted-foreground', errors.projectName - ? "border-red-500 focus:border-red-500 focus:ring-red-500/20" - : "border-border" + ? 'border-red-500 focus:border-red-500 focus:ring-red-500/20' + : 'border-border' )} data-testid="project-name-input" autoFocus /> - {errors.projectName && ( -

Project name is required

- )} + {errors.projectName &&

Project name is required

}
{/* Workspace Directory Display */}
{isLoadingWorkspace ? ( - "Loading workspace..." + 'Loading workspace...' ) : workspaceDir ? ( <> - Will be created at:{" "} + Will be created at:{' '} {projectPath || workspaceDir} @@ -305,7 +279,7 @@ export function NewProjectModal({ setActiveTab(v as "blank" | "template")} + onValueChange={(v) => setActiveTab(v as 'blank' | 'template')} className="flex-1 flex flex-col overflow-hidden" > @@ -323,9 +297,8 @@ export function NewProjectModal({

- Create an empty project with the standard .automaker directory - structure. Perfect for starting from scratch or importing an - existing codebase. + Create an empty project with the standard .automaker directory structure. Perfect + for starting from scratch or importing an existing codebase.

@@ -342,18 +315,18 @@ export function NewProjectModal({ {/* Preset Templates */}
{starterTemplates.map((template) => (
handleSelectTemplate(template)} data-testid={`template-${template.id}`} @@ -361,13 +334,10 @@ export function NewProjectModal({
-

- {template.name} -

- {selectedTemplate?.id === template.id && - !useCustomUrl && ( - - )} +

{template.name}

+ {selectedTemplate?.id === template.id && !useCustomUrl && ( + + )}

{template.description} @@ -376,11 +346,7 @@ export function NewProjectModal({ {/* Tech Stack */}

{template.techStack.slice(0, 6).map((tech) => ( - + {tech} ))} @@ -394,7 +360,7 @@ export function NewProjectModal({ {/* Key Features */}
Features: - {template.features.slice(0, 3).join(" · ")} + {template.features.slice(0, 3).join(' · ')} {template.features.length > 3 && ` · +${template.features.length - 3} more`}
@@ -419,47 +385,38 @@ export function NewProjectModal({ {/* Custom URL Option */}
-

- Custom GitHub URL -

- {useCustomUrl && ( - - )} +

Custom GitHub URL

+ {useCustomUrl && }

Clone any public GitHub repository as a starting point.

{useCustomUrl && ( -
e.stopPropagation()} - className="space-y-1" - > +
e.stopPropagation()} className="space-y-1"> setCustomUrl(e.target.value)} className={cn( - "bg-input text-foreground placeholder:text-muted-foreground", + 'bg-input text-foreground placeholder:text-muted-foreground', errors.customUrl - ? "border-red-500 focus:border-red-500 focus:ring-red-500/20" - : "border-border" + ? 'border-red-500 focus:border-red-500 focus:ring-red-500/20' + : 'border-border' )} data-testid="custom-url-input" /> {errors.customUrl && ( -

- GitHub URL is required -

+

GitHub URL is required

)}
)} @@ -482,14 +439,14 @@ export function NewProjectModal({ onClick={validateAndCreate} disabled={isCreating} className="bg-gradient-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-600 text-white border-0" - hotkey={{ key: "Enter", cmdCtrl: true }} + hotkey={{ key: 'Enter', cmdCtrl: true }} hotkeyActive={open} data-testid="confirm-create-project" > {isCreating ? ( <> - {activeTab === "template" ? "Cloning..." : "Creating..."} + {activeTab === 'template' ? 'Cloning...' : 'Creating...'} ) : ( <>Create Project diff --git a/apps/ui/src/components/workspace-picker-modal.tsx b/apps/ui/src/components/dialogs/workspace-picker-modal.tsx similarity index 85% rename from apps/ui/src/components/workspace-picker-modal.tsx rename to apps/ui/src/components/dialogs/workspace-picker-modal.tsx index 2f3303a2..4f287465 100644 --- a/apps/ui/src/components/workspace-picker-modal.tsx +++ b/apps/ui/src/components/dialogs/workspace-picker-modal.tsx @@ -1,5 +1,4 @@ - -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback } from 'react'; import { Dialog, DialogContent, @@ -7,10 +6,10 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Folder, Loader2, FolderOpen, AlertCircle } from "lucide-react"; -import { getHttpApiClient } from "@/lib/http-api-client"; +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Folder, Loader2, FolderOpen, AlertCircle } from 'lucide-react'; +import { getHttpApiClient } from '@/lib/http-api-client'; interface WorkspaceDirectory { name: string; @@ -23,11 +22,7 @@ interface WorkspacePickerModalProps { onSelect: (path: string, name: string) => void; } -export function WorkspacePickerModal({ - open, - onOpenChange, - onSelect, -}: WorkspacePickerModalProps) { +export function WorkspacePickerModal({ open, onOpenChange, onSelect }: WorkspacePickerModalProps) { const [isLoading, setIsLoading] = useState(false); const [directories, setDirectories] = useState([]); const [error, setError] = useState(null); @@ -43,10 +38,10 @@ export function WorkspacePickerModal({ if (result.success && result.directories) { setDirectories(result.directories); } else { - setError(result.error || "Failed to load directories"); + setError(result.error || 'Failed to load directories'); } } catch (err) { - setError(err instanceof Error ? err.message : "Failed to load directories"); + setError(err instanceof Error ? err.message : 'Failed to load directories'); } finally { setIsLoading(false); } @@ -90,12 +85,7 @@ export function WorkspacePickerModal({

{error}

-
@@ -128,9 +118,7 @@ export function WorkspacePickerModal({

{dir.name}

-

- {dir.path} -

+

{dir.path}

))} diff --git a/apps/ui/src/components/layout/sidebar.tsx b/apps/ui/src/components/layout/sidebar.tsx index cca6aa22..3cafe020 100644 --- a/apps/ui/src/components/layout/sidebar.tsx +++ b/apps/ui/src/components/layout/sidebar.tsx @@ -72,7 +72,7 @@ import { toast } from 'sonner'; import { themeOptions } from '@/config/theme-options'; import type { SpecRegenerationEvent } from '@/types/electron'; import { DeleteProjectDialog } from '@/components/views/settings-view/components/delete-project-dialog'; -import { NewProjectModal } from '@/components/new-project-modal'; +import { NewProjectModal } from '@/components/dialogs/new-project-modal'; import { CreateSpecDialog } from '@/components/views/spec-view/dialogs'; import type { FeatureCount } from '@/components/views/spec-view/types'; import { diff --git a/apps/ui/src/components/session-manager.tsx b/apps/ui/src/components/session-manager.tsx index c255c27f..f8452aa1 100644 --- a/apps/ui/src/components/session-manager.tsx +++ b/apps/ui/src/components/session-manager.tsx @@ -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([]); - const [activeTab, setActiveTab] = useState<"active" | "archived">("active"); + const [activeTab, setActiveTab] = useState<'active' | 'archived'>('active'); const [editingSessionId, setEditingSessionId] = useState(null); - const [editingName, setEditingName] = useState(""); + const [editingName, setEditingName] = useState(''); const [isCreating, setIsCreating] = useState(false); - const [newSessionName, setNewSessionName] = useState(""); - const [runningSessions, setRunningSessions] = useState>( - new Set() - ); + const [newSessionName, setNewSessionName] = useState(''); + const [runningSessions, setRunningSessions] = useState>(new Set()); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); - const [sessionToDelete, setSessionToDelete] = - useState(null); - const [isDeleteAllArchivedDialogOpen, setIsDeleteAllArchivedDialogOpen] = - useState(false); + const [sessionToDelete, setSessionToDelete] = useState(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 ( @@ -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({ - setActiveTab(value as "active" | "archived") - } + onValueChange={(value) => setActiveTab(value as 'active' | 'archived')} className="w-full" > @@ -372,10 +353,7 @@ export function SessionManager({ - + {/* Create new session */} {isCreating && (
@@ -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(''); }} > @@ -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 && (
)} diff --git a/apps/ui/src/components/views/board-view/components/kanban-card/index.ts b/apps/ui/src/components/views/board-view/components/kanban-card/index.ts new file mode 100644 index 00000000..a8b7a36a --- /dev/null +++ b/apps/ui/src/components/views/board-view/components/kanban-card/index.ts @@ -0,0 +1,7 @@ +export { AgentInfoPanel } from './agent-info-panel'; +export { CardActions } from './card-actions'; +export { CardBadges, PriorityBadges } from './card-badges'; +export { CardContentSections } from './card-content-sections'; +export { CardHeaderSection } from './card-header'; +export { KanbanCard } from './kanban-card'; +export { SummaryDialog } from './summary-dialog'; diff --git a/apps/ui/src/components/views/settings-view/api-keys/hooks/index.ts b/apps/ui/src/components/views/settings-view/api-keys/hooks/index.ts new file mode 100644 index 00000000..7b953096 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/api-keys/hooks/index.ts @@ -0,0 +1 @@ +export { useApiKeyManagement } from './use-api-key-management'; diff --git a/apps/ui/src/components/views/settings-view/api-keys/index.ts b/apps/ui/src/components/views/settings-view/api-keys/index.ts new file mode 100644 index 00000000..0a51c82f --- /dev/null +++ b/apps/ui/src/components/views/settings-view/api-keys/index.ts @@ -0,0 +1,4 @@ +export { ApiKeyField } from './api-key-field'; +export { ApiKeysSection } from './api-keys-section'; +export { AuthenticationStatusDisplay } from './authentication-status-display'; +export { SecurityNotice } from './security-notice'; diff --git a/apps/ui/src/components/views/settings-view/appearance/index.ts b/apps/ui/src/components/views/settings-view/appearance/index.ts new file mode 100644 index 00000000..9273561e --- /dev/null +++ b/apps/ui/src/components/views/settings-view/appearance/index.ts @@ -0,0 +1 @@ +export { AppearanceSection } from './appearance-section'; diff --git a/apps/ui/src/components/views/settings-view/audio/index.ts b/apps/ui/src/components/views/settings-view/audio/index.ts new file mode 100644 index 00000000..a6d19cf6 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/audio/index.ts @@ -0,0 +1 @@ +export { AudioSection } from './audio-section'; diff --git a/apps/ui/src/components/views/settings-view/cli-status/index.ts b/apps/ui/src/components/views/settings-view/cli-status/index.ts new file mode 100644 index 00000000..a6d7cf87 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/cli-status/index.ts @@ -0,0 +1 @@ +export { ClaudeCliStatus } from './claude-cli-status'; diff --git a/apps/ui/src/components/views/settings-view/components/index.ts b/apps/ui/src/components/views/settings-view/components/index.ts new file mode 100644 index 00000000..de388fad --- /dev/null +++ b/apps/ui/src/components/views/settings-view/components/index.ts @@ -0,0 +1,4 @@ +export { DeleteProjectDialog } from './delete-project-dialog'; +export { KeyboardMapDialog } from './keyboard-map-dialog'; +export { SettingsHeader } from './settings-header'; +export { SettingsNavigation } from './settings-navigation'; diff --git a/apps/ui/src/components/views/settings-view/config/index.ts b/apps/ui/src/components/views/settings-view/config/index.ts new file mode 100644 index 00000000..9591a88d --- /dev/null +++ b/apps/ui/src/components/views/settings-view/config/index.ts @@ -0,0 +1,2 @@ +export { NAV_ITEMS } from './navigation'; +export type { NavigationItem } from './navigation'; diff --git a/apps/ui/src/components/views/settings-view/danger-zone/index.ts b/apps/ui/src/components/views/settings-view/danger-zone/index.ts new file mode 100644 index 00000000..f4426f7e --- /dev/null +++ b/apps/ui/src/components/views/settings-view/danger-zone/index.ts @@ -0,0 +1 @@ +export { DangerZoneSection } from './danger-zone-section'; diff --git a/apps/ui/src/components/views/settings-view/feature-defaults/index.ts b/apps/ui/src/components/views/settings-view/feature-defaults/index.ts new file mode 100644 index 00000000..95d00123 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/feature-defaults/index.ts @@ -0,0 +1 @@ +export { FeatureDefaultsSection } from './feature-defaults-section'; diff --git a/apps/ui/src/components/views/settings-view/keyboard-shortcuts/index.ts b/apps/ui/src/components/views/settings-view/keyboard-shortcuts/index.ts new file mode 100644 index 00000000..5db1d7b5 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/keyboard-shortcuts/index.ts @@ -0,0 +1 @@ +export { KeyboardShortcutsSection } from './keyboard-shortcuts-section'; diff --git a/apps/ui/src/components/views/settings-view/shared/index.ts b/apps/ui/src/components/views/settings-view/shared/index.ts new file mode 100644 index 00000000..9f0b5505 --- /dev/null +++ b/apps/ui/src/components/views/settings-view/shared/index.ts @@ -0,0 +1,2 @@ +export type { Theme } from '@/config/theme-options'; +export type { CliStatus, KanbanDetailLevel, Project, ApiKeys } from './types'; diff --git a/apps/ui/src/components/views/welcome-view.tsx b/apps/ui/src/components/views/welcome-view.tsx index e9948f29..00c3899a 100644 --- a/apps/ui/src/components/views/welcome-view.tsx +++ b/apps/ui/src/components/views/welcome-view.tsx @@ -1,6 +1,5 @@ - -import { useState, useCallback } from "react"; -import { Button } from "@/components/ui/button"; +import { useState, useCallback } from 'react'; +import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, @@ -8,10 +7,10 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; -import { useAppStore, type ThemeMode } from "@/store/app-store"; -import { getElectronAPI, type Project } from "@/lib/electron"; -import { initializeProject } from "@/lib/project-init"; +} from '@/components/ui/dialog'; +import { useAppStore, type ThemeMode } from '@/store/app-store'; +import { getElectronAPI, type Project } from '@/lib/electron'; +import { initializeProject } from '@/lib/project-init'; import { FolderOpen, Plus, @@ -21,19 +20,19 @@ import { MessageSquare, ChevronDown, Loader2, -} from "lucide-react"; +} from 'lucide-react'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { toast } from "sonner"; -import { WorkspacePickerModal } from "@/components/workspace-picker-modal"; -import { NewProjectModal } from "@/components/new-project-modal"; -import { getHttpApiClient } from "@/lib/http-api-client"; -import type { StarterTemplate } from "@/lib/templates"; -import { useNavigate } from "@tanstack/react-router"; +} from '@/components/ui/dropdown-menu'; +import { toast } from 'sonner'; +import { WorkspacePickerModal } from '@/components/dialogs/workspace-picker-modal'; +import { NewProjectModal } from '@/components/dialogs/new-project-modal'; +import { getHttpApiClient } from '@/lib/http-api-client'; +import type { StarterTemplate } from '@/lib/templates'; +import { useNavigate } from '@tanstack/react-router'; export function WelcomeView() { const { @@ -66,24 +65,24 @@ export function WelcomeView() { const api = getElectronAPI(); if (!api.autoMode?.analyzeProject) { - console.log("[Welcome] Auto mode API not available, skipping analysis"); + console.log('[Welcome] Auto mode API not available, skipping analysis'); return; } setIsAnalyzing(true); try { - console.log("[Welcome] Starting project analysis for:", projectPath); + console.log('[Welcome] Starting project analysis for:', projectPath); const result = await api.autoMode.analyzeProject(projectPath); if (result.success) { - toast.success("Project analyzed", { - description: "AI agent has analyzed your project structure", + toast.success('Project analyzed', { + description: 'AI agent has analyzed your project structure', }); } else { - console.error("[Welcome] Project analysis failed:", result.error); + console.error('[Welcome] Project analysis failed:', result.error); } } catch (error) { - console.error("[Welcome] Failed to analyze project:", error); + console.error('[Welcome] Failed to analyze project:', error); } finally { setIsAnalyzing(false); } @@ -100,8 +99,8 @@ export function WelcomeView() { const initResult = await initializeProject(path); if (!initResult.success) { - toast.error("Failed to initialize project", { - description: initResult.error || "Unknown error occurred", + toast.error('Failed to initialize project', { + description: initResult.error || 'Unknown error occurred', }); return; } @@ -126,26 +125,23 @@ export function WelcomeView() { setShowInitDialog(true); // Kick off agent to analyze the project and update app_spec.txt - console.log( - "[Welcome] Project initialized, created files:", - initResult.createdFiles - ); - console.log("[Welcome] Kicking off project analysis agent..."); + console.log('[Welcome] Project initialized, created files:', initResult.createdFiles); + console.log('[Welcome] Kicking off project analysis agent...'); // Start analysis in background (don't await, let it run async) analyzeProject(path); } else { - toast.success("Project opened", { + toast.success('Project opened', { description: `Opened ${name}`, }); } // Navigate to the board view - navigate({ to: "/board" }); + navigate({ to: '/board' }); } catch (error) { - console.error("[Welcome] Failed to open project:", error); - toast.error("Failed to open project", { - description: error instanceof Error ? error.message : "Unknown error", + console.error('[Welcome] Failed to open project:', error); + toast.error('Failed to open project', { + description: error instanceof Error ? error.message : 'Unknown error', }); } finally { setIsOpening(false); @@ -178,21 +174,19 @@ export function WelcomeView() { if (!result.canceled && result.filePaths[0]) { const path = result.filePaths[0]; // Extract folder name from path (works on both Windows and Mac/Linux) - const name = - path.split(/[/\\]/).filter(Boolean).pop() || "Untitled Project"; + const name = path.split(/[/\\]/).filter(Boolean).pop() || 'Untitled Project'; await initializeAndOpenProject(path, name); } } } catch (error) { - console.error("[Welcome] Failed to check workspace config:", error); + console.error('[Welcome] Failed to check workspace config:', error); // Fall back to current behavior on error const api = getElectronAPI(); const result = await api.openDirectory(); if (!result.canceled && result.filePaths[0]) { const path = result.filePaths[0]; - const name = - path.split(/[/\\]/).filter(Boolean).pop() || "Untitled Project"; + const name = path.split(/[/\\]/).filter(Boolean).pop() || 'Untitled Project'; await initializeAndOpenProject(path, name); } } @@ -224,16 +218,13 @@ export function WelcomeView() { }; const handleInteractiveMode = () => { - navigate({ to: "/interview" }); + navigate({ to: '/interview' }); }; /** * Create a blank project with just .automaker directory structure */ - const handleCreateBlankProject = async ( - projectName: string, - parentDir: string - ) => { + const handleCreateBlankProject = async (projectName: string, parentDir: string) => { setIsCreating(true); try { const api = getElectronAPI(); @@ -242,7 +233,7 @@ export function WelcomeView() { // Validate that parent directory exists const parentExists = await api.exists(parentDir); if (!parentExists) { - toast.error("Parent directory does not exist", { + toast.error('Parent directory does not exist', { description: `Cannot create project in non-existent directory: ${parentDir}`, }); return; @@ -251,7 +242,7 @@ export function WelcomeView() { // Verify parent is actually a directory const parentStat = await api.stat(parentDir); if (parentStat && !parentStat.isDirectory) { - toast.error("Parent path is not a directory", { + toast.error('Parent path is not a directory', { description: `${parentDir} is not a directory`, }); return; @@ -260,8 +251,8 @@ export function WelcomeView() { // Create project directory const mkdirResult = await api.mkdir(projectPath); if (!mkdirResult.success) { - toast.error("Failed to create project directory", { - description: mkdirResult.error || "Unknown error occurred", + toast.error('Failed to create project directory', { + description: mkdirResult.error || 'Unknown error occurred', }); return; } @@ -270,8 +261,8 @@ export function WelcomeView() { const initResult = await initializeProject(projectPath); if (!initResult.success) { - toast.error("Failed to initialize project", { - description: initResult.error || "Unknown error occurred", + toast.error('Failed to initialize project', { + description: initResult.error || 'Unknown error occurred', }); return; } @@ -313,7 +304,7 @@ export function WelcomeView() { setCurrentProject(project); setShowNewProjectModal(false); - toast.success("Project created", { + toast.success('Project created', { description: `Created ${projectName} with .automaker directory`, }); @@ -326,9 +317,9 @@ export function WelcomeView() { }); setShowInitDialog(true); } catch (error) { - console.error("Failed to create project:", error); - toast.error("Failed to create project", { - description: error instanceof Error ? error.message : "Unknown error", + console.error('Failed to create project:', error); + toast.error('Failed to create project', { + description: error instanceof Error ? error.message : 'Unknown error', }); } finally { setIsCreating(false); @@ -356,8 +347,8 @@ export function WelcomeView() { ); if (!cloneResult.success || !cloneResult.projectPath) { - toast.error("Failed to clone template", { - description: cloneResult.error || "Unknown error occurred", + toast.error('Failed to clone template', { + description: cloneResult.error || 'Unknown error occurred', }); return; } @@ -368,8 +359,8 @@ export function WelcomeView() { const initResult = await initializeProject(projectPath); if (!initResult.success) { - toast.error("Failed to initialize project", { - description: initResult.error || "Unknown error occurred", + toast.error('Failed to initialize project', { + description: initResult.error || 'Unknown error occurred', }); return; } @@ -387,15 +378,11 @@ export function WelcomeView() { - ${template.techStack - .map((tech) => `${tech}`) - .join("\n ")} + ${template.techStack.map((tech) => `${tech}`).join('\n ')} - ${template.features - .map((feature) => `${feature}`) - .join("\n ")} + ${template.features.map((feature) => `${feature}`).join('\n ')} @@ -415,7 +402,7 @@ export function WelcomeView() { setCurrentProject(project); setShowNewProjectModal(false); - toast.success("Project created from template", { + toast.success('Project created from template', { description: `Created ${projectName} from ${template.name}`, }); @@ -431,9 +418,9 @@ export function WelcomeView() { // Kick off project analysis analyzeProject(projectPath); } catch (error) { - console.error("Failed to create project from template:", error); - toast.error("Failed to create project", { - description: error instanceof Error ? error.message : "Unknown error", + console.error('Failed to create project from template:', error); + toast.error('Failed to create project', { + description: error instanceof Error ? error.message : 'Unknown error', }); } finally { setIsCreating(false); @@ -454,15 +441,11 @@ export function WelcomeView() { const api = getElectronAPI(); // Clone the repository - const cloneResult = await httpClient.templates.clone( - repoUrl, - projectName, - parentDir - ); + const cloneResult = await httpClient.templates.clone(repoUrl, projectName, parentDir); if (!cloneResult.success || !cloneResult.projectPath) { - toast.error("Failed to clone repository", { - description: cloneResult.error || "Unknown error occurred", + toast.error('Failed to clone repository', { + description: cloneResult.error || 'Unknown error occurred', }); return; } @@ -473,8 +456,8 @@ export function WelcomeView() { const initResult = await initializeProject(projectPath); if (!initResult.success) { - toast.error("Failed to initialize project", { - description: initResult.error || "Unknown error occurred", + toast.error('Failed to initialize project', { + description: initResult.error || 'Unknown error occurred', }); return; } @@ -516,7 +499,7 @@ export function WelcomeView() { setCurrentProject(project); setShowNewProjectModal(false); - toast.success("Project created from repository", { + toast.success('Project created from repository', { description: `Created ${projectName} from ${repoUrl}`, }); @@ -532,9 +515,9 @@ export function WelcomeView() { // Kick off project analysis analyzeProject(projectPath); } catch (error) { - console.error("Failed to create project from custom URL:", error); - toast.error("Failed to create project", { - description: error instanceof Error ? error.message : "Unknown error", + console.error('Failed to create project from custom URL:', error); + toast.error('Failed to create project', { + description: error instanceof Error ? error.message : 'Unknown error', }); } finally { setIsCreating(false); @@ -587,12 +570,9 @@ export function WelcomeView() {
-

- New Project -

+

New Project

- Create a new project from scratch with AI-powered - development + Create a new project from scratch with AI-powered development

@@ -608,10 +588,7 @@ export function WelcomeView() { - + Quick Setup @@ -640,9 +617,7 @@ export function WelcomeView() {
-

- Open Project -

+

Open Project

Open an existing project folder to continue working

@@ -667,9 +642,7 @@ export function WelcomeView() {
-

- Recent Projects -

+

Recent Projects

{recentProjects.map((project, index) => ( @@ -695,9 +668,7 @@ export function WelcomeView() {

{project.lastOpened && (

- {new Date( - project.lastOpened - ).toLocaleDateString()} + {new Date(project.lastOpened).toLocaleDateString()}

)}
@@ -715,9 +686,7 @@ export function WelcomeView() {
-

- No projects yet -

+

No projects yet

Get started by creating a new project or opening an existing one

@@ -747,9 +716,7 @@ export function WelcomeView() {
- {initStatus?.isNewProject - ? "Project Initialized" - : "Project Updated"} + {initStatus?.isNewProject ? 'Project Initialized' : 'Project Updated'} {initStatus?.isNewProject @@ -759,9 +726,7 @@ export function WelcomeView() {
-

- Created files: -

+

Created files:

    {initStatus?.createdFiles.map((file) => (
  • ) : (

    - Tip: Edit the{" "} + Tip: Edit the{' '} app_spec.txt - {" "} - file to describe your project. The AI agent will use this to - understand your project structure. + {' '} + file to describe your project. The AI agent will use this to understand your + project structure.

    )}
@@ -826,9 +791,7 @@ export function WelcomeView() { >
-

- Initializing project... -

+

Initializing project...

)}