From d989ceca286bb965e72241b0a26417bfdf737995 Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 12:56:22 +0200 Subject: [PATCH 01/13] Group task configuration under settings Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 230 +++++++++++++----- .../settings/sections/AgentsSettings.tsx | 5 + .../ui/src/features/settings/types.test.ts | 15 ++ packages/ui/src/features/settings/types.ts | 6 + .../sidebar/components/SidebarNavSection.tsx | 35 +-- .../sidebar/components/items/AgentsItem.tsx | 19 -- .../components/items/ConfigureItem.tsx | 17 ++ .../components/items/McpServersItem.tsx | 19 -- .../sidebar/components/items/SkillsItem.tsx | 19 -- 9 files changed, 218 insertions(+), 147 deletions(-) create mode 100644 packages/ui/src/features/settings/sections/AgentsSettings.tsx create mode 100644 packages/ui/src/features/settings/types.test.ts delete mode 100644 packages/ui/src/features/sidebar/components/items/AgentsItem.tsx create mode 100644 packages/ui/src/features/sidebar/components/items/ConfigureItem.tsx delete mode 100644 packages/ui/src/features/sidebar/components/items/McpServersItem.tsx delete mode 100644 packages/ui/src/features/sidebar/components/items/SkillsItem.tsx diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 87f698a212..587338603f 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -11,7 +11,10 @@ import { GearSix, GithubLogo, Keyboard, + Lightbulb, Palette, + Plugs, + Robot, SignOut, SlackLogo, Terminal, @@ -26,8 +29,10 @@ import { useLogoutMutation } from "@posthog/ui/features/auth/useAuthMutations"; import { useCurrentUser } from "@posthog/ui/features/auth/useCurrentUser"; import { getUserInitials } from "@posthog/ui/features/auth/userInitials"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; +import { McpServersView } from "@posthog/ui/features/mcp-servers/components/McpServersView"; import { closeSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; import { AdvancedSettings } from "@posthog/ui/features/settings/sections/AdvancedSettings"; +import { AgentsSettings } from "@posthog/ui/features/settings/sections/AgentsSettings"; import { ClaudeCodeSettings } from "@posthog/ui/features/settings/sections/ClaudeCodeSettings"; import { DiscordSettings } from "@posthog/ui/features/settings/sections/DiscordSettings"; import { EnvironmentsSettings } from "@posthog/ui/features/settings/sections/environments/EnvironmentsSettings"; @@ -45,6 +50,7 @@ import { WorkspacesSettings } from "@posthog/ui/features/settings/sections/Works import { WorktreesSettings } from "@posthog/ui/features/settings/sections/worktrees/WorktreesSettings"; import { useSettingsPageStore } from "@posthog/ui/features/settings/stores/settingsPageStore"; import type { SettingsCategory } from "@posthog/ui/features/settings/types"; +import { SkillsView } from "@posthog/ui/features/skills/SkillsView"; import { useSpendAnalysisEnabled } from "@posthog/ui/features/usage/useSpendAnalysisEnabled"; import * as nav from "@posthog/ui/router/navigationBridge"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; @@ -59,29 +65,81 @@ interface SidebarItem { hasChevron?: boolean; } -const SIDEBAR_ITEMS: SidebarItem[] = [ - { id: "general", label: "General", icon: }, - { id: "notifications", label: "Notifications", icon: }, - { id: "plan-usage", label: "Plan & usage", icon: }, - { id: "workspaces", label: "Workspaces", icon: }, - { id: "worktrees", label: "Worktrees", icon: }, - { id: "environments", label: "Environments", icon: }, +interface SidebarGroup { + label: string; + items: SidebarItem[]; +} + +const SIDEBAR_GROUPS: SidebarGroup[] = [ + { + label: "Account", + items: [ + { id: "general", label: "General", icon: }, + { id: "notifications", label: "Notifications", icon: }, + { + id: "plan-usage", + label: "Plan & usage", + icon: , + }, + ], + }, + { + label: "Workspace", + items: [ + { id: "workspaces", label: "Workspaces", icon: }, + { + id: "worktrees", + label: "Worktrees", + icon: , + }, + { id: "environments", label: "Environments", icon: }, + ], + }, + { + label: "Configure", + items: [ + { id: "agents", label: "Agents", icon: }, + { id: "skills", label: "Skills", icon: }, + { id: "mcp-servers", label: "MCP servers", icon: }, + { + id: "signals", + label: "Self-driving", + icon: , + }, + ], + }, { - id: "personalization", - label: "Personalization", - icon: , + label: "Experience", + items: [ + { + id: "personalization", + label: "Personalization", + icon: , + }, + { id: "terminal", label: "Terminal", icon: }, + { id: "claude-code", label: "Claude Code", icon: }, + { id: "shortcuts", label: "Shortcuts", icon: }, + ], + }, + { + label: "Integrations", + items: [ + { id: "github", label: "GitHub", icon: }, + { id: "slack", label: "Slack", icon: }, + { id: "discord", label: "Discord", icon: }, + ], + }, + { + label: "Application", + items: [ + { id: "updates", label: "Updates", icon: }, + { id: "advanced", label: "Advanced", icon: }, + ], }, - { id: "terminal", label: "Terminal", icon: }, - { id: "claude-code", label: "Claude Code", icon: }, - { id: "shortcuts", label: "Shortcuts", icon: }, - { id: "github", label: "GitHub", icon: }, - { id: "slack", label: "Slack", icon: }, - { id: "discord", label: "Discord", icon: }, - { id: "signals", label: "Self-driving", icon: }, - { id: "updates", label: "Updates", icon: }, - { id: "advanced", label: "Advanced", icon: }, ]; +const SIDEBAR_ITEMS = SIDEBAR_GROUPS.flatMap((group) => group.items); + // Settings that only make sense with a local filesystem/host (local worktrees, // terminal, the local `claude` CLI, the desktop app itself). Hidden on the // cloud-only web host. @@ -102,6 +160,9 @@ const CATEGORY_TITLES: Record = { worktrees: "Worktrees", environments: "Environments", "cloud-environments": "Environments", + agents: "Agents", + skills: "Skills", + "mcp-servers": "MCP servers", personalization: "Personalization", terminal: "Terminal", "claude-code": "Claude Code", @@ -122,6 +183,9 @@ const CATEGORY_COMPONENTS: Record = { worktrees: WorktreesSettings, environments: EnvironmentsSettings, "cloud-environments": EnvironmentsSettings, + agents: AgentsSettings, + skills: SkillsView, + "mcp-servers": McpServersView, personalization: PersonalizationSettings, terminal: TerminalSettings, "claude-code": ClaudeCodeSettings, @@ -136,6 +200,11 @@ const CATEGORY_COMPONENTS: Record = { advanced: AdvancedSettings, }; +const FULL_HEIGHT_CATEGORIES: ReadonlySet = new Set([ + "skills", + "mcp-servers", +]); + export interface SettingsPanelProps { /** * Override the active category. Defaults to the `$category` URL param @@ -170,19 +239,22 @@ export function SettingsPanel({ const logoutMutation = useLogoutMutation(); const spendAnalysisEnabled = useSpendAnalysisEnabled(); - const sidebarItems = useMemo( + const sidebarGroups = useMemo( () => - SIDEBAR_ITEMS.filter((item) => { - if ( - item.id === "plan-usage" && - !billingEnabled && - !spendAnalysisEnabled - ) - return false; - if (!localWorkspaces && LOCAL_ONLY_CATEGORIES.has(item.id)) - return false; - return true; - }), + SIDEBAR_GROUPS.map((group) => ({ + ...group, + items: group.items.filter((item) => { + if ( + item.id === "plan-usage" && + !billingEnabled && + !spendAnalysisEnabled + ) + return false; + if (!localWorkspaces && LOCAL_ONLY_CATEGORIES.has(item.id)) + return false; + return true; + }), + })).filter((group) => group.items.length > 0), [billingEnabled, spendAnalysisEnabled, localWorkspaces], ); @@ -246,21 +318,28 @@ export function SettingsPanel({ -
- {sidebarItems.map((item) => { - const isActive = - resolvedCategory === item.id || - (item.id === "environments" && - resolvedCategory === "cloud-environments"); - return ( - setCategory(item.id)} - /> - ); - })} +
+ {sidebarGroups.map((group) => ( +
+ + {group.label} + + {group.items.map((item) => { + const isActive = + resolvedCategory === item.id || + (item.id === "environments" && + resolvedCategory === "cloud-environments"); + return ( + setCategory(item.id)} + /> + ); + })} +
+ ))}
@@ -312,23 +391,50 @@ export function SettingsPanel({ fill="url(#settings-dot-pattern)" /> - - - - {!formMode && ( - - {activeCategoryIcon && ( - {activeCategoryIcon} - )} - - {CATEGORY_TITLES[resolvedCategory]} - - - )} + {FULL_HEIGHT_CATEGORIES.has(resolvedCategory) ? ( + + {!formMode && ( + + {activeCategoryIcon && ( + {activeCategoryIcon} + )} + + {CATEGORY_TITLES[resolvedCategory]} + + + )} +
- - - +
+
+ ) : ( + + + + {!formMode && ( + + {activeCategoryIcon && ( + + {activeCategoryIcon} + + )} + + {CATEGORY_TITLES[resolvedCategory]} + + + )} + + + + + )}
diff --git a/packages/ui/src/features/settings/sections/AgentsSettings.tsx b/packages/ui/src/features/settings/sections/AgentsSettings.tsx new file mode 100644 index 0000000000..459e1fbdac --- /dev/null +++ b/packages/ui/src/features/settings/sections/AgentsSettings.tsx @@ -0,0 +1,5 @@ +import { ConfigureAgentsSection } from "@posthog/ui/features/inbox/components/ConfigureAgentsSection"; + +export function AgentsSettings() { + return ; +} diff --git a/packages/ui/src/features/settings/types.test.ts b/packages/ui/src/features/settings/types.test.ts new file mode 100644 index 0000000000..3100878d8b --- /dev/null +++ b/packages/ui/src/features/settings/types.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; +import { isSettingsCategory } from "./types"; + +describe("isSettingsCategory", () => { + it.each(["agents", "skills", "mcp-servers"])( + "accepts the %s configure category", + (category) => { + expect(isSettingsCategory(category)).toBe(true); + }, + ); + + it("rejects unknown categories", () => { + expect(isSettingsCategory("configure")).toBe(false); + }); +}); diff --git a/packages/ui/src/features/settings/types.ts b/packages/ui/src/features/settings/types.ts index 325494103b..e549aba42f 100644 --- a/packages/ui/src/features/settings/types.ts +++ b/packages/ui/src/features/settings/types.ts @@ -6,6 +6,9 @@ export type SettingsCategory = | "worktrees" | "environments" | "cloud-environments" + | "agents" + | "skills" + | "mcp-servers" | "personalization" | "terminal" | "claude-code" @@ -25,6 +28,9 @@ export const SETTINGS_CATEGORIES: readonly SettingsCategory[] = [ "worktrees", "environments", "cloud-environments", + "agents", + "skills", + "mcp-servers", "personalization", "terminal", "claude-code", diff --git a/packages/ui/src/features/sidebar/components/SidebarNavSection.tsx b/packages/ui/src/features/sidebar/components/SidebarNavSection.tsx index c92edb805f..45b8039867 100644 --- a/packages/ui/src/features/sidebar/components/SidebarNavSection.tsx +++ b/packages/ui/src/features/sidebar/components/SidebarNavSection.tsx @@ -6,20 +6,16 @@ import { HOME_TAB_FLAG } from "@posthog/shared/constants"; import { useCommandCenterStore } from "@posthog/ui/features/command-center/commandCenterStore"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { useInboxAllReports } from "@posthog/ui/features/inbox/hooks/useInboxAllReports"; +import { openSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; import { useSidebarStore } from "@posthog/ui/features/sidebar/sidebarStore"; import { useTasks } from "@posthog/ui/features/tasks/useTasks"; import { navigateToActivity, - navigateToAgents, navigateToCommandCenter, navigateToHome, navigateToInbox, - navigateToMcpServers, - navigateToSkills, navigateToWebsiteCommandCenter, navigateToWebsiteHome, - navigateToWebsiteMcpServers, - navigateToWebsiteSkills, } from "@posthog/ui/router/navigationBridge"; import { useAppView } from "@posthog/ui/router/useAppView"; import { openTaskInput } from "@posthog/ui/router/useOpenTask"; @@ -28,14 +24,12 @@ import { useCommandMenuStore } from "@posthog/ui/shell/commandMenuStore"; import { Box, Flex } from "@radix-ui/themes"; import { useRouterState } from "@tanstack/react-router"; import { ActivityItem } from "./items/ActivityItem"; -import { AgentsItem } from "./items/AgentsItem"; import { CommandCenterItem } from "./items/CommandCenterItem"; +import { ConfigureItem } from "./items/ConfigureItem"; import { HomeItem } from "./items/HomeItem"; import { InboxItem } from "./items/InboxItem"; -import { McpServersItem } from "./items/McpServersItem"; import { NewTaskItem } from "./items/NewTaskItem"; import { SearchItem } from "./items/SearchItem"; -import { SkillsItem } from "./items/SkillsItem"; const SIDEBAR_INBOX_REFETCH_INTERVAL_MS = 60_000; @@ -52,8 +46,8 @@ interface SidebarNavSectionProps { // and the Channels pane. It is fully self-contained — every item's active // state, badge count, and click handler is wired here — so it can be dropped // into either layout. In the Channels space, destinations with a /website -// mirror (Home, Skills, MCP servers, Command Center) stay in that space; -// Inbox, Agents and New task have no mirror yet and jump back to Code. +// mirror (Home and Command Center) stay in that space; Inbox and New task have +// no mirror yet and jump back to Code. Configure opens the shared settings UI. // Search opens the command menu in place. export function SidebarNavSection({ commandCenterActiveCount: providedActiveCount, @@ -72,18 +66,14 @@ export function SidebarNavSection({ // When this section renders inside the Channels space, the destinations that // have a /website mirror stay in that space; everything else (and the whole - // section in the Code space) uses the canonical routes. Inbox, Agents and - // New task have no mirror yet, so they intentionally jump back to Code. + // section in the Code space) uses the canonical routes. Inbox and New task + // have no mirror yet, so they intentionally jump back to Code. const inChannels = useRouterState({ select: (s) => s.location.pathname.startsWith("/website"), }); const goNewTask = () => openTaskInput(inChannels ? { space: "website" } : undefined); const goHome = inChannels ? navigateToWebsiteHome : navigateToHome; - const goSkills = inChannels ? navigateToWebsiteSkills : navigateToSkills; - const goMcpServers = inChannels - ? navigateToWebsiteMcpServers - : navigateToMcpServers; const goCommandCenter = inChannels ? navigateToWebsiteCommandCenter : navigateToCommandCenter; @@ -95,10 +85,7 @@ export function SidebarNavSection({ const isHomeViewActive = view.type === "home"; const isActivityActive = view.type === "activity"; const isInboxActive = view.type === "inbox"; - const isAgentsActive = view.type === "agents"; const isCommandCenterActive = view.type === "command-center"; - const isSkillsActive = view.type === "skills"; - const isMcpServersActive = view.type === "mcp-servers"; // Open pull requests in the inbox — the main CTA, and the same count the inbox // Pull requests tab shows, so the badge and the tab always agree. @@ -158,15 +145,7 @@ export function SidebarNavSection({ - - - - - - - - - + openSettings("agents")} /> diff --git a/packages/ui/src/features/sidebar/components/items/AgentsItem.tsx b/packages/ui/src/features/sidebar/components/items/AgentsItem.tsx deleted file mode 100644 index 7e935e2557..0000000000 --- a/packages/ui/src/features/sidebar/components/items/AgentsItem.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Robot } from "@phosphor-icons/react"; -import { SidebarItem } from "../SidebarItem"; - -interface AgentsItemProps { - isActive: boolean; - onClick: () => void; -} - -export function AgentsItem({ isActive, onClick }: AgentsItemProps) { - return ( - } - label="Agents" - isActive={isActive} - onClick={onClick} - /> - ); -} diff --git a/packages/ui/src/features/sidebar/components/items/ConfigureItem.tsx b/packages/ui/src/features/sidebar/components/items/ConfigureItem.tsx new file mode 100644 index 0000000000..c63562953b --- /dev/null +++ b/packages/ui/src/features/sidebar/components/items/ConfigureItem.tsx @@ -0,0 +1,17 @@ +import { SlidersHorizontal } from "@phosphor-icons/react"; +import { SidebarItem } from "../SidebarItem"; + +interface ConfigureItemProps { + onClick: () => void; +} + +export function ConfigureItem({ onClick }: ConfigureItemProps) { + return ( + } + label="Configure" + onClick={onClick} + /> + ); +} diff --git a/packages/ui/src/features/sidebar/components/items/McpServersItem.tsx b/packages/ui/src/features/sidebar/components/items/McpServersItem.tsx deleted file mode 100644 index f3f1146a0b..0000000000 --- a/packages/ui/src/features/sidebar/components/items/McpServersItem.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Plugs } from "@phosphor-icons/react"; -import { SidebarItem } from "../SidebarItem"; - -interface McpServersItemProps { - isActive: boolean; - onClick: () => void; -} - -export function McpServersItem({ isActive, onClick }: McpServersItemProps) { - return ( - } - label="MCP servers" - isActive={isActive} - onClick={onClick} - /> - ); -} diff --git a/packages/ui/src/features/sidebar/components/items/SkillsItem.tsx b/packages/ui/src/features/sidebar/components/items/SkillsItem.tsx deleted file mode 100644 index 3794819d99..0000000000 --- a/packages/ui/src/features/sidebar/components/items/SkillsItem.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Lightbulb } from "@phosphor-icons/react"; -import { SidebarItem } from "../SidebarItem"; - -interface SkillsItemProps { - isActive: boolean; - onClick: () => void; -} - -export function SkillsItem({ isActive, onClick }: SkillsItemProps) { - return ( - } - label="Skills" - isActive={isActive} - onClick={onClick} - /> - ); -} From 3246380a244270ab96a396f408e87c86d02e8325 Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:05:05 +0200 Subject: [PATCH 02/13] Align settings groups by concern Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 587338603f..41e7a4f019 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -66,13 +66,13 @@ interface SidebarItem { } interface SidebarGroup { - label: string; + label: string | null; items: SidebarItem[]; } const SIDEBAR_GROUPS: SidebarGroup[] = [ { - label: "Account", + label: null, items: [ { id: "general", label: "General", icon: }, { id: "notifications", label: "Notifications", icon: }, @@ -84,23 +84,17 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "Workspace", - items: [ - { id: "workspaces", label: "Workspaces", icon: }, - { - id: "worktrees", - label: "Worktrees", - icon: , - }, - { id: "environments", label: "Environments", icon: }, - ], - }, - { - label: "Configure", + label: "Agents", items: [ { id: "agents", label: "Agents", icon: }, { id: "skills", label: "Skills", icon: }, { id: "mcp-servers", label: "MCP servers", icon: }, + { id: "claude-code", label: "Claude Code", icon: }, + { + id: "personalization", + label: "Personalization", + icon: , + }, { id: "signals", label: "Self-driving", @@ -109,16 +103,15 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "Experience", + label: "Workspace", items: [ + { id: "workspaces", label: "Workspaces", icon: }, { - id: "personalization", - label: "Personalization", - icon: , + id: "worktrees", + label: "Worktrees", + icon: , }, - { id: "terminal", label: "Terminal", icon: }, - { id: "claude-code", label: "Claude Code", icon: }, - { id: "shortcuts", label: "Shortcuts", icon: }, + { id: "environments", label: "Environments", icon: }, ], }, { @@ -130,8 +123,10 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "Application", + label: "App", items: [ + { id: "terminal", label: "Terminal", icon: }, + { id: "shortcuts", label: "Shortcuts", icon: }, { id: "updates", label: "Updates", icon: }, { id: "advanced", label: "Advanced", icon: }, ], @@ -318,12 +313,17 @@ export function SettingsPanel({ -
+
{sidebarGroups.map((group) => ( -
- - {group.label} - +
+ {group.label && ( + + {group.label} + + )} {group.items.map((item) => { const isActive = resolvedCategory === item.id || From 81f284d63a78a04a242d14f4bd5a7778893745d9 Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:13:05 +0200 Subject: [PATCH 03/13] Restore Configure settings group Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- packages/ui/src/features/settings/components/SettingsPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 41e7a4f019..9de5b4a45c 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -84,7 +84,7 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "Agents", + label: "Configure", items: [ { id: "agents", label: "Agents", icon: }, { id: "skills", label: "Skills", icon: }, From d09fcaaa27f98ff7ebb3c5ec53cdb4065707900e Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:15:02 +0200 Subject: [PATCH 04/13] Restore original settings grouping Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 9de5b4a45c..587338603f 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -66,13 +66,13 @@ interface SidebarItem { } interface SidebarGroup { - label: string | null; + label: string; items: SidebarItem[]; } const SIDEBAR_GROUPS: SidebarGroup[] = [ { - label: null, + label: "Account", items: [ { id: "general", label: "General", icon: }, { id: "notifications", label: "Notifications", icon: }, @@ -83,18 +83,24 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ }, ], }, + { + label: "Workspace", + items: [ + { id: "workspaces", label: "Workspaces", icon: }, + { + id: "worktrees", + label: "Worktrees", + icon: , + }, + { id: "environments", label: "Environments", icon: }, + ], + }, { label: "Configure", items: [ { id: "agents", label: "Agents", icon: }, { id: "skills", label: "Skills", icon: }, { id: "mcp-servers", label: "MCP servers", icon: }, - { id: "claude-code", label: "Claude Code", icon: }, - { - id: "personalization", - label: "Personalization", - icon: , - }, { id: "signals", label: "Self-driving", @@ -103,15 +109,16 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "Workspace", + label: "Experience", items: [ - { id: "workspaces", label: "Workspaces", icon: }, { - id: "worktrees", - label: "Worktrees", - icon: , + id: "personalization", + label: "Personalization", + icon: , }, - { id: "environments", label: "Environments", icon: }, + { id: "terminal", label: "Terminal", icon: }, + { id: "claude-code", label: "Claude Code", icon: }, + { id: "shortcuts", label: "Shortcuts", icon: }, ], }, { @@ -123,10 +130,8 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ ], }, { - label: "App", + label: "Application", items: [ - { id: "terminal", label: "Terminal", icon: }, - { id: "shortcuts", label: "Shortcuts", icon: }, { id: "updates", label: "Updates", icon: }, { id: "advanced", label: "Advanced", icon: }, ], @@ -313,17 +318,12 @@ export function SettingsPanel({ -
+
{sidebarGroups.map((group) => ( -
- {group.label && ( - - {group.label} - - )} +
+ + {group.label} + {group.items.map((item) => { const isActive = resolvedCategory === item.id || From d2647f069568b5b13437ab4cb211711d05f7423f Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:17:04 +0200 Subject: [PATCH 05/13] Move Claude Code into Configure settings Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- packages/ui/src/features/settings/components/SettingsPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 587338603f..33199d9672 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -101,6 +101,7 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ { id: "agents", label: "Agents", icon: }, { id: "skills", label: "Skills", icon: }, { id: "mcp-servers", label: "MCP servers", icon: }, + { id: "claude-code", label: "Claude Code", icon: }, { id: "signals", label: "Self-driving", @@ -117,7 +118,6 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ icon: , }, { id: "terminal", label: "Terminal", icon: }, - { id: "claude-code", label: "Claude Code", icon: }, { id: "shortcuts", label: "Shortcuts", icon: }, ], }, From 591f679eb9914fe15b68566297290658b68da0dd Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:28:24 +0200 Subject: [PATCH 06/13] Normalize active settings sidebar category Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../features/settings/components/SettingsPanel.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 33199d9672..36f80dd2d8 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -265,6 +265,10 @@ export function SettingsPanel({ !localWorkspaces && LOCAL_ONLY_CATEGORIES.has(activeCategory) ? "general" : activeCategory; + const activeSidebarCategory: SettingsCategory = + resolvedCategory === "cloud-environments" + ? "environments" + : resolvedCategory; useHotkeys("escape", close, { enabled: true, @@ -276,9 +280,7 @@ export function SettingsPanel({ const ActiveComponent = CATEGORY_COMPONENTS[resolvedCategory]; const activeCategoryIcon = SIDEBAR_ITEMS.find( - (item) => - item.id === resolvedCategory || - (item.id === "environments" && resolvedCategory === "cloud-environments"), + (item) => item.id === activeSidebarCategory, )?.icon; const initials = getUserInitials(user); @@ -325,10 +327,7 @@ export function SettingsPanel({ {group.label} {group.items.map((item) => { - const isActive = - resolvedCategory === item.id || - (item.id === "environments" && - resolvedCategory === "cloud-environments"); + const isActive = activeSidebarCategory === item.id; return ( Date: Tue, 21 Jul 2026 13:29:53 +0200 Subject: [PATCH 07/13] Remove redundant settings category test Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- packages/ui/src/features/settings/types.test.ts | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 packages/ui/src/features/settings/types.test.ts diff --git a/packages/ui/src/features/settings/types.test.ts b/packages/ui/src/features/settings/types.test.ts deleted file mode 100644 index 3100878d8b..0000000000 --- a/packages/ui/src/features/settings/types.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { isSettingsCategory } from "./types"; - -describe("isSettingsCategory", () => { - it.each(["agents", "skills", "mcp-servers"])( - "accepts the %s configure category", - (category) => { - expect(isSettingsCategory(category)).toBe(true); - }, - ); - - it("rejects unknown categories", () => { - expect(isSettingsCategory("configure")).toBe(false); - }); -}); From fb94daa8efc188e562378ba83131fe2345a0f206 Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:36:58 +0200 Subject: [PATCH 08/13] Unify settings page layout metadata Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 105 +++++++++--------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 36f80dd2d8..ebdcd054f1 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -152,59 +152,59 @@ const LOCAL_ONLY_CATEGORIES: ReadonlySet = new Set([ "updates", ]); -const CATEGORY_TITLES: Record = { - general: "General", - notifications: "Notifications", - "plan-usage": "Plan & usage", - workspaces: "Workspaces", - worktrees: "Worktrees", - environments: "Environments", - "cloud-environments": "Environments", - agents: "Agents", - skills: "Skills", - "mcp-servers": "MCP servers", - personalization: "Personalization", - terminal: "Terminal", - "claude-code": "Claude Code", - shortcuts: "Shortcuts", - github: "GitHub", - slack: "Slack integration", - discord: "Discord", - signals: "Self-driving", - updates: "Updates", - advanced: "Advanced", -}; +type SettingsPageLayout = "contained" | "full-bleed"; + +interface SettingsPageDefinition { + title: string; + component: React.ComponentType; + layout: SettingsPageLayout; +} -const CATEGORY_COMPONENTS: Record = { - general: GeneralSettings, - notifications: NotificationsSettings, - "plan-usage": PlanUsageSettings, - workspaces: WorkspacesSettings, - worktrees: WorktreesSettings, - environments: EnvironmentsSettings, - "cloud-environments": EnvironmentsSettings, - agents: AgentsSettings, - skills: SkillsView, - "mcp-servers": McpServersView, - personalization: PersonalizationSettings, - terminal: TerminalSettings, - "claude-code": ClaudeCodeSettings, - shortcuts: ShortcutsSettings, - github: GitHubSettings, - slack: SlackSettings, - discord: DiscordSettings, +function defineSettingsPage( + title: string, + component: React.ComponentType, + layout: SettingsPageLayout = "contained", +): SettingsPageDefinition { + return { title, component, layout }; +} + +const SETTINGS_PAGES: Record = { + general: defineSettingsPage("General", GeneralSettings), + notifications: defineSettingsPage("Notifications", NotificationsSettings), + "plan-usage": defineSettingsPage("Plan & usage", PlanUsageSettings), + workspaces: defineSettingsPage("Workspaces", WorkspacesSettings), + worktrees: defineSettingsPage("Worktrees", WorktreesSettings), + environments: defineSettingsPage("Environments", EnvironmentsSettings), + "cloud-environments": defineSettingsPage( + "Environments", + EnvironmentsSettings, + ), + agents: defineSettingsPage("Agents", AgentsSettings), + skills: defineSettingsPage("Skills", SkillsView, "full-bleed"), + "mcp-servers": defineSettingsPage( + "MCP servers", + McpServersView, + "full-bleed", + ), + personalization: defineSettingsPage( + "Personalization", + PersonalizationSettings, + ), + terminal: defineSettingsPage("Terminal", TerminalSettings), + "claude-code": defineSettingsPage("Claude Code", ClaudeCodeSettings), + shortcuts: defineSettingsPage("Shortcuts", ShortcutsSettings), + github: defineSettingsPage("GitHub", GitHubSettings), + slack: defineSettingsPage("Slack integration", SlackSettings), + discord: defineSettingsPage("Discord", DiscordSettings), // Slack notification config lives in the dedicated Slack section; the Signals // section links out to it rather than duplicating the controls. - signals: () => , - updates: UpdatesSettings, - advanced: AdvancedSettings, + signals: defineSettingsPage("Self-driving", () => ( + + )), + updates: defineSettingsPage("Updates", UpdatesSettings), + advanced: defineSettingsPage("Advanced", AdvancedSettings), }; -const FULL_HEIGHT_CATEGORIES: ReadonlySet = new Set([ - "skills", - "mcp-servers", -]); - export interface SettingsPanelProps { /** * Override the active category. Defaults to the `$category` URL param @@ -277,7 +277,8 @@ export function SettingsPanel({ preventDefault: true, }); - const ActiveComponent = CATEGORY_COMPONENTS[resolvedCategory]; + const activePage = SETTINGS_PAGES[resolvedCategory]; + const ActiveComponent = activePage.component; const activeCategoryIcon = SIDEBAR_ITEMS.find( (item) => item.id === activeSidebarCategory, @@ -390,7 +391,7 @@ export function SettingsPanel({ fill="url(#settings-dot-pattern)" /> - {FULL_HEIGHT_CATEGORIES.has(resolvedCategory) ? ( + {activePage.layout === "full-bleed" ? ( {activeCategoryIcon} )} - {CATEGORY_TITLES[resolvedCategory]} + {activePage.title} )} @@ -425,7 +426,7 @@ export function SettingsPanel({ )} - {CATEGORY_TITLES[resolvedCategory]} + {activePage.title} )} From 4099fc194ba6e9ceae70c081befdd42de39062cd Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 13:47:28 +0200 Subject: [PATCH 09/13] Refine settings visibility and layout rendering Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 199 ++++++++++++------ 1 file changed, 133 insertions(+), 66 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index ebdcd054f1..cae44f7eef 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -152,7 +152,13 @@ const LOCAL_ONLY_CATEGORIES: ReadonlySet = new Set([ "updates", ]); -type SettingsPageLayout = "contained" | "full-bleed"; +const SETTINGS_PAGE_LAYOUT = { + CONTAINED: "contained", + FULL_BLEED: "full-bleed", +} as const; + +type SettingsPageLayout = + (typeof SETTINGS_PAGE_LAYOUT)[keyof typeof SETTINGS_PAGE_LAYOUT]; interface SettingsPageDefinition { title: string; @@ -163,7 +169,7 @@ interface SettingsPageDefinition { function defineSettingsPage( title: string, component: React.ComponentType, - layout: SettingsPageLayout = "contained", + layout: SettingsPageLayout = SETTINGS_PAGE_LAYOUT.CONTAINED, ): SettingsPageDefinition { return { title, component, layout }; } @@ -180,11 +186,15 @@ const SETTINGS_PAGES: Record = { EnvironmentsSettings, ), agents: defineSettingsPage("Agents", AgentsSettings), - skills: defineSettingsPage("Skills", SkillsView, "full-bleed"), + skills: defineSettingsPage( + "Skills", + SkillsView, + SETTINGS_PAGE_LAYOUT.FULL_BLEED, + ), "mcp-servers": defineSettingsPage( "MCP servers", McpServersView, - "full-bleed", + SETTINGS_PAGE_LAYOUT.FULL_BLEED, ), personalization: defineSettingsPage( "Personalization", @@ -205,6 +215,105 @@ const SETTINGS_PAGES: Record = { advanced: defineSettingsPage("Advanced", AdvancedSettings), }; +interface SettingsVisibility { + billingEnabled: boolean; + spendAnalysisEnabled: boolean; + localWorkspaces: boolean; +} + +function getHiddenSettingsCategories({ + billingEnabled, + spendAnalysisEnabled, + localWorkspaces, +}: SettingsVisibility): ReadonlySet { + const hiddenCategories = new Set(); + + if (!billingEnabled && !spendAnalysisEnabled) { + hiddenCategories.add("plan-usage"); + } + if (!localWorkspaces) { + for (const category of LOCAL_ONLY_CATEGORIES) { + hiddenCategories.add(category); + } + } + + return hiddenCategories; +} + +interface SettingsPageLayoutProps { + children: ReactNode; + formMode: boolean; + icon?: ReactNode; + title: string; +} + +function SettingsPageHeader({ + formMode, + icon, + title, + bordered = false, +}: Omit & { bordered?: boolean }) { + if (formMode) return null; + + return ( + + {icon && {icon}} + {title} + + ); +} + +function ContainedSettingsPageLayout({ + children, + formMode, + icon, + title, +}: SettingsPageLayoutProps) { + return ( + + + + + {children} + + + + ); +} + +function FullBleedSettingsPageLayout({ + children, + formMode, + icon, + title, +}: SettingsPageLayoutProps) { + return ( + + +
{children}
+
+ ); +} + +const SETTINGS_PAGE_LAYOUT_COMPONENTS: Record< + SettingsPageLayout, + React.ComponentType +> = { + [SETTINGS_PAGE_LAYOUT.CONTAINED]: ContainedSettingsPageLayout, + [SETTINGS_PAGE_LAYOUT.FULL_BLEED]: FullBleedSettingsPageLayout, +}; + export interface SettingsPanelProps { /** * Override the active category. Defaults to the `$category` URL param @@ -239,24 +348,18 @@ export function SettingsPanel({ const logoutMutation = useLogoutMutation(); const spendAnalysisEnabled = useSpendAnalysisEnabled(); - const sidebarGroups = useMemo( - () => - SIDEBAR_GROUPS.map((group) => ({ - ...group, - items: group.items.filter((item) => { - if ( - item.id === "plan-usage" && - !billingEnabled && - !spendAnalysisEnabled - ) - return false; - if (!localWorkspaces && LOCAL_ONLY_CATEGORIES.has(item.id)) - return false; - return true; - }), - })).filter((group) => group.items.length > 0), - [billingEnabled, spendAnalysisEnabled, localWorkspaces], - ); + const sidebarGroups = useMemo(() => { + const hiddenCategories = getHiddenSettingsCategories({ + billingEnabled, + spendAnalysisEnabled, + localWorkspaces, + }); + + return SIDEBAR_GROUPS.map((group) => ({ + ...group, + items: group.items.filter((item) => !hiddenCategories.has(item.id)), + })).filter((group) => group.items.length > 0); + }, [billingEnabled, spendAnalysisEnabled, localWorkspaces]); // Guard direct navigation (URL, deep link, programmatic openSettings) to a // category hidden on this host. Fall back to General so a hidden section is @@ -279,6 +382,7 @@ export function SettingsPanel({ const activePage = SETTINGS_PAGES[resolvedCategory]; const ActiveComponent = activePage.component; + const ActiveLayout = SETTINGS_PAGE_LAYOUT_COMPONENTS[activePage.layout]; const activeCategoryIcon = SIDEBAR_ITEMS.find( (item) => item.id === activeSidebarCategory, @@ -391,50 +495,13 @@ export function SettingsPanel({ fill="url(#settings-dot-pattern)" /> - {activePage.layout === "full-bleed" ? ( - - {!formMode && ( - - {activeCategoryIcon && ( - {activeCategoryIcon} - )} - - {activePage.title} - - - )} -
- -
-
- ) : ( - - - - {!formMode && ( - - {activeCategoryIcon && ( - - {activeCategoryIcon} - - )} - - {activePage.title} - - - )} - - - - - )} + + +
From 34cf1cb66975378c1a0c7adb819a437e0e45ea1d Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 15:00:47 +0200 Subject: [PATCH 10/13] Extract settings page content rendering Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../components/SettingsPageContent.tsx | 181 ++++++++++++++++ .../settings/components/SettingsPanel.tsx | 194 ++---------------- 2 files changed, 196 insertions(+), 179 deletions(-) create mode 100644 packages/ui/src/features/settings/components/SettingsPageContent.tsx diff --git a/packages/ui/src/features/settings/components/SettingsPageContent.tsx b/packages/ui/src/features/settings/components/SettingsPageContent.tsx new file mode 100644 index 0000000000..4afe68ab67 --- /dev/null +++ b/packages/ui/src/features/settings/components/SettingsPageContent.tsx @@ -0,0 +1,181 @@ +import { McpServersView } from "@posthog/ui/features/mcp-servers/components/McpServersView"; +import { AdvancedSettings } from "@posthog/ui/features/settings/sections/AdvancedSettings"; +import { AgentsSettings } from "@posthog/ui/features/settings/sections/AgentsSettings"; +import { ClaudeCodeSettings } from "@posthog/ui/features/settings/sections/ClaudeCodeSettings"; +import { DiscordSettings } from "@posthog/ui/features/settings/sections/DiscordSettings"; +import { EnvironmentsSettings } from "@posthog/ui/features/settings/sections/environments/EnvironmentsSettings"; +import { GeneralSettings } from "@posthog/ui/features/settings/sections/GeneralSettings"; +import { GitHubSettings } from "@posthog/ui/features/settings/sections/GitHubSettings"; +import { NotificationsSettings } from "@posthog/ui/features/settings/sections/NotificationsSettings"; +import { PersonalizationSettings } from "@posthog/ui/features/settings/sections/PersonalizationSettings"; +import { PlanUsageSettings } from "@posthog/ui/features/settings/sections/PlanUsageSettings"; +import { ShortcutsSettings } from "@posthog/ui/features/settings/sections/ShortcutsSettings"; +import { SignalSourcesSettings } from "@posthog/ui/features/settings/sections/SignalSourcesSettings"; +import { SlackSettings } from "@posthog/ui/features/settings/sections/SlackSettings"; +import { TerminalSettings } from "@posthog/ui/features/settings/sections/TerminalSettings"; +import { UpdatesSettings } from "@posthog/ui/features/settings/sections/UpdatesSettings"; +import { WorkspacesSettings } from "@posthog/ui/features/settings/sections/WorkspacesSettings"; +import { WorktreesSettings } from "@posthog/ui/features/settings/sections/worktrees/WorktreesSettings"; +import type { SettingsCategory } from "@posthog/ui/features/settings/types"; +import { SkillsView } from "@posthog/ui/features/skills/SkillsView"; +import { Box, Flex, ScrollArea, Text } from "@radix-ui/themes"; +import type { ComponentType, ReactNode } from "react"; + +const SETTINGS_PAGE_LAYOUT = { + CONTAINED: "contained", + FULL_BLEED: "full-bleed", +} as const; + +type SettingsPageLayout = + (typeof SETTINGS_PAGE_LAYOUT)[keyof typeof SETTINGS_PAGE_LAYOUT]; + +interface SettingsPageDefinition { + title: string; + component: ComponentType; + layout: SettingsPageLayout; +} + +function defineSettingsPage( + title: string, + component: ComponentType, + layout: SettingsPageLayout = SETTINGS_PAGE_LAYOUT.CONTAINED, +): SettingsPageDefinition { + return { title, component, layout }; +} + +const SETTINGS_PAGES: Record = { + general: defineSettingsPage("General", GeneralSettings), + notifications: defineSettingsPage("Notifications", NotificationsSettings), + "plan-usage": defineSettingsPage("Plan & usage", PlanUsageSettings), + workspaces: defineSettingsPage("Workspaces", WorkspacesSettings), + worktrees: defineSettingsPage("Worktrees", WorktreesSettings), + environments: defineSettingsPage("Environments", EnvironmentsSettings), + "cloud-environments": defineSettingsPage( + "Environments", + EnvironmentsSettings, + ), + agents: defineSettingsPage("Agents", AgentsSettings), + skills: defineSettingsPage( + "Skills", + SkillsView, + SETTINGS_PAGE_LAYOUT.FULL_BLEED, + ), + "mcp-servers": defineSettingsPage( + "MCP servers", + McpServersView, + SETTINGS_PAGE_LAYOUT.FULL_BLEED, + ), + personalization: defineSettingsPage( + "Personalization", + PersonalizationSettings, + ), + terminal: defineSettingsPage("Terminal", TerminalSettings), + "claude-code": defineSettingsPage("Claude Code", ClaudeCodeSettings), + shortcuts: defineSettingsPage("Shortcuts", ShortcutsSettings), + github: defineSettingsPage("GitHub", GitHubSettings), + slack: defineSettingsPage("Slack integration", SlackSettings), + discord: defineSettingsPage("Discord", DiscordSettings), + // Slack notification config lives in the dedicated Slack section; the Signals + // section links out to it rather than duplicating the controls. + signals: defineSettingsPage("Self-driving", () => ( + + )), + updates: defineSettingsPage("Updates", UpdatesSettings), + advanced: defineSettingsPage("Advanced", AdvancedSettings), +}; + +interface SettingsPageLayoutProps { + children: ReactNode; + formMode: boolean; + icon?: ReactNode; + title: string; +} + +function SettingsPageHeader({ + formMode, + icon, + title, + bordered = false, +}: Omit & { bordered?: boolean }) { + if (formMode) return null; + + return ( + + {icon && {icon}} + {title} + + ); +} + +function ContainedSettingsPageLayout({ + children, + formMode, + icon, + title, +}: SettingsPageLayoutProps) { + return ( + + + + + {children} + + + + ); +} + +function FullBleedSettingsPageLayout({ + children, + formMode, + icon, + title, +}: SettingsPageLayoutProps) { + return ( + + +
{children}
+
+ ); +} + +const SETTINGS_PAGE_LAYOUT_COMPONENTS: Record< + SettingsPageLayout, + ComponentType +> = { + [SETTINGS_PAGE_LAYOUT.CONTAINED]: ContainedSettingsPageLayout, + [SETTINGS_PAGE_LAYOUT.FULL_BLEED]: FullBleedSettingsPageLayout, +}; + +interface SettingsPageContentProps { + category: SettingsCategory; + formMode: boolean; + icon?: ReactNode; +} + +export function SettingsPageContent({ + category, + formMode, + icon, +}: SettingsPageContentProps) { + const page = SETTINGS_PAGES[category]; + const PageComponent = page.component; + const PageLayout = SETTINGS_PAGE_LAYOUT_COMPONENTS[page.layout]; + + return ( + + + + ); +} diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index cae44f7eef..3922d03ae0 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -29,33 +29,15 @@ import { useLogoutMutation } from "@posthog/ui/features/auth/useAuthMutations"; import { useCurrentUser } from "@posthog/ui/features/auth/useCurrentUser"; import { getUserInitials } from "@posthog/ui/features/auth/userInitials"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; -import { McpServersView } from "@posthog/ui/features/mcp-servers/components/McpServersView"; +import { SettingsPageContent } from "@posthog/ui/features/settings/components/SettingsPageContent"; import { closeSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; -import { AdvancedSettings } from "@posthog/ui/features/settings/sections/AdvancedSettings"; -import { AgentsSettings } from "@posthog/ui/features/settings/sections/AgentsSettings"; -import { ClaudeCodeSettings } from "@posthog/ui/features/settings/sections/ClaudeCodeSettings"; -import { DiscordSettings } from "@posthog/ui/features/settings/sections/DiscordSettings"; -import { EnvironmentsSettings } from "@posthog/ui/features/settings/sections/environments/EnvironmentsSettings"; -import { GeneralSettings } from "@posthog/ui/features/settings/sections/GeneralSettings"; -import { GitHubSettings } from "@posthog/ui/features/settings/sections/GitHubSettings"; -import { NotificationsSettings } from "@posthog/ui/features/settings/sections/NotificationsSettings"; -import { PersonalizationSettings } from "@posthog/ui/features/settings/sections/PersonalizationSettings"; -import { PlanUsageSettings } from "@posthog/ui/features/settings/sections/PlanUsageSettings"; -import { ShortcutsSettings } from "@posthog/ui/features/settings/sections/ShortcutsSettings"; -import { SignalSourcesSettings } from "@posthog/ui/features/settings/sections/SignalSourcesSettings"; -import { SlackSettings } from "@posthog/ui/features/settings/sections/SlackSettings"; -import { TerminalSettings } from "@posthog/ui/features/settings/sections/TerminalSettings"; -import { UpdatesSettings } from "@posthog/ui/features/settings/sections/UpdatesSettings"; -import { WorkspacesSettings } from "@posthog/ui/features/settings/sections/WorkspacesSettings"; -import { WorktreesSettings } from "@posthog/ui/features/settings/sections/worktrees/WorktreesSettings"; import { useSettingsPageStore } from "@posthog/ui/features/settings/stores/settingsPageStore"; import type { SettingsCategory } from "@posthog/ui/features/settings/types"; -import { SkillsView } from "@posthog/ui/features/skills/SkillsView"; import { useSpendAnalysisEnabled } from "@posthog/ui/features/usage/useSpendAnalysisEnabled"; import * as nav from "@posthog/ui/router/navigationBridge"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; -import { Avatar, Box, Flex, ScrollArea, Text } from "@radix-ui/themes"; -import { type ReactNode, useMemo } from "react"; +import { Avatar, Flex, ScrollArea, Text } from "@radix-ui/themes"; +import type { ReactNode } from "react"; import { useHotkeys } from "react-hotkeys-hook"; interface SidebarItem { @@ -152,69 +134,6 @@ const LOCAL_ONLY_CATEGORIES: ReadonlySet = new Set([ "updates", ]); -const SETTINGS_PAGE_LAYOUT = { - CONTAINED: "contained", - FULL_BLEED: "full-bleed", -} as const; - -type SettingsPageLayout = - (typeof SETTINGS_PAGE_LAYOUT)[keyof typeof SETTINGS_PAGE_LAYOUT]; - -interface SettingsPageDefinition { - title: string; - component: React.ComponentType; - layout: SettingsPageLayout; -} - -function defineSettingsPage( - title: string, - component: React.ComponentType, - layout: SettingsPageLayout = SETTINGS_PAGE_LAYOUT.CONTAINED, -): SettingsPageDefinition { - return { title, component, layout }; -} - -const SETTINGS_PAGES: Record = { - general: defineSettingsPage("General", GeneralSettings), - notifications: defineSettingsPage("Notifications", NotificationsSettings), - "plan-usage": defineSettingsPage("Plan & usage", PlanUsageSettings), - workspaces: defineSettingsPage("Workspaces", WorkspacesSettings), - worktrees: defineSettingsPage("Worktrees", WorktreesSettings), - environments: defineSettingsPage("Environments", EnvironmentsSettings), - "cloud-environments": defineSettingsPage( - "Environments", - EnvironmentsSettings, - ), - agents: defineSettingsPage("Agents", AgentsSettings), - skills: defineSettingsPage( - "Skills", - SkillsView, - SETTINGS_PAGE_LAYOUT.FULL_BLEED, - ), - "mcp-servers": defineSettingsPage( - "MCP servers", - McpServersView, - SETTINGS_PAGE_LAYOUT.FULL_BLEED, - ), - personalization: defineSettingsPage( - "Personalization", - PersonalizationSettings, - ), - terminal: defineSettingsPage("Terminal", TerminalSettings), - "claude-code": defineSettingsPage("Claude Code", ClaudeCodeSettings), - shortcuts: defineSettingsPage("Shortcuts", ShortcutsSettings), - github: defineSettingsPage("GitHub", GitHubSettings), - slack: defineSettingsPage("Slack integration", SlackSettings), - discord: defineSettingsPage("Discord", DiscordSettings), - // Slack notification config lives in the dedicated Slack section; the Signals - // section links out to it rather than duplicating the controls. - signals: defineSettingsPage("Self-driving", () => ( - - )), - updates: defineSettingsPage("Updates", UpdatesSettings), - advanced: defineSettingsPage("Advanced", AdvancedSettings), -}; - interface SettingsVisibility { billingEnabled: boolean; spendAnalysisEnabled: boolean; @@ -240,80 +159,6 @@ function getHiddenSettingsCategories({ return hiddenCategories; } -interface SettingsPageLayoutProps { - children: ReactNode; - formMode: boolean; - icon?: ReactNode; - title: string; -} - -function SettingsPageHeader({ - formMode, - icon, - title, - bordered = false, -}: Omit & { bordered?: boolean }) { - if (formMode) return null; - - return ( - - {icon && {icon}} - {title} - - ); -} - -function ContainedSettingsPageLayout({ - children, - formMode, - icon, - title, -}: SettingsPageLayoutProps) { - return ( - - - - - {children} - - - - ); -} - -function FullBleedSettingsPageLayout({ - children, - formMode, - icon, - title, -}: SettingsPageLayoutProps) { - return ( - - -
{children}
-
- ); -} - -const SETTINGS_PAGE_LAYOUT_COMPONENTS: Record< - SettingsPageLayout, - React.ComponentType -> = { - [SETTINGS_PAGE_LAYOUT.CONTAINED]: ContainedSettingsPageLayout, - [SETTINGS_PAGE_LAYOUT.FULL_BLEED]: FullBleedSettingsPageLayout, -}; - export interface SettingsPanelProps { /** * Override the active category. Defaults to the `$category` URL param @@ -348,18 +193,15 @@ export function SettingsPanel({ const logoutMutation = useLogoutMutation(); const spendAnalysisEnabled = useSpendAnalysisEnabled(); - const sidebarGroups = useMemo(() => { - const hiddenCategories = getHiddenSettingsCategories({ - billingEnabled, - spendAnalysisEnabled, - localWorkspaces, - }); - - return SIDEBAR_GROUPS.map((group) => ({ - ...group, - items: group.items.filter((item) => !hiddenCategories.has(item.id)), - })).filter((group) => group.items.length > 0); - }, [billingEnabled, spendAnalysisEnabled, localWorkspaces]); + const hiddenCategories = getHiddenSettingsCategories({ + billingEnabled, + spendAnalysisEnabled, + localWorkspaces, + }); + const sidebarGroups = SIDEBAR_GROUPS.map((group) => ({ + ...group, + items: group.items.filter((item) => !hiddenCategories.has(item.id)), + })).filter((group) => group.items.length > 0); // Guard direct navigation (URL, deep link, programmatic openSettings) to a // category hidden on this host. Fall back to General so a hidden section is @@ -380,10 +222,6 @@ export function SettingsPanel({ preventDefault: true, }); - const activePage = SETTINGS_PAGES[resolvedCategory]; - const ActiveComponent = activePage.component; - const ActiveLayout = SETTINGS_PAGE_LAYOUT_COMPONENTS[activePage.layout]; - const activeCategoryIcon = SIDEBAR_ITEMS.find( (item) => item.id === activeSidebarCategory, )?.icon; @@ -495,13 +333,11 @@ export function SettingsPanel({ fill="url(#settings-dot-pattern)" /> - - - + />
From 5a876ecfbd939bd5ef1cb45fc532c6b5aeed6b80 Mon Sep 17 00:00:00 2001 From: Shy Alter Date: Tue, 21 Jul 2026 15:28:14 +0200 Subject: [PATCH 11/13] Keep app chrome visible in settings Generated-By: PostHog Code Task-Id: 5579dbc2-d5dd-440f-8fb5-3cbd00ed0957 --- .../settings/components/SettingsPanel.tsx | 119 ++++++------------ packages/ui/src/router/routes/__root.tsx | 118 ++++++++--------- .../src/router/routes/settings/$category.tsx | 4 +- 3 files changed, 94 insertions(+), 147 deletions(-) diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 3922d03ae0..ad5f6897c5 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -15,7 +15,6 @@ import { Palette, Plugs, Robot, - SignOut, SlackLogo, Terminal, TrafficSignal, @@ -23,20 +22,16 @@ import { Wrench, } from "@phosphor-icons/react"; import { BILLING_FLAG } from "@posthog/shared"; -import { useOptionalAuthenticatedClient } from "@posthog/ui/features/auth/authClient"; -import { useAuthStateValue } from "@posthog/ui/features/auth/store"; -import { useLogoutMutation } from "@posthog/ui/features/auth/useAuthMutations"; -import { useCurrentUser } from "@posthog/ui/features/auth/useCurrentUser"; -import { getUserInitials } from "@posthog/ui/features/auth/userInitials"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { SettingsPageContent } from "@posthog/ui/features/settings/components/SettingsPageContent"; import { closeSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; import { useSettingsPageStore } from "@posthog/ui/features/settings/stores/settingsPageStore"; import type { SettingsCategory } from "@posthog/ui/features/settings/types"; +import { ProjectSwitcher } from "@posthog/ui/features/sidebar/components/ProjectSwitcher"; import { useSpendAnalysisEnabled } from "@posthog/ui/features/usage/useSpendAnalysisEnabled"; import * as nav from "@posthog/ui/router/navigationBridge"; import { useHostCapabilities } from "@posthog/ui/shell/useHostCapabilities"; -import { Avatar, Flex, ScrollArea, Text } from "@radix-ui/themes"; +import { Box, ScrollArea, Text } from "@radix-ui/themes"; import type { ReactNode } from "react"; import { useHotkeys } from "react-hotkeys-hook"; @@ -170,12 +165,15 @@ export interface SettingsPanelProps { onClose?: () => void; /** Override the category-change handler. Defaults to router navigation. */ onCategoryChange?: (category: SettingsCategory) => void; + /** Whether to show the settings navigation sidebar. */ + sidebarOpen?: boolean; } export function SettingsPanel({ activeCategory: activeCategoryProp, onClose, onCategoryChange, + sidebarOpen = true, }: SettingsPanelProps = {}) { const formMode = useSettingsPageStore((s) => s.formMode); const activeCategory = activeCategoryProp ?? "general"; @@ -183,14 +181,8 @@ export function SettingsPanel({ const setCategory = onCategoryChange ?? ((cat: SettingsCategory) => nav.navigateToSettings(cat, { replace: true })); - const isAuthenticated = useAuthStateValue( - (state) => state.status === "authenticated", - ); - const client = useOptionalAuthenticatedClient(); - const { data: user } = useCurrentUser({ client }); const billingEnabled = useFeatureFlag(BILLING_FLAG); const { localWorkspaces } = useHostCapabilities(); - const logoutMutation = useLogoutMutation(); const spendAnalysisEnabled = useSpendAnalysisEnabled(); const hiddenCategories = getHiddenSettingsCategories({ @@ -226,83 +218,52 @@ export function SettingsPanel({ (item) => item.id === activeSidebarCategory, )?.icon; - const initials = getUserInitials(user); - return (
-
-
- - {isAuthenticated && user && ( - - - - - {user.email} - - - - )} - - - - -
- {sidebarGroups.map((group) => ( -
- - {group.label} - - {group.items.map((item) => { - const isActive = activeSidebarCategory === item.id; - return ( - setCategory(item.id)} - /> - ); - })} -
- ))} -
-
- - {isAuthenticated && ( + {sidebarOpen && ( +
- )} -
+ + +
+ {sidebarGroups.map((group) => ( +
+ + {group.label} + + {group.items.map((item) => { + const isActive = activeSidebarCategory === item.id; + return ( + setCategory(item.id)} + /> + ); + })} +
+ ))} +
+
+ + + + +
+ )}
-
- {sidebarOpen && ( -
+
+
+ + {isAuthenticated && user && ( + + + + + {user.email} + + + + )} + + + + +
+ {sidebarGroups.map((group) => ( +
+ + {group.label} + + {group.items.map((item) => { + const isActive = activeSidebarCategory === item.id; + return ( + setCategory(item.id)} + /> + ); + })} +
+ ))} +
+
+ + {isAuthenticated && ( - - -
- {sidebarGroups.map((group) => ( -
- - {group.label} - - {group.items.map((item) => { - const isActive = activeSidebarCategory === item.id; - return ( - setCategory(item.id)} - /> - ); - })} -
- ))} -
-
- - - - -
- )} + )} +
+
{index > 0 ? " · " : null} { track(ANALYTICS_EVENTS.SCOUT_ACTION, { action_type: "open_helper_skill", diff --git a/packages/ui/src/features/settings/components/SettingsPanel.tsx b/packages/ui/src/features/settings/components/SettingsPanel.tsx index 3922d03ae0..5949d40d34 100644 --- a/packages/ui/src/features/settings/components/SettingsPanel.tsx +++ b/packages/ui/src/features/settings/components/SettingsPanel.tsx @@ -22,6 +22,7 @@ import { TreeStructure, Wrench, } from "@phosphor-icons/react"; +import { MenuLabel } from "@posthog/quill"; import { BILLING_FLAG } from "@posthog/shared"; import { useOptionalAuthenticatedClient } from "@posthog/ui/features/auth/authClient"; import { useAuthStateValue } from "@posthog/ui/features/auth/store"; @@ -31,6 +32,7 @@ import { getUserInitials } from "@posthog/ui/features/auth/userInitials"; import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag"; import { SettingsPageContent } from "@posthog/ui/features/settings/components/SettingsPageContent"; import { closeSettings } from "@posthog/ui/features/settings/hooks/useOpenSettings"; +import { getHiddenSettingsCategories } from "@posthog/ui/features/settings/settingsVisibility"; import { useSettingsPageStore } from "@posthog/ui/features/settings/stores/settingsPageStore"; import type { SettingsCategory } from "@posthog/ui/features/settings/types"; import { useSpendAnalysisEnabled } from "@posthog/ui/features/usage/useSpendAnalysisEnabled"; @@ -122,43 +124,6 @@ const SIDEBAR_GROUPS: SidebarGroup[] = [ const SIDEBAR_ITEMS = SIDEBAR_GROUPS.flatMap((group) => group.items); -// Settings that only make sense with a local filesystem/host (local worktrees, -// terminal, the local `claude` CLI, the desktop app itself). Hidden on the -// cloud-only web host. -const LOCAL_ONLY_CATEGORIES: ReadonlySet = new Set([ - "workspaces", - "worktrees", - "terminal", - "claude-code", - "discord", - "updates", -]); - -interface SettingsVisibility { - billingEnabled: boolean; - spendAnalysisEnabled: boolean; - localWorkspaces: boolean; -} - -function getHiddenSettingsCategories({ - billingEnabled, - spendAnalysisEnabled, - localWorkspaces, -}: SettingsVisibility): ReadonlySet { - const hiddenCategories = new Set(); - - if (!billingEnabled && !spendAnalysisEnabled) { - hiddenCategories.add("plan-usage"); - } - if (!localWorkspaces) { - for (const category of LOCAL_ONLY_CATEGORIES) { - hiddenCategories.add(category); - } - } - - return hiddenCategories; -} - export interface SettingsPanelProps { /** * Override the active category. Defaults to the `$category` URL param @@ -206,10 +171,11 @@ export function SettingsPanel({ // Guard direct navigation (URL, deep link, programmatic openSettings) to a // category hidden on this host. Fall back to General so a hidden section is // never rendered. - const resolvedCategory: SettingsCategory = - !localWorkspaces && LOCAL_ONLY_CATEGORIES.has(activeCategory) - ? "general" - : activeCategory; + const resolvedCategory: SettingsCategory = hiddenCategories.has( + activeCategory, + ) + ? "general" + : activeCategory; const activeSidebarCategory: SettingsCategory = resolvedCategory === "cloud-environments" ? "environments" @@ -266,9 +232,9 @@ export function SettingsPanel({
{sidebarGroups.map((group) => (
- + {group.label} - + {group.items.map((item) => { const isActive = activeSidebarCategory === item.id; return ( diff --git a/packages/ui/src/features/settings/settingsVisibility.test.ts b/packages/ui/src/features/settings/settingsVisibility.test.ts new file mode 100644 index 0000000000..2bf877efd1 --- /dev/null +++ b/packages/ui/src/features/settings/settingsVisibility.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from "vitest"; +import { getHiddenSettingsCategories } from "./settingsVisibility"; + +describe("getHiddenSettingsCategories", () => { + it.each([ + { + name: "shows all categories when every capability is available", + input: { + billingEnabled: true, + spendAnalysisEnabled: true, + localWorkspaces: true, + }, + expected: [], + }, + { + name: "hides plan and usage without billing or spend analysis", + input: { + billingEnabled: false, + spendAnalysisEnabled: false, + localWorkspaces: true, + }, + expected: ["plan-usage"], + }, + { + name: "hides host-specific categories without local workspaces", + input: { + billingEnabled: true, + spendAnalysisEnabled: true, + localWorkspaces: false, + }, + expected: [ + "workspaces", + "worktrees", + "terminal", + "claude-code", + "discord", + "updates", + ], + }, + ])("$name", ({ input, expected }) => { + expect([...getHiddenSettingsCategories(input)]).toEqual(expected); + }); +}); diff --git a/packages/ui/src/features/settings/settingsVisibility.ts b/packages/ui/src/features/settings/settingsVisibility.ts new file mode 100644 index 0000000000..dcba2a38b1 --- /dev/null +++ b/packages/ui/src/features/settings/settingsVisibility.ts @@ -0,0 +1,38 @@ +import type { SettingsCategory } from "@posthog/ui/features/settings/types"; + +// Settings that only make sense with a local filesystem/host (local worktrees, +// terminal, the local `claude` CLI, the desktop app itself). Hidden on the +// cloud-only web host. +const LOCAL_ONLY_CATEGORIES: ReadonlySet = new Set([ + "workspaces", + "worktrees", + "terminal", + "claude-code", + "discord", + "updates", +]); + +interface SettingsVisibility { + billingEnabled: boolean; + spendAnalysisEnabled: boolean; + localWorkspaces: boolean; +} + +export function getHiddenSettingsCategories({ + billingEnabled, + spendAnalysisEnabled, + localWorkspaces, +}: SettingsVisibility): ReadonlySet { + const hiddenCategories = new Set(); + + if (!billingEnabled && !spendAnalysisEnabled) { + hiddenCategories.add("plan-usage"); + } + if (!localWorkspaces) { + for (const category of LOCAL_ONLY_CATEGORIES) { + hiddenCategories.add(category); + } + } + + return hiddenCategories; +} diff --git a/packages/ui/src/router/navigationBridge.ts b/packages/ui/src/router/navigationBridge.ts index 1489badac8..c906c67f3d 100644 --- a/packages/ui/src/router/navigationBridge.ts +++ b/packages/ui/src/router/navigationBridge.ts @@ -155,10 +155,6 @@ export function navigateToScoutFindings(): void { void getRouterOrNull()?.navigate({ to: "/code/agents/scouts/findings" }); } -export function navigateToAgents(): void { - void getRouterOrNull()?.navigate({ to: "/code/agents" }); -} - export function navigateToApproval(requestId: string): void { void getRouterOrNull()?.navigate({ to: "/code/agents/applications/approvals", @@ -177,14 +173,6 @@ export function navigateToCommandCenter(): void { track(ANALYTICS_EVENTS.COMMAND_CENTER_VIEWED); } -export function navigateToSkills(): void { - void getRouterOrNull()?.navigate({ to: "/skills" }); -} - -export function navigateToMcpServers(): void { - void getRouterOrNull()?.navigate({ to: "/mcp-servers" }); -} - // Channels-space mirrors. These render the same shared views as their /code (or // top-level) counterparts but under /website, so navigating from the channels // sidebar keeps the channels chrome instead of switching back to Code. The @@ -203,14 +191,6 @@ export function navigateToCanvas(): void { void getRouterOrNull()?.navigate({ to: "/website" }); } -export function navigateToWebsiteSkills(): void { - void getRouterOrNull()?.navigate({ to: "/website/skills" }); -} - -export function navigateToWebsiteMcpServers(): void { - void getRouterOrNull()?.navigate({ to: "/website/mcp-servers" }); -} - export function navigateToWebsiteCommandCenter(): void { void getRouterOrNull()?.navigate({ to: "/website/command-center" }); // Parity with navigateToCommandCenter's analytics tracking. diff --git a/packages/ui/src/router/routes/mcp-servers.tsx b/packages/ui/src/router/routes/mcp-servers.tsx index 4c5ff42e90..69b2f20e0d 100644 --- a/packages/ui/src/router/routes/mcp-servers.tsx +++ b/packages/ui/src/router/routes/mcp-servers.tsx @@ -1,11 +1,11 @@ -import { McpServersView } from "@posthog/ui/features/mcp-servers/components/McpServersView"; -import { - AppPageSkeleton, - withRouteSkeleton, -} from "@posthog/ui/router/routeSkeletons"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/mcp-servers")({ - component: McpServersView, - ...withRouteSkeleton(AppPageSkeleton), + beforeLoad: () => { + throw redirect({ + to: "/settings/$category", + params: { category: "mcp-servers" }, + replace: true, + }); + }, }); diff --git a/packages/ui/src/router/routes/skills.tsx b/packages/ui/src/router/routes/skills.tsx index 79081df381..973ae90db4 100644 --- a/packages/ui/src/router/routes/skills.tsx +++ b/packages/ui/src/router/routes/skills.tsx @@ -1,11 +1,11 @@ -import { SkillsView } from "@posthog/ui/features/skills/SkillsView"; -import { - AppPageSkeleton, - withRouteSkeleton, -} from "@posthog/ui/router/routeSkeletons"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/skills")({ - component: SkillsView, - ...withRouteSkeleton(AppPageSkeleton), + beforeLoad: () => { + throw redirect({ + to: "/settings/$category", + params: { category: "skills" }, + replace: true, + }); + }, }); diff --git a/packages/ui/src/router/routes/website/mcp-servers.tsx b/packages/ui/src/router/routes/website/mcp-servers.tsx index 6c99cefc7e..ac8cccef19 100644 --- a/packages/ui/src/router/routes/website/mcp-servers.tsx +++ b/packages/ui/src/router/routes/website/mcp-servers.tsx @@ -1,9 +1,11 @@ -import { McpServersView } from "@posthog/ui/features/mcp-servers/components/McpServersView"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; -// Channels-space mirror of /mcp-servers. Renders the same shared McpServersView -// so the page stays single-source; only the route entry is duplicated so -// navigating here keeps the channels chrome (rail + channel sidebar). export const Route = createFileRoute("/website/mcp-servers")({ - component: McpServersView, + beforeLoad: () => { + throw redirect({ + to: "/settings/$category", + params: { category: "mcp-servers" }, + replace: true, + }); + }, }); diff --git a/packages/ui/src/router/routes/website/skills.tsx b/packages/ui/src/router/routes/website/skills.tsx index e18f14d5e8..c01c82aa6f 100644 --- a/packages/ui/src/router/routes/website/skills.tsx +++ b/packages/ui/src/router/routes/website/skills.tsx @@ -1,9 +1,11 @@ -import { SkillsView } from "@posthog/ui/features/skills/SkillsView"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; -// Channels-space mirror of /skills. Renders the same shared SkillsView so the -// page stays single-source; only the route entry is duplicated so navigating -// here keeps the channels chrome (rail + channel sidebar). export const Route = createFileRoute("/website/skills")({ - component: SkillsView, + beforeLoad: () => { + throw redirect({ + to: "/settings/$category", + params: { category: "skills" }, + replace: true, + }); + }, });