fix: host defaults tabs sit where the host editor puts them and skip plugins with nothing to default

This commit is contained in:
LukeGus committed 2026-09-30 23:14:17 -05:00
1 parent f101d29d8a
commit d2a1be2eb0
2 files changed
+77 -4

No files matched your search

+32 -4
View File
@@ -12,12 +12,13 @@ import {
registerHostEditorSection,
unregisterHostEditorSection,
} from "@/sidebar/HostManagerTabs";
import type { PluginSummary } from "@/api/plugins-api";
import type { PluginSettingsField, PluginSummary } from "@/api/plugins-api";
import {
HostPluginSections,
type HostPluginSettings,
} from "./HostPluginSections";
import { hasVisibleFields } from "./settings-fields-util";
import { isDefaultableField } from "@/sidebar/host-defaults/catalog";
const TAB_PREFIX = "feature:";
@@ -29,6 +30,16 @@ interface HostFormLike {
pluginSettings?: HostPluginSettings;
}
function hasDefaultableFields(host: {
enableKey?: string;
fields: PluginSettingsField[];
}): boolean {
return (
!!host.enableKey ||
host.fields.some((field) => !field.hidden && isDefaultableField(field))
);
}
/** Adds, updates and removes generated tabs to match the plugin list. */
export function syncHostFeatureTabs(plugins: PluginSummary[]): string[] {
const existing = hostEditorSectionList();
@@ -36,6 +47,19 @@ export function syncHostFeatureTabs(plugins: PluginSummary[]): string[] {
(section) => section.pluginId && !section.id.startsWith(TAB_PREFIX),
);
const ownSections = new Set(own.map((section) => section.pluginId));
// The defaults tab sits where the plugin's own section does on a host.
const ownPlacement = new Map<
string,
{ group: "top" | "ssh"; order?: number }
>();
for (const section of own) {
if (!ownPlacement.has(section.pluginId!)) {
ownPlacement.set(section.pluginId!, {
group: section.group,
order: section.order,
});
}
}
// A plugin whose own sections are all about one host still gets its
// switches in the defaults editor, through a generated tab shown only there.
const ownDefaultsSections = new Set(
@@ -47,7 +71,10 @@ export function syncHostFeatureTabs(plugins: PluginSummary[]): string[] {
const contributors = plugins.filter((plugin) => {
if (!plugin.enabled || ownDefaultsSections.has(plugin.id)) return false;
const host = plugin.contributes?.settings?.host;
return !!host && (hasVisibleFields(host.fields) || !!host.enableKey);
if (!host) return false;
// Its own section edits it per host, so only a default is left to offer.
if (ownSections.has(plugin.id)) return hasDefaultableFields(host);
return hasVisibleFields(host.fields) || !!host.enableKey;
});
const wanted = new Set(
@@ -62,11 +89,12 @@ export function syncHostFeatureTabs(plugins: PluginSummary[]): string[] {
for (const plugin of contributors) {
const host = plugin.contributes!.settings!.host!;
const icon = plugin.icon;
const placement = ownPlacement.get(plugin.id);
registerHostEditorSection({
id: hostFeatureTabId(plugin.id),
pluginId: plugin.id,
group: host.editorGroup ?? "top",
order: host.editorOrder ?? 100,
group: placement?.group ?? host.editorGroup ?? "top",
order: placement?.order ?? host.editorOrder ?? 100,
labelKey: plugin.name,
label: plugin.name,
// Nothing but the plugin's host settings, so it works for defaults too.
@@ -121,6 +121,51 @@ describe("syncHostFeatureTabs", () => {
);
});
it("places the defaults tab where the plugin's own section sits", () => {
registerHostEditorSection({
id: "docker-own",
pluginId: "docker",
group: "ssh",
order: 40,
labelKey: "k",
component: () => null,
});
syncHostFeatureTabs([plugin()]);
const tab = getHostEditorSection(hostFeatureTabId("docker"));
expect(tab?.group).toBe("ssh");
expect(tab?.order).toBe(40);
});
it("gives a plugin with its own section no tab when nothing can have a default", () => {
registerHostEditorSection({
id: "proxmox-own",
pluginId: "proxmox",
group: "ssh",
labelKey: "k",
component: () => null,
});
const proxmox = plugin({
id: "proxmox",
name: "Proxmox",
contributes: {
settings: {
host: {
fields: [
{
key: "enableProxmox",
type: "custom",
component: "proxmoxHostSettings",
defaultable: false,
},
],
},
},
},
} as Partial<PluginSummary>);
expect(syncHostFeatureTabs([proxmox])).toEqual([]);
expect(getHostEditorSection(hostFeatureTabId("proxmox"))).toBeUndefined();
});
it("skips a plugin whose own section already covers the defaults editor", () => {
registerHostEditorSection({
id: "docker-own",