feat: integrate Pi backend and Pi Web

This commit is contained in:
luckyyzh
2026-07-30 19:37:53 +08:00
commit 7392ab9dd7
1390 changed files with 337197 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
"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>
);
}