import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
BarChart3,
Bot,
Database,
Folder,
PanelLeftOpen,
Radio,
Sparkles,
type LucideIcon,
} from 'lucide-react';
import type { AppTab, Project, ProjectDiscoveryPlansResponse, ProjectSession } from '../../types/app';
import MainContent from '../main-content/view/MainContent';
import type { MainContentProps } from '../main-content/types/types';
import { cn } from '../../lib/utils.js';
import { projectDisplayName, sessionDisplayTitle, useCustomNamesVersion } from '../../lib/customNames';
import { api } from '../../utils/api';
type Tab = { id: AppTab; labelKey: string; icon: LucideIcon };
const TABS: Tab[] = [
{ id: 'chat', labelKey: 'tabs.chat', icon: Bot },
{ id: 'files', labelKey: 'tabs.files', icon: Folder },
{ id: 'skills', labelKey: 'tabs.skills', icon: Sparkles },
{ id: 'dashboard', labelKey: 'tabs.dashboard', icon: BarChart3 },
{ id: 'memory', labelKey: 'tabs.memory', icon: Database },
{ id: 'always-on', labelKey: 'tabs.alwaysOn', icon: Radio },
];
const ALWAYS_ON_READY_PLAN_POLL_INTERVAL_MS = 15_000;
type MainAreaV2Props = MainContentProps & {
selectedProject: Project | null;
selectedSession: ProjectSession | null;
activeTab: AppTab;
isSidebarCollapsed?: boolean;
onOpenSidebar?: () => void;
};
export default function MainAreaV2(props: MainAreaV2Props) {
const { t } = useTranslation();
const {
selectedProject,
selectedSession,
activeTab,
setActiveTab,
isSidebarCollapsed,
onOpenSidebar,
} = props;
const projectName = selectedProject?.name ?? null;
const [latestReadyPlanMarker, setLatestReadyPlanMarker] = useState<string | null>(null);
const [lastViewedReadyPlanMarker, setLastViewedReadyPlanMarker] = useState<string | null>(null);
useEffect(() => {
if (activeTab === 'home') {
setActiveTab('chat');
}
}, [activeTab, setActiveTab]);
useEffect(() => {
if (!projectName) {
setLatestReadyPlanMarker(null);
setLastViewedReadyPlanMarker(null);
return undefined;
}
let cancelled = false;
const refreshReadyPlanMarker = async () => {
try {
const response = await api.projectDiscoveryPlans(projectName);
if (!response.ok) {
return;
}
const payload = (await response.json()) as ProjectDiscoveryPlansResponse;
const latestReadyPlan = Array.isArray(payload.plans)
? payload.plans
.filter((plan) => plan.status === 'ready')
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0]
: null;
if (!cancelled) {
setLatestReadyPlanMarker(
latestReadyPlan ? `${latestReadyPlan.updatedAt}:${latestReadyPlan.id}` : null,
);
}
} catch {
}
};
void refreshReadyPlanMarker();
const timer = window.setInterval(() => {
void refreshReadyPlanMarker();
}, ALWAYS_ON_READY_PLAN_POLL_INTERVAL_MS);
return () => {
cancelled = true;
window.clearInterval(timer);
};
}, [projectName]);
useEffect(() => {
if (activeTab === 'always-on' && latestReadyPlanMarker) {
setLastViewedReadyPlanMarker(latestReadyPlanMarker);
}
}, [activeTab, latestReadyPlanMarker]);
useCustomNamesVersion();
const displayActiveTab = activeTab === 'home' ? 'chat' : activeTab;
const tabLabelKey = TABS.find((tab) => tab.id === displayActiveTab)?.labelKey;
const tabLabel = tabLabelKey
? t(tabLabelKey)
: displayActiveTab.startsWith('plugin:')
? displayActiveTab.replace('plugin:', '')
: displayActiveTab;
const sessionSummary = selectedSession ? sessionDisplayTitle(selectedSession) : '';
const alwaysOnUnread = Boolean(
latestReadyPlanMarker &&
activeTab !== 'always-on' &&
latestReadyPlanMarker !== lastViewedReadyPlanMarker,
);
return (
<div className="flex h-full min-w-0 flex-col bg-white text-neutral-900 dark:bg-neutral-950 dark:text-neutral-100">
{/* Header: breadcrumb left, tool switcher right. */}
<header className="flex h-12 shrink-0 items-center px-6">
{isSidebarCollapsed ? (
// Just the "expand sidebar" affordance — the PilotDeck logo lives
// in the sidebar header, so showing a duplicate badge here when
// the sidebar is collapsed feels redundant.
<button
type="button"
onClick={onOpenSidebar}
aria-label={t('sidebar:tooltips.showSidebar', { defaultValue: 'Show sidebar' }) as string}
title={t('sidebar:tooltips.showSidebar', { defaultValue: 'Show sidebar' }) as string}
className="mr-4 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-100"
>
<PanelLeftOpen className="h-4 w-4" strokeWidth={1.75} />
</button>
) : null}
<div className="flex min-w-0 flex-1 items-center gap-2 text-[13px]">
<span className="shrink-0 text-neutral-500 dark:text-neutral-400">
{selectedProject ? projectDisplayName(selectedProject) : t('home', { defaultValue: 'Home' })}
</span>
<span className="shrink-0 text-neutral-400/60 dark:text-neutral-500/60">/</span>
<span className="shrink-0 font-medium">{tabLabel}</span>
{sessionSummary ? (
<span
className="ml-2 min-w-0 max-w-[28rem] truncate font-mono text-[11px] text-neutral-500 dark:text-neutral-400"
title={sessionSummary}
>
{sessionSummary}
</span>
) : null}
</div>
<div
role="tablist"
aria-label="Tools"
className="scrollbar-thin ml-4 flex h-9 max-w-[70%] shrink-0 items-center gap-1 overflow-x-auto"
>
{TABS.map((tab) => {
const Icon = tab.icon;
const isActive = displayActiveTab === tab.id;
return (
<button
key={tab.id}
type="button"
role="tab"
aria-selected={isActive}
onClick={() => setActiveTab(tab.id)}
className={cn(
'relative inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md px-2.5 text-[13px] transition-colors',
isActive
? 'bg-neutral-100 font-medium text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100'
: 'text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-100',
)}
>
<Icon className="h-3.5 w-3.5" strokeWidth={1.75} />
<span>{t(tab.labelKey)}</span>
{tab.id === 'always-on' && alwaysOnUnread ? (
<span
aria-hidden="true"
className="absolute right-1 top-1 h-2 w-2 rounded-full bg-blue-500 ring-2 ring-white dark:ring-neutral-950"
/>
) : null}
</button>
);
})}
</div>
</header>
{/* Body */}
<div className="min-h-0 flex-1 overflow-hidden">
<MainContent {...props} />
</div>
</div>
);
}