import { useEffect, useRef, useState } from "react"; import type { PluginRuntimePhase, PluginRuntimeStatus } from "../../shared/api"; import { PageContent } from "../../shell/layout/PageContent"; import { appStore, useAppStore } from "../../shared/store/appStore"; import { Button } from "../../shared/ui/Button"; import { Modal } from "../../shared/ui/Modal"; import { TitledCard } from "../../shared/ui/TitledCard"; import styles from "./PluginManagementPage.module.scss"; export function PluginManagementPage() { const { pluginRuntime } = useAppStore(); const [progressOpen, setProgressOpen] = useState(false); const [starting, setStarting] = useState(false); const cancelRequested = useRef(false); useEffect(() => { if (!pluginRuntime) void appStore.refreshPluginRuntime(); }, [pluginRuntime]); useEffect(() => { if (pluginRuntime?.state !== "initializing") return; if (!cancelRequested.current) setProgressOpen(true); const timer = window.setInterval(() => void appStore.refreshPluginRuntime(), 300); return () => window.clearInterval(timer); }, [pluginRuntime?.state]); const initialize = async () => { if (starting) return; cancelRequested.current = false; setStarting(true); setProgressOpen(true); const status = await appStore.initializePluginRuntime(); setStarting(false); if (!status) { setProgressOpen(false); } else if (cancelRequested.current && status.state === "initializing") { void appStore.cancelPluginRuntimeInitialization(); } }; const closeProgress = () => { setProgressOpen(false); cancelRequested.current = true; if (pluginRuntime?.state === "initializing") { void appStore.cancelPluginRuntimeInitialization(); } }; const content = pluginRuntime?.state === "ready" ? : void initialize()} />; return <> ; } function RuntimeGate({ status, starting, onInitialize }: { status: PluginRuntimeStatus | null; starting: boolean; onInitialize: () => void }) { const checking = status === null; const initializing = starting || status?.state === "initializing"; const failed = status?.state === "failed"; const unsupported = status?.state === "unsupported"; const title = checking ? t("正在检查插件运行时") : failed ? t("插件运行时初始化失败") : unsupported ? t("当前系统不支持插件运行时") : t("需要先初始化插件运行时"); const description = failed ? t("请重试初始化") : unsupported ? status.error ?? t("当前操作系统或 CPU 架构暂不受支持") : t("初始化将下载并安装插件运行时。"); return
{title} {description} {!unsupported && }
; } function RuntimeReady({ status }: { status: PluginRuntimeStatus }) { return
{t("状态")}{t("已就绪")}
{t("插件运行时版本")}{status.version}
{t("运行平台")}{status.target}
; } function RuntimeProgressModal({ open, status, starting, onClose }: { open: boolean; status: PluginRuntimeStatus | null; starting: boolean; onClose: () => void }) { const initializing = starting || status?.state === "initializing"; const downloaded = status?.downloaded_bytes ?? 0; const total = status?.total_bytes ?? null; const percent = total && total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : null; const stage = status?.state === "ready" ? t("插件运行时初始化完成") : status?.state === "failed" ? t("插件运行时初始化失败") : phaseText(status?.phase ?? null); return
{stage} {status?.phase === "downloading" && <> {total ? t("已下载 {downloaded} / {total}", { downloaded: formatBytes(downloaded), total: formatBytes(total) }) : t("已下载 {downloaded}", { downloaded: formatBytes(downloaded) })} } {status?.state === "failed" && {t("请重试初始化")}} {status?.state === "ready" && {t("插件运行时 {version} 已安装,可以开始使用插件。", { version: status.version })}}
; } function phaseText(phase: PluginRuntimePhase | null) { switch (phase) { case "checking": return t("正在检查插件运行时"); case "downloading": return t("正在下载插件运行时"); case "verifying": return t("正在验证插件运行时下载文件"); case "installing": return t("正在安装插件运行时"); case "validating": return t("正在验证插件运行时"); default: return t("正在准备插件运行时"); } } function formatBytes(bytes: number) { if (bytes < 1024) return `${bytes} B`; const units = ["KB", "MB", "GB"]; let value = bytes / 1024; let unit = 0; while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit += 1; } return `${value < 10 ? value.toFixed(1) : value.toFixed(0)} ${units[unit]}`; }