/** * `ProjectTabs` — the project tab bar rendered below the app header. * * Shows one tab per open project with a close control, and a "+" button to the * right that opens the **Projects** panel (floating) — the single entry point * for creating a project or switching to a known one (#26). Purely * presentational: all state lives in the parent (ProjectsView via useProjects). * * The bar is always present (even with no open project) so the "+" stays * reachable; the empty case shows a muted placeholder next to it. */ import type { TabItem } from "@/shared"; import { Button, Tabs, cn } from "@/shared"; import { useI18n } from "@/i18n"; import type { ProjectActivityStatus } from "./projectActivity"; import { accessibleProjectAccent } from "./projectTheme"; export interface ProjectTabsProps { items: TabItem[]; activeTabId: string | null; onSelect: (id: string) => void; onClose: (id: string) => void; /** Whether the Projects panel is currently open (in any placement). */ projectsPanelOpen: boolean; /** Open the Projects panel (floating) — create/switch project. */ onOpenProjectsPanel: () => void; /** Per-project custom CLI activity, keyed by project id. Missing = none. */ activityByProjectId?: Record; themeColorByProjectId?: Record; className?: string; } export function ProjectTabs({ items, activeTabId, onSelect, onClose, projectsPanelOpen, onOpenProjectsPanel, activityByProjectId = {}, themeColorByProjectId = {}, className, }: ProjectTabsProps) { const { tr } = useI18n(); const tabs = items.map((item) => ({ ...item, themeColor: themeColorByProjectId[item.id] ?? null, themeAccent: themeColorByProjectId[item.id] ? accessibleProjectAccent(themeColorByProjectId[item.id]!) : null, leading: ( ), })); return (
{items.length === 0 ? (

{tr("Aucun onglet ouvert.", "No open tabs.")}

) : ( )} {/* Add-project affordance (#46): always a bordered `secondary` button so it reads as a clickable control in every state — including zero open projects — instead of the near-invisible `ghost` glyph it used to be. When the Projects panel is open it also shows the pressed ring. */}
); } function ProjectActivityDot({ status, label, }: { status: ProjectActivityStatus; label: string; }) { const { tr } = useI18n(); const title = status === "busy" ? tr(`${label} : échange en cours`, `${label}: conversation in progress`) : tr(`${label} : prêt`, `${label}: ready`); return (