ui: restructure header navbar into two-row responsive layout

Redesign the header from a single overflowing row into a clean two-row
layout that prevents content from overlapping the logo and bleeding
outside the navbar on smaller screens.

Row 1: Logo + project selector + spacer + mode badges + utility icons
Row 2: Agent controls + dev server + spacer + settings + reset
(only rendered when a project is selected, with a subtle border divider)

Changes:
- App.tsx: Split header into two logical rows with flex spacers for
  right-alignment; hide title text below md breakpoint; move mode
  badges (Ollama/GLM) to row 1 with sm:hidden for small screens
- ProjectSelector: Responsive min-width (140px mobile, 200px desktop);
  truncate long project names instead of pushing icons off-screen
- AgentControl: Responsive gap (gap-2 mobile, gap-4 desktop)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-02-08 15:41:17 +02:00
parent 1925818d49
commit 96feb38aea
3 changed files with 81 additions and 78 deletions

View File

@@ -261,19 +261,19 @@ function App() {
<div className="min-h-screen bg-background"> <div className="min-h-screen bg-background">
{/* Header */} {/* Header */}
<header className="sticky top-0 z-50 bg-card/80 backdrop-blur-md text-foreground border-b-2 border-border"> <header className="sticky top-0 z-50 bg-card/80 backdrop-blur-md text-foreground border-b-2 border-border">
<div className="max-w-7xl mx-auto px-4 py-4"> <div className="max-w-7xl mx-auto px-4 py-3">
<TooltipProvider> <TooltipProvider>
<div className="flex items-center justify-between"> {/* Row 1: Branding + Project + Utility icons */}
{/* Logo and Title */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* Logo and Title */}
<div className="flex items-center gap-2 shrink-0">
<img src="/logo.png" alt="AutoForge" className="h-9 w-9 rounded-full" /> <img src="/logo.png" alt="AutoForge" className="h-9 w-9 rounded-full" />
<h1 className="font-display text-2xl font-bold tracking-tight uppercase"> <h1 className="font-display text-2xl font-bold tracking-tight uppercase hidden md:block">
AutoForge AutoForge
</h1> </h1>
</div> </div>
{/* Controls */} {/* Project selector */}
<div className="flex items-center gap-4">
<ProjectSelector <ProjectSelector
projects={projects ?? []} projects={projects ?? []}
selectedProject={selectedProject} selectedProject={selectedProject}
@@ -282,8 +282,69 @@ function App() {
onSpecCreatingChange={setIsSpecCreating} onSpecCreatingChange={setIsSpecCreating}
/> />
{/* Spacer */}
<div className="flex-1" />
{/* Ollama Mode Indicator */}
{selectedProject && settings?.ollama_mode && (
<div
className="hidden sm:flex items-center gap-1.5 px-2 py-1 bg-card rounded border-2 border-border shadow-sm"
title="Using Ollama local models"
>
<img src="/ollama.png" alt="Ollama" className="w-5 h-5" />
<span className="text-xs font-bold text-foreground">Ollama</span>
</div>
)}
{/* GLM Mode Badge */}
{selectedProject && settings?.glm_mode && (
<Badge
className="hidden sm:inline-flex bg-purple-500 text-white hover:bg-purple-600"
title="Using GLM API"
>
GLM
</Badge>
)}
{/* Utility icons - always visible */}
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => window.open('https://autoforge.cc', '_blank')}
variant="outline"
size="sm"
aria-label="Open Documentation"
>
<BookOpen size={18} />
</Button>
</TooltipTrigger>
<TooltipContent>Docs</TooltipContent>
</Tooltip>
<ThemeSelector
themes={themes}
currentTheme={theme}
onThemeChange={setTheme}
/>
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={toggleDarkMode}
variant="outline"
size="sm"
aria-label="Toggle dark mode"
>
{darkMode ? <Sun size={18} /> : <Moon size={18} />}
</Button>
</TooltipTrigger>
<TooltipContent>Toggle theme</TooltipContent>
</Tooltip>
</div>
{/* Row 2: Project controls - only when a project is selected */}
{selectedProject && ( {selectedProject && (
<> <div className="flex items-center gap-3 mt-2 pt-2 border-t border-border/50">
<AgentControl <AgentControl
projectName={selectedProject} projectName={selectedProject}
status={wsState.agentStatus} status={wsState.agentStatus}
@@ -296,6 +357,8 @@ function App() {
url={wsState.devServerUrl} url={wsState.devServerUrl}
/> />
<div className="flex-1" />
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
@@ -324,68 +387,8 @@ function App() {
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Reset (R)</TooltipContent> <TooltipContent>Reset (R)</TooltipContent>
</Tooltip> </Tooltip>
{/* Ollama Mode Indicator */}
{settings?.ollama_mode && (
<div
className="flex items-center gap-1.5 px-2 py-1 bg-card rounded border-2 border-border shadow-sm"
title="Using Ollama local models"
>
<img src="/ollama.png" alt="Ollama" className="w-5 h-5" />
<span className="text-xs font-bold text-foreground">Ollama</span>
</div> </div>
)} )}
{/* GLM Mode Badge */}
{settings?.glm_mode && (
<Badge
className="bg-purple-500 text-white hover:bg-purple-600"
title="Using GLM API"
>
GLM
</Badge>
)}
</>
)}
{/* Docs link */}
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => window.open('https://autoforge.cc', '_blank')}
variant="outline"
size="sm"
aria-label="Open Documentation"
>
<BookOpen size={18} />
</Button>
</TooltipTrigger>
<TooltipContent>Docs</TooltipContent>
</Tooltip>
{/* Theme selector */}
<ThemeSelector
themes={themes}
currentTheme={theme}
onThemeChange={setTheme}
/>
{/* Dark mode toggle - always visible */}
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={toggleDarkMode}
variant="outline"
size="sm"
aria-label="Toggle dark mode"
>
{darkMode ? <Sun size={18} /> : <Moon size={18} />}
</Button>
</TooltipTrigger>
<TooltipContent>Toggle theme</TooltipContent>
</Tooltip>
</div>
</div>
</TooltipProvider> </TooltipProvider>
</div> </div>
</header> </header>

View File

@@ -81,7 +81,7 @@ export function AgentControl({ projectName, status, defaultConcurrency = 3 }: Ag
return ( return (
<> <>
<div className="flex items-center gap-4"> <div className="flex items-center gap-2 sm:gap-4">
{/* Concurrency slider - visible when stopped */} {/* Concurrency slider - visible when stopped */}
{isStopped && ( {isStopped && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@@ -73,16 +73,16 @@ export function ProjectSelector({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
variant="outline" variant="outline"
className="min-w-[200px] justify-between" className="min-w-[140px] sm:min-w-[200px] justify-between"
disabled={isLoading} disabled={isLoading}
> >
{isLoading ? ( {isLoading ? (
<Loader2 size={18} className="animate-spin" /> <Loader2 size={18} className="animate-spin" />
) : selectedProject ? ( ) : selectedProject ? (
<> <>
<span className="flex items-center gap-2"> <span className="flex items-center gap-2 truncate">
<FolderOpen size={18} /> <FolderOpen size={18} className="shrink-0" />
{selectedProject} <span className="truncate">{selectedProject}</span>
</span> </span>
{selectedProjectData && selectedProjectData.stats.total > 0 && ( {selectedProjectData && selectedProjectData.stats.total > 0 && (
<Badge className="ml-2">{selectedProjectData.stats.percentage}%</Badge> <Badge className="ml-2">{selectedProjectData.stats.percentage}%</Badge>