refactor(sidebar): enhance sidebar responsiveness and improve layout

- Updated sidebar component to include a mobile overlay backdrop when open.
- Adjusted visibility of logo and footer elements based on sidebar state.
- Improved layout and spacing for various components within the sidebar for better usability on different screen sizes.
- Refined styles for buttons and project selectors to enhance visual consistency and responsiveness.
This commit is contained in:
webdevcody
2026-01-11 16:02:25 -05:00
parent 43fc3de2e1
commit 6638c35945
9 changed files with 215 additions and 177 deletions

View File

@@ -257,110 +257,122 @@ export function Sidebar() {
};
return (
<aside
className={cn(
'flex-shrink-0 flex flex-col z-30 relative',
// Glass morphism background with gradient
'bg-gradient-to-b from-sidebar/95 via-sidebar/85 to-sidebar/90 backdrop-blur-2xl',
// Premium border with subtle glow
'border-r border-border/60 shadow-[1px_0_20px_-5px_rgba(0,0,0,0.1)]',
// Smooth width transition
'transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)]',
sidebarOpen ? 'w-16 lg:w-72' : 'w-16'
<>
{/* Mobile overlay backdrop */}
{sidebarOpen && (
<div
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
onClick={toggleSidebar}
aria-hidden="true"
/>
)}
data-testid="sidebar"
>
<CollapseToggleButton
sidebarOpen={sidebarOpen}
toggleSidebar={toggleSidebar}
shortcut={shortcuts.toggleSidebar}
/>
<div className="flex-1 flex flex-col overflow-hidden">
<SidebarHeader sidebarOpen={sidebarOpen} navigate={navigate} />
<ProjectSelectorWithOptions
<aside
className={cn(
'flex-shrink-0 flex flex-col z-50 relative',
// Glass morphism background with gradient
'bg-gradient-to-b from-sidebar/95 via-sidebar/85 to-sidebar/90 backdrop-blur-2xl',
// Premium border with subtle glow
'border-r border-border/60 shadow-[1px_0_20px_-5px_rgba(0,0,0,0.1)]',
// Smooth width transition
'transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)]',
// Mobile: hidden when closed, full width overlay when open
// Desktop: always visible, toggle between narrow and wide
sidebarOpen ? 'fixed lg:relative left-0 top-0 h-full w-72' : 'hidden lg:flex w-16'
)}
data-testid="sidebar"
>
<CollapseToggleButton
sidebarOpen={sidebarOpen}
isProjectPickerOpen={isProjectPickerOpen}
setIsProjectPickerOpen={setIsProjectPickerOpen}
setShowDeleteProjectDialog={setShowDeleteProjectDialog}
toggleSidebar={toggleSidebar}
shortcut={shortcuts.toggleSidebar}
/>
<SidebarNavigation
currentProject={currentProject}
<div className="flex-1 flex flex-col overflow-hidden">
<SidebarHeader sidebarOpen={sidebarOpen} navigate={navigate} />
<ProjectSelectorWithOptions
sidebarOpen={sidebarOpen}
isProjectPickerOpen={isProjectPickerOpen}
setIsProjectPickerOpen={setIsProjectPickerOpen}
setShowDeleteProjectDialog={setShowDeleteProjectDialog}
/>
<SidebarNavigation
currentProject={currentProject}
sidebarOpen={sidebarOpen}
navSections={navSections}
isActiveRoute={isActiveRoute}
navigate={navigate}
/>
</div>
<SidebarFooter
sidebarOpen={sidebarOpen}
navSections={navSections}
isActiveRoute={isActiveRoute}
navigate={navigate}
hideWiki={hideWiki}
hideRunningAgents={hideRunningAgents}
runningAgentsCount={runningAgentsCount}
shortcuts={{ settings: shortcuts.settings }}
/>
<TrashDialog
open={showTrashDialog}
onOpenChange={setShowTrashDialog}
trashedProjects={trashedProjects}
activeTrashId={activeTrashId}
handleRestoreProject={handleRestoreProject}
handleDeleteProjectFromDisk={handleDeleteProjectFromDisk}
deleteTrashedProject={deleteTrashedProject}
handleEmptyTrash={handleEmptyTrash}
isEmptyingTrash={isEmptyingTrash}
/>
</div>
<SidebarFooter
sidebarOpen={sidebarOpen}
isActiveRoute={isActiveRoute}
navigate={navigate}
hideWiki={hideWiki}
hideRunningAgents={hideRunningAgents}
runningAgentsCount={runningAgentsCount}
shortcuts={{ settings: shortcuts.settings }}
/>
<TrashDialog
open={showTrashDialog}
onOpenChange={setShowTrashDialog}
trashedProjects={trashedProjects}
activeTrashId={activeTrashId}
handleRestoreProject={handleRestoreProject}
handleDeleteProjectFromDisk={handleDeleteProjectFromDisk}
deleteTrashedProject={deleteTrashedProject}
handleEmptyTrash={handleEmptyTrash}
isEmptyingTrash={isEmptyingTrash}
/>
{/* New Project Setup Dialog */}
<CreateSpecDialog
open={showSetupDialog}
onOpenChange={setShowSetupDialog}
projectOverview={projectOverview}
onProjectOverviewChange={setProjectOverview}
generateFeatures={generateFeatures}
onGenerateFeaturesChange={setGenerateFeatures}
analyzeProject={analyzeProject}
onAnalyzeProjectChange={setAnalyzeProject}
featureCount={featureCount}
onFeatureCountChange={setFeatureCount}
onCreateSpec={handleCreateInitialSpec}
onSkip={handleSkipSetup}
isCreatingSpec={isCreatingSpec}
showSkipButton={true}
title="Set Up Your Project"
description="We didn't find an app_spec.txt file. Let us help you generate your app_spec.txt to help describe your project for our system. We'll analyze your project's tech stack and create a comprehensive specification."
/>
{/* New Project Setup Dialog */}
<CreateSpecDialog
open={showSetupDialog}
onOpenChange={setShowSetupDialog}
projectOverview={projectOverview}
onProjectOverviewChange={setProjectOverview}
generateFeatures={generateFeatures}
onGenerateFeaturesChange={setGenerateFeatures}
analyzeProject={analyzeProject}
onAnalyzeProjectChange={setAnalyzeProject}
featureCount={featureCount}
onFeatureCountChange={setFeatureCount}
onCreateSpec={handleCreateInitialSpec}
onSkip={handleSkipSetup}
isCreatingSpec={isCreatingSpec}
showSkipButton={true}
title="Set Up Your Project"
description="We didn't find an app_spec.txt file. Let us help you generate your app_spec.txt to help describe your project for our system. We'll analyze your project's tech stack and create a comprehensive specification."
/>
<OnboardingDialog
open={showOnboardingDialog}
onOpenChange={setShowOnboardingDialog}
newProjectName={newProjectName}
onSkip={handleOnboardingSkip}
onGenerateSpec={handleOnboardingGenerateSpec}
/>
<OnboardingDialog
open={showOnboardingDialog}
onOpenChange={setShowOnboardingDialog}
newProjectName={newProjectName}
onSkip={handleOnboardingSkip}
onGenerateSpec={handleOnboardingGenerateSpec}
/>
{/* Delete Project Confirmation Dialog */}
<DeleteProjectDialog
open={showDeleteProjectDialog}
onOpenChange={setShowDeleteProjectDialog}
project={currentProject}
onConfirm={moveProjectToTrash}
/>
{/* Delete Project Confirmation Dialog */}
<DeleteProjectDialog
open={showDeleteProjectDialog}
onOpenChange={setShowDeleteProjectDialog}
project={currentProject}
onConfirm={moveProjectToTrash}
/>
{/* New Project Modal */}
<NewProjectModal
open={showNewProjectModal}
onOpenChange={setShowNewProjectModal}
onCreateBlankProject={handleCreateBlankProject}
onCreateFromTemplate={handleCreateFromTemplate}
onCreateFromCustomUrl={handleCreateFromCustomUrl}
isCreating={isCreatingProject}
/>
</aside>
{/* New Project Modal */}
<NewProjectModal
open={showNewProjectModal}
onOpenChange={setShowNewProjectModal}
onCreateBlankProject={handleCreateBlankProject}
onCreateFromTemplate={handleCreateFromTemplate}
onCreateFromCustomUrl={handleCreateFromCustomUrl}
isCreating={isCreatingProject}
/>
</aside>
</>
);
}

View File

@@ -35,11 +35,11 @@ export function AutomakerLogo({ sidebarOpen, navigate }: AutomakerLogoProps) {
onClick={() => navigate({ to: '/dashboard' })}
data-testid="logo-button"
>
{/* Collapsed logo - shown when sidebar is closed OR on small screens when sidebar is open */}
{/* Collapsed logo - only shown when sidebar is closed */}
<div
className={cn(
'relative flex flex-col items-center justify-center rounded-lg gap-0.5',
sidebarOpen ? 'flex lg:hidden' : 'flex'
sidebarOpen ? 'hidden' : 'flex'
)}
>
<svg
@@ -90,16 +90,16 @@ export function AutomakerLogo({ sidebarOpen, navigate }: AutomakerLogoProps) {
</span>
</div>
{/* Expanded logo - only shown when sidebar is open on large screens */}
{/* Expanded logo - shown when sidebar is open */}
{sidebarOpen && (
<div className="hidden lg:flex flex-col">
<div className="flex flex-col">
<div className="flex items-center gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
role="img"
aria-label="automaker"
className="h-[36.8px] w-[36.8px] group-hover:rotate-12 transition-transform duration-300 ease-out"
className="h-8 w-8 lg:h-[36.8px] lg:w-[36.8px] shrink-0 group-hover:rotate-12 transition-transform duration-300 ease-out"
>
<defs>
<linearGradient
@@ -137,11 +137,11 @@ export function AutomakerLogo({ sidebarOpen, navigate }: AutomakerLogoProps) {
<path d="M164 92 L204 128 L164 164" />
</g>
</svg>
<span className="font-bold text-foreground text-[1.7rem] tracking-tight leading-none translate-y-[-2px]">
<span className="font-bold text-foreground text-xl lg:text-[1.7rem] tracking-tight leading-none translate-y-[-2px]">
automaker<span className="text-brand-500">.</span>
</span>
</div>
<span className="text-[0.625rem] text-muted-foreground leading-none font-medium ml-[38.8px]">
<span className="text-[0.625rem] text-muted-foreground leading-none font-medium ml-9 lg:ml-[38.8px]">
v{appVersion} {versionSuffix}
</span>
</div>

View File

@@ -17,7 +17,9 @@ export function CollapseToggleButton({
<button
onClick={toggleSidebar}
className={cn(
'hidden lg:flex absolute top-[68px] -right-3 z-9999',
// Show on desktop always, show on mobile only when sidebar is open
sidebarOpen ? 'flex' : 'hidden lg:flex',
'absolute top-[68px] -right-3 z-9999',
'group/toggle items-center justify-center w-7 h-7 rounded-full',
// Glass morphism button
'bg-card/95 backdrop-blur-sm border border-border/80',

View File

@@ -117,7 +117,7 @@ export function ProjectSelectorWithOptions({
</div>
<div className="flex items-center gap-1.5">
<span
className="hidden lg:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md bg-muted text-muted-foreground"
className="hidden sm:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md bg-muted text-muted-foreground"
data-testid="project-picker-shortcut"
>
{formatShortcut(shortcuts.projectPicker, true)}
@@ -219,7 +219,7 @@ export function ProjectSelectorWithOptions({
<DropdownMenuTrigger asChild>
<button
className={cn(
'hidden lg:flex items-center justify-center w-[42px] h-[42px] rounded-lg',
'flex items-center justify-center w-[42px] h-[42px] rounded-lg',
'text-muted-foreground hover:text-foreground',
'bg-transparent hover:bg-accent/60',
'border border-border/50 hover:border-border',

View File

@@ -72,7 +72,7 @@ export function SidebarFooter({
<span
className={cn(
'ml-3 font-medium text-sm flex-1 text-left',
sidebarOpen ? 'hidden lg:block' : 'hidden'
sidebarOpen ? 'block' : 'hidden'
)}
>
Wiki
@@ -148,7 +148,7 @@ export function SidebarFooter({
<span
className={cn(
'ml-3 font-medium text-sm flex-1 text-left',
sidebarOpen ? 'hidden lg:block' : 'hidden'
sidebarOpen ? 'block' : 'hidden'
)}
>
Running Agents
@@ -157,7 +157,7 @@ export function SidebarFooter({
{sidebarOpen && runningAgentsCount > 0 && (
<span
className={cn(
'hidden lg:flex items-center justify-center',
'flex items-center justify-center',
'min-w-6 h-6 px-1.5 text-xs font-semibold rounded-full',
'bg-brand-500 text-white shadow-sm',
'animate-in fade-in zoom-in duration-200',
@@ -227,7 +227,7 @@ export function SidebarFooter({
<span
className={cn(
'ml-3 font-medium text-sm flex-1 text-left',
sidebarOpen ? 'hidden lg:block' : 'hidden'
sidebarOpen ? 'block' : 'hidden'
)}
>
Settings
@@ -235,7 +235,7 @@ export function SidebarFooter({
{sidebarOpen && (
<span
className={cn(
'hidden lg:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md transition-all duration-200',
'hidden sm:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md transition-all duration-200',
isActiveRoute('settings')
? 'bg-brand-500/20 text-brand-400'
: 'bg-muted text-muted-foreground group-hover:bg-accent'

View File

@@ -20,9 +20,9 @@ export function SidebarHeader({ sidebarOpen, navigate }: SidebarHeaderProps) {
// Background gradient for depth
'bg-gradient-to-b from-transparent to-background/5',
'flex items-center',
sidebarOpen ? 'px-3 lg:px-5 justify-start' : 'px-3 justify-center',
// Add left padding on macOS to avoid overlapping with traffic light buttons (only when expanded)
isMac && sidebarOpen && 'pt-4 pl-20',
sidebarOpen ? 'px-4 lg:px-5 justify-start' : 'px-3 justify-center',
// Add padding on macOS to avoid overlapping with traffic light buttons
isMac && sidebarOpen && 'pt-4',
// Smaller top padding on macOS when collapsed
isMac && !sidebarOpen && 'pt-4'
)}

View File

@@ -35,7 +35,7 @@ export function SidebarNavigation({
<div key={sectionIdx} className={sectionIdx > 0 && sidebarOpen ? 'mt-6' : ''}>
{/* Section Label */}
{section.label && sidebarOpen && (
<div className="hidden lg:block px-3 mb-2">
<div className="px-3 mb-2">
<span className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-widest">
{section.label}
</span>
@@ -115,7 +115,7 @@ export function SidebarNavigation({
<span
className={cn(
'ml-3 font-medium text-sm flex-1 text-left',
sidebarOpen ? 'hidden lg:block' : 'hidden'
sidebarOpen ? 'block' : 'hidden'
)}
>
{item.label}
@@ -124,7 +124,7 @@ export function SidebarNavigation({
{item.count !== undefined && item.count > 0 && sidebarOpen && (
<span
className={cn(
'hidden lg:flex items-center justify-center',
'flex items-center justify-center',
'min-w-5 h-5 px-1.5 text-[10px] font-bold rounded-full',
'bg-primary text-primary-foreground shadow-sm',
'animate-in fade-in zoom-in duration-200'
@@ -137,7 +137,7 @@ export function SidebarNavigation({
{item.shortcut && sidebarOpen && !item.count && (
<span
className={cn(
'hidden lg:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md transition-all duration-200',
'hidden sm:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md transition-all duration-200',
isActive
? 'bg-brand-500/20 text-brand-400'
: 'bg-muted text-muted-foreground group-hover:bg-accent'

View File

@@ -472,9 +472,9 @@ export function DashboardView() {
aria-hidden="true"
/>
)}
<div className="px-8 py-4 flex items-center justify-between">
<div className="px-4 sm:px-8 py-4 flex items-center justify-between">
<div
className="flex items-center gap-3 cursor-pointer group titlebar-no-drag"
className="flex items-center gap-2 sm:gap-3 cursor-pointer group titlebar-no-drag"
onClick={() => navigate({ to: '/dashboard' })}
>
<svg
@@ -482,7 +482,7 @@ export function DashboardView() {
viewBox="0 0 256 256"
role="img"
aria-label="Automaker Logo"
className="size-10 group-hover:rotate-12 transition-transform duration-300 ease-out"
className="size-8 sm:size-10 group-hover:rotate-12 transition-transform duration-300 ease-out"
>
<defs>
<linearGradient
@@ -521,7 +521,7 @@ export function DashboardView() {
</g>
</svg>
<div className="flex flex-col">
<span className="font-bold text-foreground text-2xl tracking-tight leading-none">
<span className="font-bold text-foreground text-xl sm:text-2xl tracking-tight leading-none">
automaker<span className="text-brand-500">.</span>
</span>
<span className="text-xs text-muted-foreground leading-none font-medium mt-1">
@@ -541,30 +541,32 @@ export function DashboardView() {
</header>
{/* Main content */}
<div className="flex-1 overflow-y-auto p-8">
<div className="flex-1 overflow-y-auto p-4 sm:p-8">
<div className="max-w-6xl mx-auto">
{/* No projects - show getting started */}
{!hasProjects && (
<div className="animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-foreground mb-3">Welcome to Automaker</h2>
<p className="text-lg text-muted-foreground max-w-xl mx-auto">
<div className="text-center mb-8 sm:mb-12">
<h2 className="text-2xl sm:text-3xl font-bold text-foreground mb-3">
Welcome to Automaker
</h2>
<p className="text-base sm:text-lg text-muted-foreground max-w-xl mx-auto px-2">
Your autonomous AI development studio. Get started by creating a new project or
opening an existing one.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-3xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 max-w-3xl mx-auto">
{/* New Project Card */}
<div
className="group relative rounded-xl border border-border bg-card/80 backdrop-blur-sm hover:bg-card hover:border-brand-500/30 hover:shadow-xl hover:shadow-brand-500/5 transition-all duration-300 hover:-translate-y-1"
data-testid="new-project-card"
>
<div className="absolute inset-0 rounded-xl bg-linear-to-br from-brand-500/5 via-transparent to-purple-600/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative p-6 h-full flex flex-col">
<div className="flex items-start gap-4 flex-1">
<div className="w-12 h-12 rounded-xl bg-linear-to-br from-brand-500 to-brand-600 shadow-lg shadow-brand-500/25 flex items-center justify-center group-hover:scale-105 group-hover:shadow-brand-500/40 transition-all duration-300 shrink-0">
<Plus className="w-6 h-6 text-white" />
<div className="relative p-4 sm:p-6 h-full flex flex-col">
<div className="flex items-start gap-3 sm:gap-4 flex-1">
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-linear-to-br from-brand-500 to-brand-600 shadow-lg shadow-brand-500/25 flex items-center justify-center group-hover:scale-105 group-hover:shadow-brand-500/40 transition-all duration-300 shrink-0">
<Plus className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
</div>
<div className="flex-1 min-w-0">
<h3 className="text-lg font-semibold text-foreground mb-1.5">
@@ -578,7 +580,7 @@ export function DashboardView() {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
className="w-full mt-5 bg-linear-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white border-0 shadow-md shadow-brand-500/20 hover:shadow-brand-500/30 transition-all"
className="w-full mt-4 sm:mt-5 bg-linear-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white border-0 shadow-md shadow-brand-500/20 hover:shadow-brand-500/30 transition-all"
data-testid="create-new-project"
>
<Plus className="w-4 h-4 mr-2" />
@@ -613,10 +615,10 @@ export function DashboardView() {
data-testid="open-project-card"
>
<div className="absolute inset-0 rounded-xl bg-linear-to-br from-blue-500/5 via-transparent to-cyan-600/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative p-6 h-full flex flex-col">
<div className="flex items-start gap-4 flex-1">
<div className="w-12 h-12 rounded-xl bg-muted border border-border flex items-center justify-center group-hover:bg-blue-500/10 group-hover:border-blue-500/30 group-hover:scale-105 transition-all duration-300 shrink-0">
<FolderOpen className="w-6 h-6 text-muted-foreground group-hover:text-blue-500 transition-colors duration-300" />
<div className="relative p-4 sm:p-6 h-full flex flex-col">
<div className="flex items-start gap-3 sm:gap-4 flex-1">
<div className="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-muted border border-border flex items-center justify-center group-hover:bg-blue-500/10 group-hover:border-blue-500/30 group-hover:scale-105 transition-all duration-300 shrink-0">
<FolderOpen className="w-5 h-5 sm:w-6 sm:h-6 text-muted-foreground group-hover:text-blue-500 transition-colors duration-300" />
</div>
<div className="flex-1 min-w-0">
<h3 className="text-lg font-semibold text-foreground mb-1.5">
@@ -629,7 +631,7 @@ export function DashboardView() {
</div>
<Button
variant="secondary"
className="w-full mt-5 bg-secondary/80 hover:bg-secondary text-foreground border border-border hover:border-blue-500/30 transition-all"
className="w-full mt-4 sm:mt-5 bg-secondary/80 hover:bg-secondary text-foreground border border-border hover:border-blue-500/30 transition-all"
data-testid="open-existing-project"
>
<FolderOpen className="w-4 h-4 mr-2" />
@@ -643,21 +645,26 @@ export function DashboardView() {
{/* Has projects - show project list */}
{hasProjects && (
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="space-y-6 sm:space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
{/* Quick actions header */}
<div className="flex items-center justify-between">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<h2 className="text-2xl font-bold text-foreground">Your Projects</h2>
<div className="flex gap-2">
<Button variant="outline" onClick={handleOpenProject}>
<FolderOpen className="w-4 h-4 mr-2" />
Open Folder
<Button
variant="outline"
onClick={handleOpenProject}
className="flex-1 sm:flex-none"
>
<FolderOpen className="w-4 h-4 sm:mr-2" />
<span className="hidden sm:inline">Open Folder</span>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="bg-linear-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white">
<Plus className="w-4 h-4 mr-2" />
New Project
<ChevronDown className="w-4 h-4 ml-2" />
<Button className="flex-1 sm:flex-none bg-linear-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-700 text-white">
<Plus className="w-4 h-4 sm:mr-2" />
<span className="hidden sm:inline">New Project</span>
<span className="sm:hidden">New</span>
<ChevronDown className="w-4 h-4 ml-1 sm:ml-2" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
@@ -677,13 +684,15 @@ export function DashboardView() {
{/* Favorites section */}
{favoriteProjects.length > 0 && (
<div>
<div className="flex items-center gap-2.5 mb-4">
<div className="w-8 h-8 rounded-lg bg-yellow-500/10 flex items-center justify-center">
<Star className="w-4 h-4 text-yellow-500 fill-yellow-500" />
<div className="flex items-center gap-2 sm:gap-2.5 mb-3 sm:mb-4">
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-yellow-500/10 flex items-center justify-center">
<Star className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-yellow-500 fill-yellow-500" />
</div>
<h3 className="text-lg font-semibold text-foreground">Favorites</h3>
<h3 className="text-base sm:text-lg font-semibold text-foreground">
Favorites
</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
{favoriteProjects.map((project) => (
<div
key={project.id}
@@ -692,40 +701,40 @@ export function DashboardView() {
data-testid={`project-card-${project.id}`}
>
<div className="absolute inset-0 rounded-xl bg-linear-to-br from-yellow-500/5 to-amber-600/5 opacity-0 group-hover:opacity-100 transition-all duration-300" />
<div className="relative p-4">
<div className="flex items-start gap-3">
<div className="w-10 h-10 rounded-lg bg-yellow-500/10 border border-yellow-500/30 flex items-center justify-center group-hover:bg-yellow-500/20 transition-all duration-300 shrink-0">
<Folder className="w-5 h-5 text-yellow-500" />
<div className="relative p-3 sm:p-4">
<div className="flex items-start gap-2.5 sm:gap-3">
<div className="w-9 h-9 sm:w-10 sm:h-10 rounded-lg bg-yellow-500/10 border border-yellow-500/30 flex items-center justify-center group-hover:bg-yellow-500/20 transition-all duration-300 shrink-0">
<Folder className="w-4 h-4 sm:w-5 sm:h-5 text-yellow-500" />
</div>
<div className="flex-1 min-w-0">
<p className="font-medium text-foreground truncate group-hover:text-yellow-500 transition-colors duration-300">
<p className="text-sm sm:text-base font-medium text-foreground truncate group-hover:text-yellow-500 transition-colors duration-300">
{project.name}
</p>
<p className="text-xs text-muted-foreground/70 truncate mt-1">
<p className="text-xs text-muted-foreground/70 truncate mt-0.5 sm:mt-1">
{project.path}
</p>
{project.lastOpened && (
<p className="text-xs text-muted-foreground mt-1.5">
<p className="text-xs text-muted-foreground mt-1 sm:mt-1.5">
{new Date(project.lastOpened).toLocaleDateString()}
</p>
)}
</div>
<div className="flex items-center gap-1">
<div className="flex items-center gap-0.5 sm:gap-1">
<button
onClick={(e) => handleToggleFavorite(e, project.id)}
className="p-1.5 rounded-lg hover:bg-yellow-500/20 transition-colors"
className="p-1 sm:p-1.5 rounded-lg hover:bg-yellow-500/20 transition-colors"
title="Remove from favorites"
>
<Star className="w-4 h-4 text-yellow-500 fill-yellow-500" />
<Star className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-yellow-500 fill-yellow-500" />
</button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
onClick={(e) => e.stopPropagation()}
className="p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
className="p-1 sm:p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
title="More options"
>
<MoreVertical className="w-4 h-4 text-muted-foreground" />
<MoreVertical className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-muted-foreground" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
@@ -750,13 +759,15 @@ export function DashboardView() {
{/* Recent projects section */}
{recentProjects.length > 0 && (
<div>
<div className="flex items-center gap-2.5 mb-4">
<div className="w-8 h-8 rounded-lg bg-muted/50 flex items-center justify-center">
<Clock className="w-4 h-4 text-muted-foreground" />
<div className="flex items-center gap-2 sm:gap-2.5 mb-3 sm:mb-4">
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-muted/50 flex items-center justify-center">
<Clock className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-muted-foreground" />
</div>
<h3 className="text-lg font-semibold text-foreground">Recent Projects</h3>
<h3 className="text-base sm:text-lg font-semibold text-foreground">
Recent Projects
</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
{recentProjects.map((project) => (
<div
key={project.id}
@@ -765,40 +776,40 @@ export function DashboardView() {
data-testid={`project-card-${project.id}`}
>
<div className="absolute inset-0 rounded-xl bg-linear-to-br from-brand-500/0 to-purple-600/0 group-hover:from-brand-500/5 group-hover:to-purple-600/5 transition-all duration-300" />
<div className="relative p-4">
<div className="flex items-start gap-3">
<div className="w-10 h-10 rounded-lg bg-muted/80 border border-border flex items-center justify-center group-hover:bg-brand-500/10 group-hover:border-brand-500/30 transition-all duration-300 shrink-0">
<Folder className="w-5 h-5 text-muted-foreground group-hover:text-brand-500 transition-colors duration-300" />
<div className="relative p-3 sm:p-4">
<div className="flex items-start gap-2.5 sm:gap-3">
<div className="w-9 h-9 sm:w-10 sm:h-10 rounded-lg bg-muted/80 border border-border flex items-center justify-center group-hover:bg-brand-500/10 group-hover:border-brand-500/30 transition-all duration-300 shrink-0">
<Folder className="w-4 h-4 sm:w-5 sm:h-5 text-muted-foreground group-hover:text-brand-500 transition-colors duration-300" />
</div>
<div className="flex-1 min-w-0">
<p className="font-medium text-foreground truncate group-hover:text-brand-500 transition-colors duration-300">
<p className="text-sm sm:text-base font-medium text-foreground truncate group-hover:text-brand-500 transition-colors duration-300">
{project.name}
</p>
<p className="text-xs text-muted-foreground/70 truncate mt-1">
<p className="text-xs text-muted-foreground/70 truncate mt-0.5 sm:mt-1">
{project.path}
</p>
{project.lastOpened && (
<p className="text-xs text-muted-foreground mt-1.5">
<p className="text-xs text-muted-foreground mt-1 sm:mt-1.5">
{new Date(project.lastOpened).toLocaleDateString()}
</p>
)}
</div>
<div className="flex items-center gap-1">
<div className="flex items-center gap-0.5 sm:gap-1">
<button
onClick={(e) => handleToggleFavorite(e, project.id)}
className="p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
className="p-1 sm:p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
title="Add to favorites"
>
<Star className="w-4 h-4 text-muted-foreground hover:text-yellow-500" />
<Star className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-muted-foreground hover:text-yellow-500" />
</button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
onClick={(e) => e.stopPropagation()}
className="p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
className="p-1 sm:p-1.5 rounded-lg hover:bg-muted transition-colors opacity-0 group-hover:opacity-100"
title="More options"
>
<MoreVertical className="w-4 h-4 text-muted-foreground" />
<MoreVertical className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-muted-foreground" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">