Customize top-level sidebar items (#4203)

* feat(app): customize top-level sidebar items

Let users reorder and hide built-in and plugin sidebar rows from Appearance settings while keeping the preference device-local. Add Search as a first-class row and standardize section explanations as header info tooltips.

* fix(app): preserve consecutive sidebar preference edits

Derive each sidebar change from the latest cached settings and retain unavailable plugin slots while editing the visible list. Keep E2E interaction selectors tied to accessible control names.

* test(app): isolate provider settings unit boundary

Keep the legacy provider-section test focused on provider behavior now that its shared section shell can render the settings info control.
This commit is contained in:
Mohamed Boudra
2026-09-02 11:42:39 +02:00
committed by GitHub
parent d67a6f5f6c
commit de8535c38b
37 changed files with 1370 additions and 311 deletions
-1
View File
@@ -1,6 +1,5 @@
export { PluginCatalogSync } from "./catalog-sync";
export { buildLegacyPluginSurfaceRedirectRoute } from "./routes";
export { PluginSidebarItems } from "./sidebar-items";
export { PluginSurfaceScreen } from "./surface-screen";
export { usePluginAttachmentPicker } from "./attachments/picker";
export { PluginResourceAttachmentPill } from "./attachments/pill";
+5 -30
View File
@@ -1,18 +1,13 @@
import { router, usePathname } from "expo-router";
import { useCallback, useMemo } from "react";
import { useCallback } from "react";
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
import { resolvePluginIcon } from "./icons";
import { useInstalledPlugins } from "./registry";
import { buildPluginSurfaceRoute, hostIdFromPathname } from "./routes";
import {
getPreferredPluginContributionHost,
rememberPluginContributionHost,
} from "./contribution-host";
import {
groupPluginSidebarContributions,
type PluginSidebarGroup,
type PluginSidebarTarget,
} from "./sidebar-groups";
import { type PluginSidebarGroup, type PluginSidebarTarget } from "./sidebar-groups";
function selectTarget(
group: PluginSidebarGroup,
@@ -25,35 +20,15 @@ function selectTarget(
return remembered ?? group.targets[0];
}
export function PluginSidebarItems({ onBeforeNavigate }: { onBeforeNavigate?: () => void }) {
const plugins = useInstalledPlugins();
const pathname = usePathname();
const groups = useMemo(() => groupPluginSidebarContributions(plugins), [plugins]);
const currentHostId = hostIdFromPathname(pathname);
return groups.map((group) => (
<PluginSidebarItemRow
key={group.key}
group={group}
currentHostId={currentHostId}
pathname={pathname}
onBeforeNavigate={onBeforeNavigate}
/>
));
}
function PluginSidebarItemRow({
export function PluginSidebarItemRow({
group,
currentHostId,
pathname,
onBeforeNavigate,
}: {
group: PluginSidebarGroup;
currentHostId: string | null;
pathname: string;
onBeforeNavigate?: () => void;
}) {
const target = selectTarget(group, currentHostId);
const pathname = usePathname();
const target = selectTarget(group, hostIdFromPathname(pathname));
const route = buildPluginSurfaceRoute(target.plugin.serverId, group.pluginId, {
kind: "sidebar",
id: group.contributionId,