mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 14:15:24 +00:00
feat: show plugin features from the manifest on the plugin page
This commit is contained in:
1 parent
60024bdd4e
commit
b6bb3ffc73
13 files changed
+158
-2
No files matched your search
@@ -252,6 +252,8 @@ export interface PluginSummary {
|
||||
repository?: string;
|
||||
/** A YouTube video id from the manifest. Admins only. */
|
||||
videoId?: string;
|
||||
/** What the plugin does, from the manifest. Admins only. */
|
||||
features?: string[];
|
||||
signedBy?: string | null;
|
||||
}
|
||||
|
||||
@@ -286,6 +288,7 @@ export interface RegistryPluginEntry {
|
||||
repository?: string;
|
||||
icon?: string;
|
||||
videoId?: string;
|
||||
features?: string[];
|
||||
versions: RegistryPluginVersion[];
|
||||
latestVersion: string | null;
|
||||
installed: boolean;
|
||||
@@ -499,6 +502,7 @@ export interface PluginUploadPreview {
|
||||
version: string;
|
||||
description: string;
|
||||
author: string;
|
||||
features?: string[];
|
||||
capabilities: string[];
|
||||
replaces: string | null;
|
||||
}
|
||||
|
||||
@@ -2122,6 +2122,7 @@
|
||||
},
|
||||
"installedBadge": "Installed",
|
||||
"incompatible": "Needs a newer Termix",
|
||||
"features": "Features",
|
||||
"whatItCanDo": "What it can do",
|
||||
"addsToTermix": "Adds to Termix",
|
||||
"addsToTermixHint": "Uninstall and every one of these goes away.",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Bug,
|
||||
Check,
|
||||
Database,
|
||||
Download,
|
||||
ExternalLink,
|
||||
@@ -203,6 +204,22 @@ export function PluginDetail({
|
||||
<div className="flex flex-col gap-2 lg:col-span-2">
|
||||
<PluginVideo videoId={plugin.videoId} name={plugin.name} />
|
||||
|
||||
{plugin.features.length > 0 && (
|
||||
<SectionCard title={t("plugins.manager.features")} icon={null}>
|
||||
<ul className="grid grid-cols-1 gap-x-4 gap-y-1.5 px-4 py-3 sm:grid-cols-2">
|
||||
{plugin.features.map((feature) => (
|
||||
<li
|
||||
key={feature}
|
||||
className="flex items-start gap-2 text-xs leading-snug"
|
||||
>
|
||||
<Check className="mt-px size-3.5 shrink-0 text-accent-brand" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
<SectionCard title={t("plugins.manager.whatItCanDo")} icon={null}>
|
||||
<div className="divide-y divide-border">
|
||||
{orderByRisk(plugin.capabilities).map((capability) => (
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface PluginEntry {
|
||||
icon?: string;
|
||||
repository?: string;
|
||||
videoId?: string;
|
||||
features: string[];
|
||||
installed: boolean;
|
||||
version: string | null;
|
||||
latestVersion: string | null;
|
||||
@@ -94,6 +95,9 @@ export function mergePlugins(
|
||||
icon: summary.icon ?? entry?.icon,
|
||||
repository: summary.repository ?? entry?.repository,
|
||||
videoId: summary.videoId ?? entry?.videoId,
|
||||
features: summary.features?.length
|
||||
? summary.features
|
||||
: (entry?.features ?? []),
|
||||
installed: true,
|
||||
version: summary.version,
|
||||
latestVersion: entry?.latestVersion ?? null,
|
||||
@@ -130,6 +134,7 @@ export function mergePlugins(
|
||||
icon: entry.icon,
|
||||
repository: entry.repository,
|
||||
videoId: entry.videoId,
|
||||
features: entry.features ?? [],
|
||||
installed: false,
|
||||
version: null,
|
||||
latestVersion: entry.latestVersion,
|
||||
@@ -163,6 +168,7 @@ export function uploadEntry(preview: PluginUploadPreview): PluginEntry {
|
||||
name: preview.name,
|
||||
description: preview.description,
|
||||
author: preview.author,
|
||||
features: preview.features ?? [],
|
||||
category: "",
|
||||
installed: preview.replaces !== null,
|
||||
version: preview.replaces,
|
||||
@@ -226,6 +232,7 @@ export function matchesQuery(row: PluginEntry, query: string): boolean {
|
||||
return (
|
||||
row.name.toLowerCase().includes(q) ||
|
||||
row.description.toLowerCase().includes(q) ||
|
||||
row.features.some((f) => f.toLowerCase().includes(q)) ||
|
||||
row.id.includes(q)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,18 @@ describe("mergePlugins", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the installed features and falls back to the registry", () => {
|
||||
const registry = entry({ features: ["From registry"] });
|
||||
expect(
|
||||
mergePlugins([summary({ features: ["Installed"] })], [registry])[0]
|
||||
.features,
|
||||
).toEqual(["Installed"]);
|
||||
expect(mergePlugins([summary()], [registry])[0].features).toEqual([
|
||||
"From registry",
|
||||
]);
|
||||
expect(mergePlugins([summary()], null)[0].features).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps installed plugins when the registry is unreachable", () => {
|
||||
const rows = mergePlugins([summary()], null);
|
||||
expect(rows).toHaveLength(1);
|
||||
@@ -120,6 +132,7 @@ describe("sorting and filtering", () => {
|
||||
name: "Alpha",
|
||||
category: "Access",
|
||||
description: "Sign in",
|
||||
features: ["Works with Podman"],
|
||||
versions: [
|
||||
{
|
||||
version: "1.0.0",
|
||||
@@ -143,6 +156,9 @@ describe("sorting and filtering", () => {
|
||||
expect(
|
||||
rows.filter((r) => matchesQuery(r, "sign")).map((r) => r.id),
|
||||
).toEqual(["a"]);
|
||||
expect(
|
||||
rows.filter((r) => matchesQuery(r, "podman")).map((r) => r.id),
|
||||
).toEqual(["a"]);
|
||||
expect(rows.filter((r) => matchesQuery(r, " ")).length).toBe(2);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user