mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 02:59:35 +00:00
116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { getFileIcon } from "./FileIcons";
|
|
import { useI18n } from "@/hooks/useI18n";
|
|
|
|
export interface Tab {
|
|
id: string;
|
|
label: string;
|
|
filePath: string;
|
|
sourceSessionId?: string | null;
|
|
initialDisplayMode?: "source" | "preview" | "diff";
|
|
}
|
|
|
|
interface Props {
|
|
tabs: Tab[];
|
|
activeTabId: string;
|
|
onSelectTab: (id: string) => void;
|
|
onCloseTab: (id: string) => void;
|
|
}
|
|
|
|
export function TabBar({ tabs, activeTabId, onSelectTab, onCloseTab }: Props) {
|
|
const { t } = useI18n();
|
|
const [hoveredClose, setHoveredClose] = useState<string | null>(null);
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "flex-end",
|
|
background: "var(--bg-panel)",
|
|
overflowX: "auto",
|
|
flexShrink: 0,
|
|
height: 36,
|
|
}}
|
|
>
|
|
{tabs.map((tab) => {
|
|
const isActive = tab.id === activeTabId;
|
|
return (
|
|
<div
|
|
key={tab.id}
|
|
onClick={() => onSelectTab(tab.id)}
|
|
onMouseDown={(e) => {
|
|
if (e.button === 1) e.preventDefault();
|
|
}}
|
|
onAuxClick={(e) => {
|
|
if (e.button !== 1) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
onCloseTab(tab.id);
|
|
}}
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 6,
|
|
height: 36,
|
|
paddingLeft: 12,
|
|
paddingRight: 6,
|
|
borderRight: "1px solid var(--border)",
|
|
background: isActive ? "var(--bg)" : "var(--bg-panel)",
|
|
cursor: "pointer",
|
|
fontSize: 12,
|
|
color: isActive ? "var(--text)" : "var(--text-muted)",
|
|
whiteSpace: "nowrap",
|
|
maxWidth: 180,
|
|
minWidth: 80,
|
|
flexShrink: 0,
|
|
userSelect: "none",
|
|
transition: "background 0.1s, color 0.1s",
|
|
}}
|
|
>
|
|
<span style={{ flexShrink: 0, opacity: isActive ? 1 : 0.7, display: "flex", alignItems: "center" }}>
|
|
{getFileIcon(tab.label, 13)}
|
|
</span>
|
|
<span
|
|
style={{
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
flex: 1,
|
|
fontWeight: isActive ? 500 : 400,
|
|
}}
|
|
title={tab.filePath}
|
|
>
|
|
{tab.label}
|
|
</span>
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); onCloseTab(tab.id); }}
|
|
onMouseEnter={() => setHoveredClose(tab.id)}
|
|
onMouseLeave={() => setHoveredClose(null)}
|
|
style={{
|
|
display: "flex", alignItems: "center", justifyContent: "center",
|
|
width: 24, height: 24,
|
|
background: hoveredClose === tab.id ? "var(--bg-hover)" : "transparent",
|
|
border: "none",
|
|
borderRadius: 4,
|
|
color: hoveredClose === tab.id ? "var(--text)" : "var(--text-dim)",
|
|
cursor: "pointer",
|
|
padding: 0,
|
|
flexShrink: 0,
|
|
transition: "background 0.1s, color 0.1s",
|
|
}}
|
|
title={t("i18n.close")}
|
|
aria-label={`${t("i18n.close")} ${tab.label}`}
|
|
>
|
|
<svg width="11" height="11" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" aria-hidden="true">
|
|
<line x1="2" y1="2" x2="8" y2="8" />
|
|
<line x1="8" y1="2" x2="2" y2="8" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|