171 lines
5.7 KiB
TypeScript
171 lines
5.7 KiB
TypeScript
import {
|
||
disconnectLanguage,
|
||
useFileConnections,
|
||
} from "@/store/file-connection";
|
||
import { useProjectSourcesStore } from "@/store/sources-store";
|
||
import { isTauriEnv } from "@/lib/is-tauri";
|
||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||
import { Button } from "../ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type Props = {
|
||
projectId: string;
|
||
};
|
||
|
||
export function HeaderConnectionIndicator({ projectId }: Props) {
|
||
const isTauri = isTauriEnv();
|
||
|
||
if (isTauri) {
|
||
return <TauriConnectionIndicator />;
|
||
}
|
||
|
||
return <BrowserConnectionIndicator projectId={projectId} />;
|
||
}
|
||
|
||
/** Tauri 模式:展示目前链接的所有语言文件地址 */
|
||
function TauriConnectionIndicator() {
|
||
const { languages, baseDir } = useProjectSourcesStore();
|
||
|
||
const hasAny = languages.length > 0;
|
||
const allExist = languages.every((lang) => lang.exists);
|
||
const missingCount = languages.filter((lang) => !lang.exists).length;
|
||
|
||
// 状态颜色:全部存在=绿色,部分缺失=黄色,无文件=红色
|
||
const statusColor = !hasAny
|
||
? "bg-red-500"
|
||
: allExist
|
||
? "bg-green-500"
|
||
: "bg-yellow-500";
|
||
|
||
return (
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
variant="outline"
|
||
title={hasAny ? "已连接文件" : "未配置任何语言文件"}
|
||
>
|
||
<span className={cn("size-2 rounded-full", statusColor)}></span>
|
||
{hasAny
|
||
? `已连接 ${languages.length}${missingCount > 0 ? ` (${missingCount} 缺失)` : ""}`
|
||
: "未配置"}
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent className="p-4 max-w-md" align="end">
|
||
<div className="text-sm font-medium mb-2">连接状态 (Tauri)</div>
|
||
{languages.length === 0 ? (
|
||
<div className="text-sm text-muted-foreground">
|
||
暂无语言文件配置。请通过项目设置配置语言源目录。
|
||
</div>
|
||
) : (
|
||
<div className="space-y-2">
|
||
{baseDir && (
|
||
<div className="text-xs text-muted-foreground mb-2 break-all">
|
||
基础目录:{baseDir}
|
||
</div>
|
||
)}
|
||
{languages.map((lang) => (
|
||
<div
|
||
key={lang.language}
|
||
className="flex items-start justify-between gap-2"
|
||
>
|
||
<div className="min-w-0 flex-1">
|
||
<div className="flex items-center gap-2">
|
||
<span
|
||
className={cn(
|
||
"size-1.5 rounded-full shrink-0",
|
||
lang.exists ? "bg-green-500" : "bg-red-500"
|
||
)}
|
||
/>
|
||
<span className="text-sm font-medium truncate">
|
||
{lang.language}
|
||
</span>
|
||
</div>
|
||
<div
|
||
className="text-xs text-muted-foreground truncate ml-3.5"
|
||
title={lang.path}
|
||
>
|
||
{lang.path}
|
||
</div>
|
||
{!lang.exists && (
|
||
<div className="text-xs text-red-500 ml-3.5">
|
||
文件不存在
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
<div className="text-xs text-muted-foreground pt-4 mt-4 border-t border-border/30">
|
||
Tauri 模式下文件自动连接,无需手动操作。
|
||
</div>
|
||
</div>
|
||
)}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
);
|
||
}
|
||
|
||
/** 浏览器模式:使用 FileSystemFileHandle 管理连接 */
|
||
function BrowserConnectionIndicator({ projectId }: { projectId: string }) {
|
||
const snap = useFileConnections(projectId);
|
||
const list = Object.values(snap.connections);
|
||
const hasAny = list.length > 0;
|
||
|
||
return (
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
variant="outline"
|
||
title={hasAny ? "已连线文件" : "未连线到任何文件"}
|
||
>
|
||
<span
|
||
className={cn(
|
||
"size-2 rounded-full",
|
||
hasAny ? "bg-green-500" : "bg-red-500"
|
||
)}
|
||
></span>
|
||
{hasAny ? `已连线 ${list.length}` : "未连线"}
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent className="p-4" align="end">
|
||
<div className="text-sm font-medium mb-2">连线状态</div>
|
||
{list.length === 0 ? (
|
||
<div className="text-sm text-muted-foreground">
|
||
暂无连线。通过"导入 JSON"选择文件后将建立连线。
|
||
</div>
|
||
) : (
|
||
<div className="space-y-2">
|
||
{list.map((c) => (
|
||
<div
|
||
key={c.language}
|
||
className="flex items-start justify-between gap-2"
|
||
>
|
||
<div className="min-w-0">
|
||
<div className="text-sm font-medium truncate">
|
||
{c.language}
|
||
</div>
|
||
<div
|
||
className="text-xs text-muted-foreground truncate"
|
||
title={c.name}
|
||
>
|
||
{c.name}
|
||
</div>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
className="px-2 h-7 rounded border text-xs hover:bg-accent"
|
||
onClick={() => disconnectLanguage(projectId, c.language)}
|
||
>
|
||
断开
|
||
</button>
|
||
</div>
|
||
))}
|
||
<div className="text-xs text-muted-foreground">
|
||
注:出于隐私,浏览器不提供完整路径,仅显示文件名;刷新页面后连线不会自动恢复。
|
||
</div>
|
||
</div>
|
||
)}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
);
|
||
}
|