mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
fix: synced plugin updates failing on desktop and server-only plugins shown as stopped
This commit is contained in:
1 parent
e92db4e9f8
commit
91e1ee94fa
12 files changed
+261
-12
No files matched your search
@@ -221,6 +221,8 @@ export interface PluginSummary {
|
||||
enabled: boolean;
|
||||
/** enabled | disabled | blocked | failed, or the loader's live state. */
|
||||
state: string;
|
||||
/** A linked desktop leaves this plugin to its server. */
|
||||
runsOnServer?: boolean;
|
||||
contributes: PluginContributions | null;
|
||||
/** Lucide icon name from the manifest. */
|
||||
icon?: string;
|
||||
|
||||
@@ -1875,12 +1875,15 @@
|
||||
"running": "Running",
|
||||
"disabled": "Disabled",
|
||||
"failed": "Stopped unexpectedly",
|
||||
"blocked": "Waiting on another plugin"
|
||||
"blocked": "Waiting on another plugin",
|
||||
"server": "Runs on your server"
|
||||
},
|
||||
"groups": {
|
||||
"running": "Running",
|
||||
"notRunning": "Not running"
|
||||
"notRunning": "Not running",
|
||||
"onServer": "On your server"
|
||||
},
|
||||
"runsOnServerHint": "This plugin only runs on your server, so it stays off in the desktop app. It still works there.",
|
||||
"sort": {
|
||||
"popular": "Most popular",
|
||||
"name": "Name",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Lightbulb,
|
||||
Pin,
|
||||
RotateCcw,
|
||||
Server,
|
||||
Settings,
|
||||
Trash2,
|
||||
TriangleAlert,
|
||||
@@ -225,6 +226,15 @@ export function PluginDetail({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{plugin.status === "server" && (
|
||||
<div className="flex items-start gap-2 border border-border bg-muted/30 px-3 py-2.5">
|
||||
<Server className="mt-px size-3.5 shrink-0 text-muted-foreground" />
|
||||
<span className="text-[11px] leading-snug text-muted-foreground">
|
||||
{t("plugins.manager.runsOnServerHint")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-2 lg:grid-cols-3">
|
||||
<div className="flex flex-col gap-2 lg:col-span-2">
|
||||
<PluginVideo videoId={plugin.videoId} name={plugin.name} />
|
||||
|
||||
@@ -483,7 +483,10 @@ function InstalledSection({
|
||||
sort,
|
||||
);
|
||||
const running = rows.filter((p) => p.status === "running");
|
||||
const stopped = rows.filter((p) => p.status !== "running");
|
||||
const onServer = rows.filter((p) => p.status === "server");
|
||||
const stopped = rows.filter(
|
||||
(p) => p.status !== "running" && p.status !== "server",
|
||||
);
|
||||
const pending = plugins.filter((p) => p.updateAvailable && !p.pinnedVersion);
|
||||
|
||||
const listColumns: DataColumn<PluginEntry>[] = [
|
||||
@@ -587,6 +590,14 @@ function InstalledSection({
|
||||
{collection(running)}
|
||||
</Group>
|
||||
)}
|
||||
{onServer.length > 0 && (
|
||||
<Group
|
||||
title={t("plugins.manager.groups.onServer")}
|
||||
count={onServer.length}
|
||||
>
|
||||
{collection(onServer)}
|
||||
</Group>
|
||||
)}
|
||||
{stopped.length > 0 && (
|
||||
<Group
|
||||
title={t("plugins.manager.groups.notRunning")}
|
||||
@@ -717,7 +728,9 @@ function InstalledCard({
|
||||
<div className="flex items-center gap-2 border-t border-border px-3 py-2">
|
||||
<span className="size-1.5 shrink-0 bg-muted-foreground/40" />
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{t("plugins.manager.status.disabled")}
|
||||
{plugin.status === "server"
|
||||
? t("plugins.manager.status.server")
|
||||
: t("plugins.manager.status.disabled")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
@@ -806,11 +819,13 @@ function StatusCell({ plugin }: { plugin: PluginEntry }) {
|
||||
? t("plugins.manager.status.blocked")
|
||||
: failed
|
||||
? t("plugins.manager.status.failed")
|
||||
: !running
|
||||
? t("plugins.manager.status.disabled")
|
||||
: plugin.pinnedVersion
|
||||
? t("plugins.manager.pinnedTo", { version: plugin.pinnedVersion })
|
||||
: t("plugins.manager.status.running");
|
||||
: plugin.status === "server"
|
||||
? t("plugins.manager.status.server")
|
||||
: !running
|
||||
? t("plugins.manager.status.disabled")
|
||||
: plugin.pinnedVersion
|
||||
? t("plugins.manager.pinnedTo", { version: plugin.pinnedVersion })
|
||||
: t("plugins.manager.status.running");
|
||||
return (
|
||||
<span
|
||||
className="flex min-w-0 items-center gap-1.5 text-muted-foreground"
|
||||
|
||||
@@ -24,7 +24,8 @@ import {
|
||||
normalizeGitHubRepo,
|
||||
} from "@/lib/issue-url";
|
||||
|
||||
export type PluginStatus = "running" | "disabled" | "failed" | "blocked";
|
||||
export type PluginStatus =
|
||||
"running" | "disabled" | "failed" | "blocked" | "server";
|
||||
|
||||
/** One row of the Plugins tab: what is installed merged with the registry. */
|
||||
export interface PluginEntry {
|
||||
@@ -100,6 +101,7 @@ export function pluginSource(summary: PluginSummary): PluginSource {
|
||||
}
|
||||
|
||||
export function pluginStatus(summary: PluginSummary): PluginStatus {
|
||||
if (summary.runsOnServer) return "server";
|
||||
if (summary.state === "failed") return "failed";
|
||||
if (summary.state === "blocked") return "blocked";
|
||||
if (!summary.enabled) return "disabled";
|
||||
@@ -136,7 +138,10 @@ export function mergePlugins(
|
||||
latestBeta: entry?.latestBeta ?? null,
|
||||
channel: summary.channel ?? entry?.channel ?? "stable",
|
||||
isBeta: isPrerelease(summary.version),
|
||||
updateAvailable: entry?.updateAvailable ?? false,
|
||||
// The server keeps its own copy up to date.
|
||||
updateAvailable: summary.runsOnServer
|
||||
? false
|
||||
: (entry?.updateAvailable ?? false),
|
||||
addedCapabilities: entry?.addedCapabilities ?? [],
|
||||
versions: entry?.versions ?? [],
|
||||
capabilities: summary.capabilities ?? [],
|
||||
|
||||
@@ -125,6 +125,14 @@ describe("mergePlugins", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("offers no update for a plugin a linked desktop leaves to its server", () => {
|
||||
const [row] = mergePlugins(
|
||||
[summary({ enabled: false, state: "disabled", runsOnServer: true })],
|
||||
[entry()],
|
||||
);
|
||||
expect(row).toMatchObject({ status: "server", updateAvailable: false });
|
||||
});
|
||||
|
||||
it("keeps installed plugins when the registry is unreachable", () => {
|
||||
const rows = mergePlugins([summary()], null);
|
||||
expect(rows).toHaveLength(1);
|
||||
@@ -140,6 +148,9 @@ describe("pluginStatus", () => {
|
||||
"disabled",
|
||||
);
|
||||
expect(pluginStatus(summary())).toBe("running");
|
||||
expect(pluginStatus(summary({ state: "failed", runsOnServer: true }))).toBe(
|
||||
"server",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user