Skip to content

Commit 96feb38

Browse files
leonvanzylclaude
andcommitted
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>
1 parent 1925818 commit 96feb38

3 files changed

Lines changed: 81 additions & 78 deletions

File tree

ui/src/App.tsx

Lines changed: 76 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -261,19 +261,19 @@ function App() {
261261
<div className="min-h-screen bg-background">
262262
{/* Header */}
263263
<header className="sticky top-0 z-50 bg-card/80 backdrop-blur-md text-foreground border-b-2 border-border">
264-
<div className="max-w-7xl mx-auto px-4 py-4">
264+
<div className="max-w-7xl mx-auto px-4 py-3">
265265
<TooltipProvider>
266-
<div className="flex items-center justify-between">
266+
{/* Row 1: Branding + Project + Utility icons */}
267+
<div className="flex items-center gap-3">
267268
{/* Logo and Title */}
268-
<div className="flex items-center gap-3">
269+
<div className="flex items-center gap-2 shrink-0">
269270
<img src="/logo.png" alt="AutoForge" className="h-9 w-9 rounded-full" />
270-
<h1 className="font-display text-2xl font-bold tracking-tight uppercase">
271+
<h1 className="font-display text-2xl font-bold tracking-tight uppercase hidden md:block">
271272
AutoForge
272273
</h1>
273274
</div>
274275

275-
{/* Controls */}
276-
<div className="flex items-center gap-4">
276+
{/* Project selector */}
277277
<ProjectSelector
278278
projects={projects ?? []}
279279
selectedProject={selectedProject}
@@ -282,73 +282,31 @@ function App() {
282282
onSpecCreatingChange={setIsSpecCreating}
283283
/>
284284

285-
{selectedProject && (
286-
<>
287-
<AgentControl
288-
projectName={selectedProject}
289-
status={wsState.agentStatus}
290-
defaultConcurrency={selectedProjectData?.default_concurrency}
291-
/>
292-
293-
<DevServerControl
294-
projectName={selectedProject}
295-
status={wsState.devServerStatus}
296-
url={wsState.devServerUrl}
297-
/>
285+
{/* Spacer */}
286+
<div className="flex-1" />
287+
288+
{/* Ollama Mode Indicator */}
289+
{selectedProject && settings?.ollama_mode && (
290+
<div
291+
className="hidden sm:flex items-center gap-1.5 px-2 py-1 bg-card rounded border-2 border-border shadow-sm"
292+
title="Using Ollama local models"
293+
>
294+
<img src="/ollama.png" alt="Ollama" className="w-5 h-5" />
295+
<span className="text-xs font-bold text-foreground">Ollama</span>
296+
</div>
297+
)}
298298

299-
<Tooltip>
300-
<TooltipTrigger asChild>
301-
<Button
302-
onClick={() => setShowSettings(true)}
303-
variant="outline"
304-
size="sm"
305-
aria-label="Open Settings"
306-
>
307-
<Settings size={18} />
308-
</Button>
309-
</TooltipTrigger>
310-
<TooltipContent>Settings (,)</TooltipContent>
311-
</Tooltip>
312-
313-
<Tooltip>
314-
<TooltipTrigger asChild>
315-
<Button
316-
onClick={() => setShowResetModal(true)}
317-
variant="outline"
318-
size="sm"
319-
aria-label="Reset Project"
320-
disabled={wsState.agentStatus === 'running'}
321-
>
322-
<RotateCcw size={18} />
323-
</Button>
324-
</TooltipTrigger>
325-
<TooltipContent>Reset (R)</TooltipContent>
326-
</Tooltip>
327-
328-
{/* Ollama Mode Indicator */}
329-
{settings?.ollama_mode && (
330-
<div
331-
className="flex items-center gap-1.5 px-2 py-1 bg-card rounded border-2 border-border shadow-sm"
332-
title="Using Ollama local models"
333-
>
334-
<img src="/ollama.png" alt="Ollama" className="w-5 h-5" />
335-
<span className="text-xs font-bold text-foreground">Ollama</span>
336-
</div>
337-
)}
338-
339-
{/* GLM Mode Badge */}
340-
{settings?.glm_mode && (
341-
<Badge
342-
className="bg-purple-500 text-white hover:bg-purple-600"
343-
title="Using GLM API"
344-
>
345-
GLM
346-
</Badge>
347-
)}
348-
</>
299+
{/* GLM Mode Badge */}
300+
{selectedProject && settings?.glm_mode && (
301+
<Badge
302+
className="hidden sm:inline-flex bg-purple-500 text-white hover:bg-purple-600"
303+
title="Using GLM API"
304+
>
305+
GLM
306+
</Badge>
349307
)}
350308

351-
{/* Docs link */}
309+
{/* Utility icons - always visible */}
352310
<Tooltip>
353311
<TooltipTrigger asChild>
354312
<Button
@@ -363,14 +321,12 @@ function App() {
363321
<TooltipContent>Docs</TooltipContent>
364322
</Tooltip>
365323

366-
{/* Theme selector */}
367324
<ThemeSelector
368325
themes={themes}
369326
currentTheme={theme}
370327
onThemeChange={setTheme}
371328
/>
372329

373-
{/* Dark mode toggle - always visible */}
374330
<Tooltip>
375331
<TooltipTrigger asChild>
376332
<Button
@@ -384,8 +340,55 @@ function App() {
384340
</TooltipTrigger>
385341
<TooltipContent>Toggle theme</TooltipContent>
386342
</Tooltip>
387-
</div>
388343
</div>
344+
345+
{/* Row 2: Project controls - only when a project is selected */}
346+
{selectedProject && (
347+
<div className="flex items-center gap-3 mt-2 pt-2 border-t border-border/50">
348+
<AgentControl
349+
projectName={selectedProject}
350+
status={wsState.agentStatus}
351+
defaultConcurrency={selectedProjectData?.default_concurrency}
352+
/>
353+
354+
<DevServerControl
355+
projectName={selectedProject}
356+
status={wsState.devServerStatus}
357+
url={wsState.devServerUrl}
358+
/>
359+
360+
<div className="flex-1" />
361+
362+
<Tooltip>
363+
<TooltipTrigger asChild>
364+
<Button
365+
onClick={() => setShowSettings(true)}
366+
variant="outline"
367+
size="sm"
368+
aria-label="Open Settings"
369+
>
370+
<Settings size={18} />
371+
</Button>
372+
</TooltipTrigger>
373+
<TooltipContent>Settings (,)</TooltipContent>
374+
</Tooltip>
375+
376+
<Tooltip>
377+
<TooltipTrigger asChild>
378+
<Button
379+
onClick={() => setShowResetModal(true)}
380+
variant="outline"
381+
size="sm"
382+
aria-label="Reset Project"
383+
disabled={wsState.agentStatus === 'running'}
384+
>
385+
<RotateCcw size={18} />
386+
</Button>
387+
</TooltipTrigger>
388+
<TooltipContent>Reset (R)</TooltipContent>
389+
</Tooltip>
390+
</div>
391+
)}
389392
</TooltipProvider>
390393
</div>
391394
</header>

ui/src/components/AgentControl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function AgentControl({ projectName, status, defaultConcurrency = 3 }: Ag
8181

8282
return (
8383
<>
84-
<div className="flex items-center gap-4">
84+
<div className="flex items-center gap-2 sm:gap-4">
8585
{/* Concurrency slider - visible when stopped */}
8686
{isStopped && (
8787
<div className="flex items-center gap-2">

ui/src/components/ProjectSelector.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,16 @@ export function ProjectSelector({
7373
<DropdownMenuTrigger asChild>
7474
<Button
7575
variant="outline"
76-
className="min-w-[200px] justify-between"
76+
className="min-w-[140px] sm:min-w-[200px] justify-between"
7777
disabled={isLoading}
7878
>
7979
{isLoading ? (
8080
<Loader2 size={18} className="animate-spin" />
8181
) : selectedProject ? (
8282
<>
83-
<span className="flex items-center gap-2">
84-
<FolderOpen size={18} />
85-
{selectedProject}
83+
<span className="flex items-center gap-2 truncate">
84+
<FolderOpen size={18} className="shrink-0" />
85+
<span className="truncate">{selectedProject}</span>
8686
</span>
8787
{selectedProjectData && selectedProjectData.stats.total > 0 && (
8888
<Badge className="ml-2">{selectedProjectData.stats.percentage}%</Badge>

0 commit comments

Comments
 (0)