fix(ui): layouts responsive pour panneau agents et bandeau d'onglets sidebar

AgentsPanel : formulaire de création et items d'agent passent en colonne,
les libellés/états longs (running in IdeA, profil) wrappent/tronquent au lieu
de déborder. ProjectsView : les onglets de la sidebar wrappent sur plusieurs
lignes pour rester tous visibles, le bouton collapse reste épinglé en haut.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 09:39:03 +02:00
parent 40bce5c8bf
commit 31b636037d
2 changed files with 35 additions and 29 deletions

View File

@ -206,9 +206,9 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
{/* ── Create form ── */} {/* ── Create form ── */}
<form <form
onSubmit={(e) => void handleCreate(e)} onSubmit={(e) => void handleCreate(e)}
className="flex flex-wrap items-end gap-2 border-b border-border p-4" className="flex flex-col gap-3 border-b border-border p-4"
> >
<div className="flex min-w-0 flex-1 flex-col gap-1"> <div className="flex min-w-0 flex-col gap-1">
<label <label
htmlFor="agent-name-input" htmlFor="agent-name-input"
className="text-xs font-medium text-muted" className="text-xs font-medium text-muted"
@ -221,12 +221,12 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
placeholder="My agent" placeholder="My agent"
value={newName} value={newName}
onChange={(e) => setNewName(e.target.value)} onChange={(e) => setNewName(e.target.value)}
className="min-w-32" className="w-full"
/> />
</div> </div>
{/* Template selector */} {/* Template selector */}
<div className="flex min-w-0 flex-1 flex-col gap-1"> <div className="flex min-w-0 flex-col gap-1">
<label <label
htmlFor="agent-template-select" htmlFor="agent-template-select"
className="text-xs font-medium text-muted" className="text-xs font-medium text-muted"
@ -256,7 +256,7 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
{/* Profile selector — hidden when a template is chosen */} {/* Profile selector — hidden when a template is chosen */}
{!hasTemplate && ( {!hasTemplate && (
<div className="flex min-w-0 flex-1 flex-col gap-1"> <div className="flex min-w-0 flex-col gap-1">
<label <label
htmlFor="agent-profile-select" htmlFor="agent-profile-select"
className="text-xs font-medium text-muted" className="text-xs font-medium text-muted"
@ -304,6 +304,7 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
aria-label="create agent" aria-label="create agent"
disabled={!canCreate} disabled={!canCreate}
loading={vm.busy} loading={vm.busy}
className="self-end"
> >
Create Create
</Button> </Button>
@ -330,17 +331,17 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
<li <li
key={a.id} key={a.id}
className={cn( className={cn(
"flex items-center justify-between gap-3 py-2 first:pt-0 last:pb-0", "flex flex-col gap-2 py-3 first:pt-0 last:pb-0",
isSelected && "rounded-md bg-raised px-2", isSelected && "rounded-md bg-raised px-2",
)} )}
> >
<button <button
type="button" type="button"
onClick={() => void vm.selectAgent(a.id)} onClick={() => void vm.selectAgent(a.id)}
className="flex min-w-0 flex-1 flex-col items-start gap-0.5 text-left" className="flex min-w-0 flex-col items-start gap-0.5 text-left"
aria-pressed={isSelected} aria-pressed={isSelected}
> >
<span className="flex items-center gap-2"> <span className="flex min-w-0 max-w-full flex-wrap items-center gap-2">
<span className="font-medium text-content">{a.name}</span> <span className="font-medium text-content">{a.name}</span>
{agentDrift && ( {agentDrift && (
<span <span
@ -366,13 +367,13 @@ export function AgentsPanel({ projectId, projectRoot = "" }: AgentsPanelProps) {
</span> </span>
<span className="text-xs text-muted">{profileName}</span> <span className="text-xs text-muted">{profileName}</span>
{live && ( {live && (
<span className="text-xs text-primary"> <span className="block w-full min-w-0 truncate text-xs text-primary">
running in IdeA · {live.sessionId ?? live.nodeId} running in IdeA · {live.sessionId ?? live.nodeId}
</span> </span>
)} )}
</button> </button>
<div className="flex items-center gap-1.5 shrink-0"> <div className="flex flex-wrap items-center gap-1.5">
{/* Profile hot-swap selector (Chantier A). Changing the {/* Profile hot-swap selector (Chantier A). Changing the
engine abandons the conversation history → confirmation. */} engine abandons the conversation history → confirmation. */}
{vm.profiles.length > 0 && ( {vm.profiles.length > 0 && (

View File

@ -147,8 +147,12 @@ export function ProjectsView() {
sidebarCollapsed && "hidden", sidebarCollapsed && "hidden",
)} )}
> >
{/* Sidebar tab strip (no role="tablist" to avoid collision with project tabs) */} {/* Sidebar tab strip (no role="tablist" to avoid collision with project tabs).
<div className="flex shrink-0 items-stretch border-b border-border"> The tabs wrap onto multiple rows when they exceed the sidebar width
so every label stays visible without scrolling; the collapse button
stays pinned to the top-right. */}
<div className="flex shrink-0 items-start border-b border-border">
<div className="flex min-w-0 flex-1 flex-wrap items-stretch">
{SIDEBAR_TABS.map((t) => ( {SIDEBAR_TABS.map((t) => (
<button <button
key={t.id} key={t.id}
@ -156,7 +160,7 @@ export function ProjectsView() {
aria-selected={sidebarTab === t.id} aria-selected={sidebarTab === t.id}
onClick={() => setSidebarTab(t.id)} onClick={() => setSidebarTab(t.id)}
className={cn( className={cn(
"flex-1 px-2 py-2 text-xs font-medium transition-colors", "shrink-0 whitespace-nowrap px-3 py-2 text-xs font-medium transition-colors",
sidebarTab === t.id sidebarTab === t.id
? "border-b-2 border-primary text-content" ? "border-b-2 border-primary text-content"
: "text-muted hover:text-content", : "text-muted hover:text-content",
@ -165,12 +169,13 @@ export function ProjectsView() {
{t.label} {t.label}
</button> </button>
))} ))}
</div>
<button <button
type="button" type="button"
aria-label="collapse sidebar" aria-label="collapse sidebar"
title="Collapse sidebar" title="Collapse sidebar"
onClick={() => setSidebarCollapsed(true)} onClick={() => setSidebarCollapsed(true)}
className="shrink-0 px-2 text-muted hover:text-content" className="shrink-0 self-stretch border-l border-border px-2 text-muted hover:text-content"
> >
« «
</button> </button>