Remove the web UI status column from the plugin pages

Users don't need the web UI load state apart from the plugin's own state now
that the web UI reloads itself. A web load error now shows under the plugin's
versions and on its view page, only for enabled plugins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-10-28 23:35:35 +00:00
1 parent 188de979b6
commit 82b7c16dba
2 files changed
+37 -92

No files matched your search

@@ -48,70 +48,40 @@ function PluginMachines(props: PluginMachinesProps) {
type ControllerPluginProps = {
pluginMeta: PluginWebApi,
pluginInfo?: lib.PluginWebEnvInfo,
loadedPlugin?: lib.PluginWebEnvInfo,
title: string,
};
/** What the controller and web UI have loaded of the plugin */
function ControllerPlugin(props: ControllerPluginProps) {
const { pluginMeta, pluginInfo, loadedPlugin } = props;
const pluginError = pluginMeta.web.error ?? pluginInfo?.error;
const hasWebModule = Boolean(pluginMeta.web.main || pluginMeta.web.error);
if (!loadedPlugin) {
return <>
<Descriptions bordered size="small" title={props.title}>
<Descriptions.Item label="Version">{pluginMeta.version}</Descriptions.Item>
<Descriptions.Item label="Loaded in Web UI" span={2}>
{hasWebModule ? "No" : "No web module"}
</Descriptions.Item>
</Descriptions>
{pluginError && <Alert
style={{
marginTop: "1em",
}}
message={pluginError}
description={
"The web interface was unable to load the web module for this plugin. This is "+
"usually due to an incorrect or missing web build for the plugin. Due to the "+
"module not being loaded, the configs and web controls defined by this plugin may "+
"not be available in the web interface."
}
type="error"
showIcon
/>}
</>;
}
const homepageUrl = lib.safeExternalUrl(loadedPlugin.package.homepage);
const { pluginMeta, pluginInfo } = props;
const pkg = pluginInfo?.package;
const pluginError = pluginMeta.enabled ? pluginMeta.web.error ?? pluginInfo?.error : undefined;
const homepageUrl = lib.safeExternalUrl(pkg?.homepage);
return <>
<Descriptions bordered size="small" title={props.title}>
<Descriptions.Item label="Version">{pluginMeta.version}</Descriptions.Item>
<Descriptions.Item label="Loaded in Web UI" span={2}>Yes</Descriptions.Item>
<Descriptions.Item label="Description" span={3}>{loadedPlugin.description}</Descriptions.Item>
{loadedPlugin.package.homepage ? <Descriptions.Item label="Homepage" span={3}>
{homepageUrl
? <a href={homepageUrl}>{loadedPlugin.package.homepage}</a>
: loadedPlugin.package.homepage}
<Descriptions.Item label="Version" span={3}>{pluginMeta.version}</Descriptions.Item>
{pluginInfo?.description ? <Descriptions.Item label="Description" span={3}>
{pluginInfo.description}
</Descriptions.Item> : null}
{loadedPlugin.package.author ? <Descriptions.Item label="Author" span={3}>
{loadedPlugin.package.author}
{pkg?.homepage ? <Descriptions.Item label="Homepage" span={3}>
{homepageUrl ? <a href={homepageUrl}>{pkg.homepage}</a> : pkg.homepage}
</Descriptions.Item> : null}
<Descriptions.Item label="License" span={3}>{loadedPlugin.package.license}</Descriptions.Item>
{pkg?.author ? <Descriptions.Item label="Author" span={3}>{pkg.author}</Descriptions.Item> : null}
{pkg?.license ? <Descriptions.Item label="License" span={3}>{pkg.license}</Descriptions.Item> : null}
</Descriptions>
{pluginMeta.version !== loadedPlugin.package.version ? <Alert
{pluginError && <Alert
style={{
marginTop: "1em",
}}
message="Version missmatch detected"
message={pluginError}
description={
`The version of the web interface module for this plugin (${loadedPlugin.package.version}) `+
`does not match the version running on the controller (${pluginMeta.version}), `+
"spurious errors may occur. This usually happens when the plugin is updated but the "+
"controller has not been restarted yet, but may also be due to an outdated build."
"The web interface was unable to load the webpack module for this plugin. This is "+
"usually due to an incorrect or missing webpack build for the plugin. Due to the "+
"module not being loaded, the configs and web controls defined by this plugin may "+
"not be available in the web interface."
}
type="warning"
type="error"
showIcon
/> : null}
/>}
</>;
}
@@ -160,7 +130,6 @@ export default function PluginViewPage() {
? <ControllerPlugin
pluginMeta={pluginMeta}
pluginInfo={pluginInfo}
loadedPlugin={control.loadedPlugins.get(pluginName)}
title={pluginTitle}
/>
: <Descriptions bordered size="small" title={pluginTitle}>
+18 -42
View File
@@ -1,9 +1,8 @@
import React, { useContext, useEffect, useState } from "react";
import { Space, Table, Typography } from "antd";
import { Space, Table, Tooltip, Typography } from "antd";
import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled";
import InfoCircleFilled from "@ant-design/icons/InfoCircleFilled";
import type { PluginWebApi, PluginWebEnvInfo } from "@clusterio/lib";
import type { PluginWebApi } from "@clusterio/lib";
import notify from "../util/notify";
import { useAccount } from "../model/account";
@@ -28,8 +27,7 @@ type PluginRow = {
title: string;
/** Not present for plugins the controller hasn't loaded */
meta?: PluginWebApi;
info?: PluginWebEnvInfo;
package?: any;
webError?: string;
status?: PluginStatus;
};
@@ -38,31 +36,15 @@ function pluginPath(plugin: PluginRow) {
return `/plugins/${encodeURIComponent(plugin.meta?.name ?? plugin.key)}/view`;
}
function WebUiStatus(props: { plugin: PluginRow }) {
const control = useContext(ControlContext);
const { meta } = props.plugin;
if (!meta) {
return <Typography.Text type="secondary">Not on controller</Typography.Text>;
}
if (!meta.enabled) {
return <><InfoCircleFilled style={{ color: "#1668dc" }} /> Disabled on controller</>;
}
if (!meta.web.main && !meta.web.error) {
return <Typography.Text type="secondary">No web module</Typography.Text>;
}
if (!meta.web.error && !control.pluginInfos.has(meta.name)) {
return <><InfoCircleFilled style={{ color: "#1668dc" }} /> Reload page to load</>;
}
if (!props.plugin.package) {
return <><CloseCircleFilled style={{ color: "#dc4446" }} /> Error loading module</>;
}
if (props.plugin.package.version !== meta.version) {
return <><CloseCircleFilled style={{ color: "#dc4446" }} /> Version mis-matched</>;
}
return <>Loaded {meta.version}</>;
/** Shown with the versions when the plugin's web code failed to load */
function WebError(props: { error: string }) {
return <div>
<Tooltip title={props.error}>
<Typography.Text type="danger"><CloseCircleFilled /> Web interface failed to load</Typography.Text>
</Tooltip>
</div>;
}
export default function PluginsPage() {
const control = useContext(ControlContext);
const account = useAccount();
@@ -95,14 +77,12 @@ export default function PluginsPage() {
let tableContents: PluginRow[] = [];
for (let meta of pluginList) {
const key = pluginStatusKey(meta);
const loaded = control.loadedPlugins.get(meta.name);
const info = loaded ?? control.pluginInfos.get(meta.name);
const info = control.pluginInfos.get(meta.name);
tableContents.push({
key,
title: info ? info.title : meta.name,
meta,
info,
package: loaded?.package,
webError: meta.enabled ? meta.web.error ?? info?.error : undefined,
status: statuses.get(key),
});
}
@@ -151,16 +131,12 @@ export default function PluginsPage() {
{
title: "Versions",
key: "versions",
render: (_, plugin) => (plugin.status
? <PluginVersions status={plugin.status} latest={latestOf(plugin)} />
: plugin.meta?.version
),
},
{
title: "Web UI Status",
key: "web",
render: (_, plugin) => <WebUiStatus plugin={plugin} />,
responsive: ["sm"],
render: (_, plugin) => <>
{plugin.status
? <PluginVersions status={plugin.status} latest={latestOf(plugin)} />
: plugin.meta?.version}
{plugin.webError && <WebError error={plugin.webError} />}
</>,
},
{
key: "actions",