Complete platform management workflows
This commit is contained in:
@@ -3,10 +3,10 @@ import { type ChangeEvent, useCallback, useEffect, useMemo, useState } from "rea
|
||||
|
||||
import { platformApiClient } from "../api/client";
|
||||
import type { GamePluginStatus, MarketplacePluginFilterRequest, MarketplacePluginResponse, MarketplacePluginStateAction } from "../api/types";
|
||||
import { ManagementDialog } from "../components/OperationControls";
|
||||
import { EmptyState, ErrorState, LoadingState, ResultBadge } from "../components/StateViews";
|
||||
import { PageFrame } from "../components/PageFrame";
|
||||
import type { PageComponentProps } from "../contracts/page";
|
||||
import { pluginCatalog } from "../contracts/shell";
|
||||
import { cx } from "../utils/classes";
|
||||
|
||||
type ListState = "loading" | "ready" | "error";
|
||||
@@ -59,28 +59,28 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
serverType: serverType.trim() || undefined,
|
||||
capability: capability.trim() || undefined
|
||||
};
|
||||
if (initialState?.plugins) {
|
||||
setListState(initialState.listState ?? "ready");
|
||||
setListError(initialState.listError ?? "");
|
||||
setPlugins(initialState.plugins);
|
||||
setSelectedId(initialState.selectedId ?? "");
|
||||
setDetail(initialState.detail ?? null);
|
||||
setUsingFallback(initialState.usingFallback ?? false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await platformApiClient.listMarketplacePlugins(filter);
|
||||
setPlugins(response.items);
|
||||
setSelectedId((current) => (current && response.items.some((plugin) => plugin.id === current) ? current : response.items[0]?.id || ""));
|
||||
setSelectedId((current) => (current && response.items.some((plugin) => plugin.id === current) ? current : ""));
|
||||
setUsingFallback(false);
|
||||
setListState("ready");
|
||||
} catch (error) {
|
||||
if (import.meta.env.DEV) {
|
||||
const fallback = fallbackMarketplacePlugins(filter);
|
||||
setPlugins(fallback);
|
||||
setSelectedId((current) => (current && fallback.some((plugin) => plugin.id === current) ? current : fallback[0]?.id || ""));
|
||||
setUsingFallback(true);
|
||||
setListState("ready");
|
||||
setListError("");
|
||||
return;
|
||||
}
|
||||
setListState("error");
|
||||
setListError(error instanceof Error ? error.message : "插件市场加载失败");
|
||||
setPlugins([]);
|
||||
setDetail(null);
|
||||
}
|
||||
}, [capability, keyword, serverType, statusFilter]);
|
||||
}, [capability, initialState, keyword, serverType, statusFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
@@ -248,13 +248,13 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{selectedId && (
|
||||
<section className="drawer-panel plugin-detail-panel" aria-label="plugin marketplace detail">
|
||||
<ManagementDialog open={selectedId !== ""} title={detail?.name ?? "插件详情"} wide onClose={() => { setSelectedId(""); setDetail(null); setDetailError(""); }}>
|
||||
<div className="plugin-detail-panel" aria-label="plugin marketplace detail">
|
||||
{detailPending && <LoadingState label="正在加载插件详情…" compact />}
|
||||
{detailError && <ErrorState title="插件详情加载失败" reason={detailError} diagnosticId={`plugin-detail:${selectedId}`} compact />}
|
||||
{detail && <PluginDetail plugin={detail} actionPending={actionPending} actionsDisabled={usingFallback} onAction={(action) => void changeState(action)} />}
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
</ManagementDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -326,43 +326,6 @@ function DetailStat({ label, value }: { label: string; value: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function fallbackMarketplacePlugins(filter: MarketplacePluginFilterRequest): MarketplacePluginResponse[] {
|
||||
const keyword = filter.keyword?.trim().toLowerCase() ?? "";
|
||||
return pluginCatalog
|
||||
.map((plugin) => ({
|
||||
id: plugin.id,
|
||||
name: plugin.name,
|
||||
description: plugin.validation,
|
||||
version: plugin.version,
|
||||
serverType: plugin.serverType,
|
||||
serverDisplayName: plugin.serverType,
|
||||
supportedOs: ["linux"],
|
||||
manifestRef: `manifest://${plugin.id}/${plugin.version}`,
|
||||
createFormSchemaRef: "schemas/create-form.schema.json",
|
||||
capabilities: ["process.install", "process.start", "logs.read"],
|
||||
declaredPermissions: plugin.permissions,
|
||||
permissions: {
|
||||
ai: plugin.permissions.includes("ai.invoke"),
|
||||
logs: plugin.permissions.includes("server.logs.read"),
|
||||
files: plugin.permissions.some((permission) => permission.includes("files")),
|
||||
jobs: plugin.permissions.includes("server.lifecycle"),
|
||||
artifacts: plugin.permissions.some((permission) => permission.includes("artifacts"))
|
||||
},
|
||||
lifecycleActions: { install: "actions/install.json", start: "actions/start.json", stop: "actions/stop.json" },
|
||||
bridgeActions: plugin.bridgeActions,
|
||||
pages: [{ key: "overview", title: "Overview", path: "/overview", permissions: plugin.permissions, bridgeActions: plugin.bridgeActions }],
|
||||
tags: [plugin.serverType],
|
||||
aiPurposes: plugin.permissions.includes("ai.invoke") ? ["logs.diagnose"] : [],
|
||||
validationViolations: plugin.status === "invalid" ? [plugin.validation] : undefined,
|
||||
status: plugin.status,
|
||||
source: "local-development"
|
||||
}))
|
||||
.filter((plugin) => !filter.status || filter.status === "all" || plugin.status === filter.status)
|
||||
.filter((plugin) => !filter.serverType || plugin.serverType === filter.serverType)
|
||||
.filter((plugin) => !filter.capability || plugin.capabilities.includes(filter.capability) || plugin.bridgeActions.includes(filter.capability))
|
||||
.filter((plugin) => !keyword || [plugin.id, plugin.name, plugin.serverType, ...plugin.tags, ...plugin.capabilities].some((value) => value.toLowerCase().includes(keyword)));
|
||||
}
|
||||
|
||||
function unique(values: string[]): string[] {
|
||||
return Array.from(new Set(values.filter(Boolean))).sort((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user