import { useQuery } from "@tanstack/react-query"; import { ArrowLeft, Cpu, Download, FlaskConical, KeyRound, Link2, MonitorCog, Puzzle, Shield, SlidersHorizontal, Upload, UserRoundPen, Users, } from "lucide-react"; import type { PluginRecord } from "@paperclipai/shared"; import { sidebarBadgesApi } from "@/api/sidebarBadges"; import { pluginsApi } from "@/api/plugins"; import { ApiError } from "@/api/client"; import { NavLink } from "@/lib/router"; import { INSTANCE_SETTINGS_PATH_PREFIX } from "@/lib/instance-settings"; import { SIDEBAR_SCROLL_RESET_STATE } from "@/lib/navigation-scroll"; import { queryKeys } from "@/lib/queryKeys"; import { useCompany } from "@/context/CompanyContext"; import { useCloudInstance } from "@/hooks/useCloudInstance"; import { useHiddenSettings } from "@/hooks/useHiddenSettings"; import { usePluginSlots } from "@/plugins/slots"; import { SidebarNavItem } from "./SidebarNavItem"; import { ContextualSidebarFrame } from "./ContextualSidebarFrame"; import { primarySidebarStyles } from "./primary-sidebar-styles"; /** * Sandbox-provider-only plugins (e.g. E2B, exe.dev, Modal) have no per-plugin * settings page, so a sidebar entry would lead nowhere useful. Filter them out * here. Plugins that mix a sandbox provider with other contributions still * appear. */ function isSandboxProviderOnly(plugin: PluginRecord): boolean { const drivers = plugin.manifestJson.environmentDrivers ?? []; if (drivers.length === 0) return false; return drivers.every((d) => d.kind === "sandbox_provider"); } export function CompanySettingsSidebar() { const { selectedCompanyId } = useCompany(); const { hidden: hiddenSettings } = useHiddenSettings(); const showPage = (pageKey: string) => !hiddenSettings.has(pageKey); const showPlugins = showPage("instance.plugins"); // Import is floored server-side on cloud-managed instances (403 cloud_managed), so the // nav entry is hidden rather than dead-ending. Export stays available. const isCloud = Boolean(useCloudInstance()); const { slots: companySettingsPluginSlots } = usePluginSlots({ slotTypes: ["companySettingsPage"], companyId: selectedCompanyId, enabled: !!selectedCompanyId, }); const { data: badges } = useQuery({ queryKey: selectedCompanyId ? queryKeys.sidebarBadges(selectedCompanyId) : ["sidebar-badges", "__disabled__"] as const, queryFn: async () => { try { return await sidebarBadgesApi.get(selectedCompanyId!); } catch (error) { if (error instanceof ApiError && (error.status === 401 || error.status === 403)) { return null; } throw error; } }, enabled: !!selectedCompanyId, retry: false, refetchInterval: 15_000, }); const { data: plugins } = useQuery({ queryKey: queryKeys.plugins.all, queryFn: () => pluginsApi.list(), // The listing only feeds the per-plugin subtree below; skip it when the // operator hides the Plugins surface. enabled: showPlugins, }); const sidebarPlugins = (plugins ?? []).filter((plugin) => !isSandboxProviderOnly(plugin)); return (
); }