mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
740d6fb2eb
* feat(annotate): add WebTUI agent terminal * feat(annotate): wire WebTUI agent into annotate UI * docs: recap annotate agent terminal work * fix(annotate): harden agent terminal runtime * docs: add annotate agent terminal runtime ADRs * fix(annotate): polish agent terminal integration * fix(ui): preserve comment draft on Ask AI failure * fix(annotate): address terminal review findings * fix(annotate): harden agent terminal runtime fallback
156 lines
5.3 KiB
TypeScript
156 lines
5.3 KiB
TypeScript
/**
|
|
* SidebarTabs — Collapsed tab flags
|
|
*
|
|
* When the sidebar is closed, small vertical tabs protrude from the left edge.
|
|
* Clicking a tab opens the sidebar in that mode.
|
|
*/
|
|
|
|
import React from "react";
|
|
import type { SidebarTab } from "../../hooks/useSidebar";
|
|
import { MessagesIcon } from "../icons/MessagesIcon";
|
|
import { ReviewAgentsIcon } from "../ReviewAgentsIcon";
|
|
|
|
interface SidebarTabsProps {
|
|
activeTab: SidebarTab;
|
|
onToggleTab: (tab: SidebarTab) => void;
|
|
hasDiff: boolean;
|
|
showVersionsTab?: boolean;
|
|
showFilesTab?: boolean;
|
|
showMessagesTab?: boolean;
|
|
showAgentTerminalTab?: boolean;
|
|
isAgentTerminalOpen?: boolean;
|
|
isAgentTerminalRunning?: boolean;
|
|
onToggleAgentTerminal?: () => void;
|
|
hasFileAnnotations?: boolean;
|
|
hasMessageAnnotations?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export const SidebarTabs: React.FC<SidebarTabsProps> = ({
|
|
activeTab,
|
|
onToggleTab,
|
|
hasDiff,
|
|
showVersionsTab,
|
|
showFilesTab,
|
|
showMessagesTab,
|
|
showAgentTerminalTab,
|
|
isAgentTerminalOpen,
|
|
isAgentTerminalRunning,
|
|
onToggleAgentTerminal,
|
|
hasFileAnnotations,
|
|
hasMessageAnnotations,
|
|
className,
|
|
}) => {
|
|
return (
|
|
<div
|
|
data-sidebar-tabs="true"
|
|
className={`flex flex-col gap-1 pt-3 pl-0.5 flex-shrink-0 ${className ?? ""}`}
|
|
>
|
|
{showAgentTerminalTab && onToggleAgentTerminal && (
|
|
<button
|
|
onClick={onToggleAgentTerminal}
|
|
className={`sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm transition-colors ${
|
|
isAgentTerminalOpen || isAgentTerminalRunning
|
|
? "text-primary"
|
|
: "text-muted-foreground hover:text-foreground hover:bg-card"
|
|
}`}
|
|
title={isAgentTerminalRunning ? "Agent running" : "Agent"}
|
|
aria-pressed={isAgentTerminalOpen}
|
|
>
|
|
<ReviewAgentsIcon className="w-3.5 h-3.5" />
|
|
{isAgentTerminalRunning && (
|
|
<span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-primary" />
|
|
)}
|
|
</button>
|
|
)}
|
|
|
|
{/* TOC tab */}
|
|
<button
|
|
onClick={() => onToggleTab("toc")}
|
|
className="sidebar-tab-flag group flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
|
|
title="Table of Contents"
|
|
>
|
|
<svg
|
|
className="w-3.5 h-3.5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M4 6h16M4 10h16M4 14h10M4 18h10"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
{/* Versions tab — only shown when multiple versions exist */}
|
|
{showVersionsTab && (
|
|
<button
|
|
onClick={() => onToggleTab("versions")}
|
|
className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
|
|
title="Plan Versions"
|
|
>
|
|
<svg
|
|
className="w-3.5 h-3.5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
{/* Availability indicator dot */}
|
|
{hasDiff && (
|
|
<span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
|
|
)}
|
|
</button>
|
|
)}
|
|
|
|
{showMessagesTab && (
|
|
<button
|
|
onClick={() => onToggleTab("messages")}
|
|
className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
|
|
title="Pick a different message"
|
|
>
|
|
<MessagesIcon />
|
|
{hasMessageAnnotations && (
|
|
<span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
|
|
)}
|
|
</button>
|
|
)}
|
|
|
|
{/* Files tab */}
|
|
{showFilesTab && (
|
|
<button
|
|
onClick={() => onToggleTab("files")}
|
|
className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
|
|
title="File Browser"
|
|
>
|
|
<svg
|
|
className="w-3.5 h-3.5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
/>
|
|
</svg>
|
|
{hasFileAnnotations && (
|
|
<span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|