diff --git a/ui/src/components/app-sidebar-menus.ts b/ui/src/components/app-sidebar-menus.ts
index 19ef55c635b5..3bd97d745be1 100644
--- a/ui/src/components/app-sidebar-menus.ts
+++ b/ui/src/components/app-sidebar-menus.ts
@@ -29,7 +29,6 @@ import {
renderSidebarMoreRow,
renderSidebarNavRoute,
sidebarMoreMenuHoldsActiveRoute,
- sidebarPluginTabs,
} from "./app-sidebar-nav-menus.ts";
import { AppSidebarSessionGroupsElement } from "./app-sidebar-session-groups.ts";
import {
@@ -592,9 +591,7 @@ export abstract class AppSidebarMenusElement extends AppSidebarSessionGroupsElem
position,
basePath: this.basePath,
activeRouteId: this.activeRouteId,
- activePluginTabId: this.activePluginTabId,
sidebarEntries: this.sidebarEntries,
- pluginTabs: sidebarPluginTabs(this.context?.gateway.snapshot.hello?.controlUiTabs),
isRouteEnabled: (routeId) => this.isRouteEnabled(routeId),
onTabAway: () => trigger?.focus(),
onClose: (restoreFocus) => {
@@ -607,10 +604,6 @@ export abstract class AppSidebarMenusElement extends AppSidebarSessionGroupsElem
this.closeMoreMenu({ restoreFocus: true });
this.onNavigate?.(routeId);
},
- onNavigatePluginTab: (search) => {
- this.closeMoreMenu({ restoreFocus: true });
- this.onNavigate?.("plugin", { search });
- },
onPreloadRoute: (routeId, event) => this.preloadRoute(routeId, event),
onCancelPreload: this.cancelPreload,
onEditPinnedItems: () => {
diff --git a/ui/src/components/app-sidebar-nav-menus.ts b/ui/src/components/app-sidebar-nav-menus.ts
index ec78b03b3cb0..990c1dd87a94 100644
--- a/ui/src/components/app-sidebar-nav-menus.ts
+++ b/ui/src/components/app-sidebar-nav-menus.ts
@@ -16,7 +16,7 @@ import {
} from "../app-navigation.ts";
import { pathForRoute } from "../app-route-paths.ts";
import { t } from "../i18n/index.ts";
-import { pluginTabKey, pluginTabSearch } from "../pages/plugin/route.ts";
+import { pluginTabSearch } from "../pages/plugin/route.ts";
import { icons, type IconName } from "./icons.ts";
import { consumeDropdownKeyboardDismissal, trackDropdownKeyboardDismissal } from "./web-awesome.ts";
@@ -54,7 +54,7 @@ export function isSidebarRouteActive(
return activeRouteId === routeId;
}
-/** Dynamic plugin tabs stay in the More menu; only stable static route ids can be persisted as pins. */
+/** Stable ordering for plugin-provided sidebar tabs. */
export function sidebarPluginTabs(
tabs: readonly GatewayControlUiPluginTab[] | undefined,
): GatewayControlUiPluginTab[] {
@@ -99,7 +99,36 @@ export function renderSidebarNavRoute(params: SidebarNavRouteParams) {
`;
}
-/** Unpinned routes, plugin tabs, and the pin editor live in a popup behind this row. */
+export function renderSidebarPluginTab(params: {
+ tab: GatewayControlUiPluginTab;
+ basePath: string;
+ active: boolean;
+ onNavigate: (search: string) => void;
+}) {
+ const search = pluginTabSearch({ pluginId: params.tab.pluginId, id: params.tab.id });
+ const iconName = Object.hasOwn(icons, params.tab.icon!)
+ ? (params.tab.icon as IconName)
+ : "puzzle";
+ return html`
+ {
+ if (!shouldHandleNavigationClick(event)) {
+ return;
+ }
+ event.preventDefault();
+ params.onNavigate(search);
+ }}
+ >
+
+
+
+ `;
+}
+
+/** Unpinned routes and the pin editor live in a popup behind this row. */
export function renderSidebarMoreRow(params: {
open: boolean;
active: boolean;
@@ -121,7 +150,6 @@ export function renderSidebarMoreRow(params: {
type SidebarMenuNavigationHandlers = {
onNavigateRoute: (routeId: SidebarNavRoute) => void;
- onNavigatePluginTab: (search: string) => void;
onPreloadRoute: (routeId: SidebarNavRoute, event: Event) => void;
onCancelPreload: (event: Event) => void;
};
@@ -130,9 +158,7 @@ type SidebarMoreMenuParams = SidebarMenuNavigationHandlers & {
position: SidebarMenuPosition | null;
basePath: string;
activeRouteId: NavigationRouteId | undefined;
- activePluginTabId: string;
sidebarEntries: readonly string[];
- pluginTabs: readonly GatewayControlUiPluginTab[];
isRouteEnabled: (routeId: NavigationRouteId) => boolean;
onEditPinnedItems: () => void;
onTabAway: () => void;
@@ -166,33 +192,6 @@ function renderMoreMenuRoute(params: SidebarMoreMenuParams, routeId: SidebarNavR
`;
}
-function renderMoreMenuPluginTab(params: SidebarMoreMenuParams, tab: GatewayControlUiPluginTab) {
- const ref = { pluginId: tab.pluginId, id: tab.id };
- const search = pluginTabSearch(ref);
- const active =
- params.activeRouteId === "plugin" && params.activePluginTabId === pluginTabKey(ref);
- const iconName = tab.icon && Object.hasOwn(icons, tab.icon) ? (tab.icon as IconName) : "puzzle";
- return html`
-