* feat: shell controller, TabStage, layout and theme plugins with repair view

* feat: move the classic layout into its own bundled plugin

* feat: move color themes into a bundled themes plugin

* fix: plugin tests and type checks work with a linked sdk

* fix: home tab, layout switch and missing layout fallbacks for looks

* chore: add classic layout and themes to the readme

* chore: drop example plugin links from the plugin guide

* feat: plugin filters by type, paged browse, hide plugins needing newer termix

* feat: move desktop, status, import, audit and branding features into plugins

* feat: host lock api so a plugin can load hosts from a config file (#1581)

* chore: add hosts config to the plugin table

* feat: login policy hook for plugins and optional https redirect (#1584)

* chore: add login security to the plugin table

* feat: api key socket tickets so a master can open remote sessions (#1654)

* feat: sync moves to a plugin, desktop gets multiple accounts (#1580, #1654)

* chore: add sync to the plugin table

* feat: move the sync screens and wizard out of core into the plugin (#1580)

* fix: add a neutral linked desktop hook for plugins (#1580)

* fix: show the linked account's name and admin flag in the app (#1580)

* feat: ssh transport providers, core ssh relay and plugin folders api

* feat: support FIDO2 security keys through ssh agent

* feat: add companion panels beside host tabs

* feat: mTLS client certificates for desktop server links

* feat: native web view for plugins in the desktop app

* fix: detect the desktop link window by Electron's default user agent

Fixes #1675

* fix: changelog for desktop link fix

* fix: keep text primary keys in the plugin data export

* fix: ship sharp binaries for Windows arm64 builds

* feat: client cert on the sync link types (sdk 1.0.18)

* chore: update translations

* feat: themed title bar for the desktop app (#1690)

* chore: changelog for the themed title bar

* fix: draw the title bar in the sidebar color

* feat: per host layout for the terminal panel bar

* chore: sdk 1.0.19 for the panel bar layout

* chore: bundle all 43 plugins for 26.10.3

* chore: update translations

---------

Co-authored-by: SirHephaistos <60616553+SirHephaistos@users.noreply.github.com>
Co-authored-by: Giovanny Aranda <garanda21@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-20 16:03:29 -05:00
1 parent aff29a9581
commit a89ccc6b07
577 files changed
+76479 -101831

No files matched your search

+68
View File
@@ -71,6 +71,14 @@ export interface PluginEntry {
registryName: string | null;
/** A linked server decides whether it runs here. */
managedByServer: boolean;
/** The app is drawn with it by default, so it can be swapped but not uninstalled. */
defaultLook: boolean;
/** Contribution keys from the manifest or the registry, like layouts. */
kinds: string[];
/** Listed, but no release runs on this build. */
unsupported: boolean;
/** The Termix range the newest release needs, when this build is too old. */
needsTermix: string | null;
}
export type PluginSource =
@@ -168,6 +176,15 @@ export function mergePlugins(
registryId: entry?.registryId ?? summary.registryId ?? null,
registryName: entry?.registryName ?? null,
managedByServer: summary.managedByServer ?? false,
defaultLook: summary.defaultLook ?? false,
kinds: [
...new Set([
...Object.keys(summary.contributes ?? {}),
...(entry?.contributes ?? []),
]),
],
unsupported: false,
needsTermix: null,
});
}
@@ -212,6 +229,13 @@ export function mergePlugins(
registryId: entry.registryId ?? "official",
registryName: entry.registryName ?? null,
managedByServer: false,
defaultLook: false,
kinds: entry.contributes ?? [],
unsupported: entry.latestVersion === null,
needsTermix:
entry.latestVersion === null
? (entry.versions.find((v) => v.needsTermix)?.needsTermix ?? null)
: null,
});
}
@@ -253,6 +277,10 @@ export function uploadEntry(preview: PluginUploadPreview): PluginEntry {
registryId: null,
registryName: null,
managedByServer: false,
defaultLook: false,
kinds: [],
unsupported: false,
needsTermix: null,
};
}
@@ -304,6 +332,46 @@ export function categoriesOf(rows: PluginEntry[]): string[] {
return [...new Set(rows.map((row) => row.category).filter(Boolean))].sort();
}
/** Contribution keys worth filtering by, in the order the filter lists them. */
export const PLUGIN_KINDS = [
"layouts",
"themes",
"tabs",
"panels",
"dashboardCards",
"protocols",
"auth",
"actions",
"hostCapability",
] as const;
export type PluginKind = (typeof PLUGIN_KINDS)[number];
/** The filterable kinds present in a set of rows, with how many have each. */
export function kindsOf(
rows: PluginEntry[],
): Array<{ kind: PluginKind; count: number }> {
return PLUGIN_KINDS.map((kind) => ({
kind,
count: rows.filter((row) => row.kinds.includes(kind)).length,
})).filter((entry) => entry.count > 0);
}
/** One page of rows, with the page clamped to what exists. */
export function pageOf<T>(
rows: T[],
page: number,
size: number,
): { items: T[]; page: number; pages: number } {
const pages = Math.max(1, Math.ceil(rows.length / size));
const current = Math.min(Math.max(1, page), pages);
return {
items: rows.slice((current - 1) * size, current * size),
page: current,
pages,
};
}
export function capabilityRisk(capability: string): CapabilityRisk {
return getCapabilityInfo(capability)?.risk ?? "medium";
}