feat(ui): refonte menus & fenêtres — menu unique Panneaux et onglets projet (#26)
Sous-menus flyout dans MenuBar ; ProjectsView expose un menu unique Panneaux et supprime les menus View/Window/File ainsi que le sélecteur de projet ; ProjectTabs gère les onglets et le bouton +. Tests migrés en conséquence. QA vert : tsc --noEmit exit 0, vitest 62 fichiers / 616 tests passés, invariants préservés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,9 +1,13 @@
|
||||
/**
|
||||
* `ProjectTabs` — the project tab bar rendered below the app header.
|
||||
*
|
||||
* Shows one tab per open project with a close control, and a "+" button to
|
||||
* return to the launcher. Purely presentational: all state lives in the
|
||||
* parent (ProjectsView via useProjects).
|
||||
* 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";
|
||||
@ -14,9 +18,10 @@ export interface ProjectTabsProps {
|
||||
activeTabId: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
onClose: (id: string) => void;
|
||||
/** Whether the launcher overlay is currently visible (no active tab). */
|
||||
showingLauncher: boolean;
|
||||
onShowLauncher: () => void;
|
||||
/** Whether the Projects panel is currently open (in any placement). */
|
||||
projectsPanelOpen: boolean;
|
||||
/** Open the Projects panel (floating) — create/switch project. */
|
||||
onOpenProjectsPanel: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -25,31 +30,33 @@ export function ProjectTabs({
|
||||
activeTabId,
|
||||
onSelect,
|
||||
onClose,
|
||||
showingLauncher,
|
||||
onShowLauncher,
|
||||
projectsPanelOpen,
|
||||
onOpenProjectsPanel,
|
||||
className,
|
||||
}: ProjectTabsProps) {
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-1 border-b border-border bg-surface px-2 py-1",
|
||||
"flex shrink-0 items-center gap-1 border-b border-border bg-surface px-2 py-1",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Tabs
|
||||
items={items}
|
||||
value={activeTabId}
|
||||
onSelect={onSelect}
|
||||
onClose={onClose}
|
||||
className="flex-1"
|
||||
/>
|
||||
{items.length === 0 ? (
|
||||
<p className="flex-1 px-2 text-sm text-muted">No open tabs.</p>
|
||||
) : (
|
||||
<Tabs
|
||||
items={items}
|
||||
value={activeTabId}
|
||||
onSelect={onSelect}
|
||||
onClose={onClose}
|
||||
className="flex-1"
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
size="sm"
|
||||
variant={showingLauncher ? "secondary" : "ghost"}
|
||||
onClick={onShowLauncher}
|
||||
aria-label="open project launcher"
|
||||
variant={projectsPanelOpen ? "secondary" : "ghost"}
|
||||
onClick={onOpenProjectsPanel}
|
||||
aria-label="open projects panel"
|
||||
className="shrink-0"
|
||||
>
|
||||
+
|
||||
|
||||
Reference in New Issue
Block a user