From 8229db753387bb82837cc9b2335017cbe6124fe9 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sun, 15 Nov 2026 18:26:41 +0100 Subject: [PATCH] Fix plugin list and header layout on mobile --- CHANGELOG.md | 7 +++ .../src/components/PluginClusterStatus.tsx | 10 ++-- .../web_ui/src/components/PluginsPage.tsx | 58 ++++++++++++------- packages/web_ui/src/components/SiteLayout.tsx | 8 ++- packages/web_ui/src/style.css | 32 ++++++++++ test/manual/README.md | 15 +++++ 6 files changed, 101 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea17be12..2ea38d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,13 @@ Many thanks to the following for contributing to this release: [@username](https://github.com/username) --> +## Unreleased + +### Fixes + +- Fixed cramped plugin rows and wrapping header branding on mobile screens. + + ## Version 2.1.0 ### Features diff --git a/packages/web_ui/src/components/PluginClusterStatus.tsx b/packages/web_ui/src/components/PluginClusterStatus.tsx index 570c1f1b..3797d4d0 100644 --- a/packages/web_ui/src/components/PluginClusterStatus.tsx +++ b/packages/web_ui/src/components/PluginClusterStatus.tsx @@ -172,10 +172,12 @@ export function PluginVersions(props: { status: PluginStatus, latest?: string }) {versions.map(version => { const now = count(machine => machine.running === version); const after = count(machine => machine.afterRestart === version); - return
- {version} - {props.latest === version && latest} - + return
+ + {version} + {props.latest === version && latest} + + {plural(now, "machine")}{now !== after && ` → ${after} after restart`}
; diff --git a/packages/web_ui/src/components/PluginsPage.tsx b/packages/web_ui/src/components/PluginsPage.tsx index 52e7d85c..f7829ee2 100644 --- a/packages/web_ui/src/components/PluginsPage.tsx +++ b/packages/web_ui/src/components/PluginsPage.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useState } from "react"; -import { Space, Table, Tooltip, Typography } from "antd"; +import { Grid, Space, Table, Tooltip, Typography } from "antd"; import CloseCircleFilled from "@ant-design/icons/CloseCircleFilled"; import type { PluginWebApi } from "@clusterio/lib"; @@ -48,6 +48,8 @@ function WebError(props: { error: string }) { export default function PluginsPage() { const control = useContext(ControlContext); const account = useAccount(); + const screens = Grid.useBreakpoint(); + const isMobile = screens.md === false; let [pluginList, setPluginList] = useState([]); const tableState = useTableQueryState({ namespace: "plugin", defaultSortKey: "name", @@ -96,6 +98,24 @@ export default function PluginsPage() { const latestOf = (plugin: PluginRow) => ( plugin.status?.npmPackage ? latestVersions[plugin.status.npmPackage] : undefined ); + const versions = (plugin: PluginRow) => <> + {plugin.status + ? + : plugin.meta?.version} + {plugin.webError && } + ; + const updateAction = (plugin: PluginRow) => (plugin.status + // Keep clicks on the update action from opening the plugin. + ? { event.stopPropagation(); }}> + + + : null + ); return } /> - {plugin.title} - , + render: (_, plugin) => <> + + {plugin.title} + + {isMobile &&
+
{versions(plugin)}
+ {updateAction(plugin)} +
} + , sorter: (a, b) => strcmp(a.title, b.title), sortOrder: tableState.sortOrder("name"), filteredValue: tableState.filteredValue("name"), @@ -131,28 +159,14 @@ export default function PluginsPage() { { title: "Versions", key: "versions", - render: (_, plugin) => <> - {plugin.status - ? - : plugin.meta?.version} - {plugin.webError && } - , + responsive: ["md"], + render: (_, plugin) => versions(plugin), }, { key: "actions", align: "right", - render: (_, plugin) => (plugin.status - // Keep clicks on the update action from opening the plugin - ? { event.stopPropagation(); }}> - - - : null - ), + responsive: ["md"], + render: (_, plugin) => updateAction(plugin), }, ]} dataSource={tableContents} diff --git a/packages/web_ui/src/components/SiteLayout.tsx b/packages/web_ui/src/components/SiteLayout.tsx index 03e183e3..ed03e0ad 100644 --- a/packages/web_ui/src/components/SiteLayout.tsx +++ b/packages/web_ui/src/components/SiteLayout.tsx @@ -169,8 +169,10 @@ export default function SiteLayout() { />; let accountMenu = - ; @@ -228,7 +230,7 @@ export default function SiteLayout() { Clusterio logo - + Clusterio a { + font-weight: 600; + overflow-wrap: anywhere; + } +} + +.plugin-mobile-details { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + margin-top: 8px; + overflow-wrap: anywhere; +} + +.plugin-version { + display: flex; + flex-wrap: wrap; + align-items: baseline; + column-gap: 12px; +} + /* Keep the sidebar fixed in place (below the header) while content scrolls. */ .site-layout-sider { position: sticky; diff --git a/test/manual/README.md b/test/manual/README.md index 3b24dc23..977b1b4d 100644 --- a/test/manual/README.md +++ b/test/manual/README.md @@ -2,6 +2,21 @@ This folder contains files used for performing manual tests that are non-trivial to automate. + +## Plugins page on small screens + +Use a cluster with several plugins, including one with different running and installed versions and one with a web load error. +Compare the Plugins page at 390px and 1440px viewport widths, and check the 768px breakpoint. + +1. On phones, check that each row shows its name, versions, machine counts, status and update action vertically, without clipping or breaking ordinary words. +2. Check that a long plugin name wraps and the header branding stays on one line. +3. Search and sort by name, reload, and check that the selected filter and sort order remain applied. +4. Open an update or restart checklist, then cancel it; update actions must not navigate to the plugin page. +5. Open a plugin by its name and by clicking elsewhere in its row. +6. Open the account menu using the user icon on mobile; the full account name should appear in the menu. +7. On desktop, check that the name, versions and update action remain separate columns. + + ## Setup test mod Run `node packages/lib/dist/node/build_mod.js --source-dir test/manual/test_mod/ --output-dir mods/` to create the test mod.