11import { Badge , Switch } from "@posthog/quill" ;
22import { PROJECT_BLUEBIRD_FLAG } from "@posthog/shared" ;
3- import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events" ;
3+ import {
4+ ANALYTICS_EVENTS ,
5+ type SidebarNavItem ,
6+ } from "@posthog/shared/analytics-events" ;
47import { HOME_TAB_FLAG } from "@posthog/shared/constants" ;
58import { useCommandCenterStore } from "@posthog/ui/features/command-center/commandCenterStore" ;
69import { useFeatureFlag } from "@posthog/ui/features/feature-flags/useFeatureFlag" ;
710import { useInboxAllReports } from "@posthog/ui/features/inbox/hooks/useInboxAllReports" ;
11+ import {
12+ MORE_NAV_ITEMS ,
13+ type MoreNavItemId ,
14+ } from "@posthog/ui/features/sidebar/constants" ;
815import { useSidebarStore } from "@posthog/ui/features/sidebar/sidebarStore" ;
916import { useTasks } from "@posthog/ui/features/tasks/useTasks" ;
1017import {
@@ -27,12 +34,16 @@ import { useCommandMenuStore } from "@posthog/ui/shell/commandMenuStore";
2734import { Box , Flex } from "@radix-ui/themes" ;
2835import { useRouterState } from "@tanstack/react-router" ;
2936import { SquircleDashed } from "lucide-react" ;
37+ import { useState } from "react" ;
38+ import { CustomizeSidebarDialog } from "./CustomizeSidebarDialog" ;
3039import { ActivityItem } from "./items/ActivityItem" ;
3140import { AgentsItem } from "./items/AgentsItem" ;
3241import { CommandCenterItem } from "./items/CommandCenterItem" ;
42+ import { CustomizeSidebarItem } from "./items/CustomizeSidebarItem" ;
3343import { HomeItem } from "./items/HomeItem" ;
3444import { InboxItem } from "./items/InboxItem" ;
3545import { McpServersItem } from "./items/McpServersItem" ;
46+ import { MoreItem } from "./items/MoreItem" ;
3647import { NewTaskItem } from "./items/NewTaskItem" ;
3748import { SearchItem } from "./items/SearchItem" ;
3849import { SkillsItem } from "./items/SkillsItem" ;
@@ -54,7 +65,9 @@ interface SidebarNavSectionProps {
5465// into either layout. In the Channels space, destinations with a /website
5566// mirror (Home, Skills, MCP servers, Command Center) stay in that space;
5667// Inbox, Agents and New task have no mirror yet and jump back to Code.
57- // Search opens the command menu in place.
68+ // Search opens the command menu in place. Search, Skills and MCP servers are
69+ // tucked under the collapsible More row by default; the Customize sidebar
70+ // dialog promotes them back to the top level.
5871export function SidebarNavSection ( {
5972 commandCenterActiveCount : providedActiveCount ,
6073} : SidebarNavSectionProps = { } ) {
@@ -133,50 +146,149 @@ export function SidebarNavSection({
133146
134147 const openCommandMenu = useCommandMenuStore ( ( s ) => s . open ) ;
135148
149+ // Every nav row reports which item was clicked so per-item usage is
150+ // measurable; in_more distinguishes clicks inside the expanded More section.
151+ const withNavTrack =
152+ ( item : SidebarNavItem , action : ( ) => void , inMore = false ) =>
153+ ( ) => {
154+ track ( ANALYTICS_EVENTS . SIDEBAR_NAV_ITEM_CLICKED , {
155+ item,
156+ in_more : inMore ,
157+ } ) ;
158+ action ( ) ;
159+ } ;
160+
161+ const hiddenNavItems = useSidebarStore ( ( s ) => s . hiddenNavItems ) ;
162+ const hidden = new Set < MoreNavItemId > ( hiddenNavItems ) ;
163+ const [ moreExpanded , setMoreExpanded ] = useState ( false ) ;
164+ const [ customizeOpen , setCustomizeOpen ] = useState ( false ) ;
165+
166+ // While More is collapsed, an active item hidden under it takes over the
167+ // More row so the current page stays visible in the nav.
168+ const moreItemActive : Record < MoreNavItemId , boolean > = {
169+ search : false ,
170+ skills : isSkillsActive ,
171+ "mcp-servers" : isMcpServersActive ,
172+ } ;
173+ const activeHiddenItem = MORE_NAV_ITEMS . find (
174+ ( { id } ) => hidden . has ( id ) && moreItemActive [ id ] ,
175+ ) ;
176+ const takeoverLabel =
177+ ! moreExpanded && activeHiddenItem ? activeHiddenItem . label : null ;
178+
136179 return (
137180 < Flex direction = "column" className = "shrink-0 gap-px px-2 py-2" >
138181 < Box mb = "2" >
139- < NewTaskItem isActive = { isHomeActive } onClick = { goNewTask } />
182+ < NewTaskItem
183+ isActive = { isHomeActive }
184+ onClick = { withNavTrack ( "new_task" , goNewTask ) }
185+ />
140186 </ Box >
141187
142188 { homeTabEnabled && (
143189 < Box >
144- < HomeItem isActive = { isHomeViewActive } onClick = { goHome } />
190+ < HomeItem
191+ isActive = { isHomeViewActive }
192+ onClick = { withNavTrack ( "home" , goHome ) }
193+ />
145194 </ Box >
146195 ) }
147196
148- < Box >
149- < SearchItem onClick = { openCommandMenu } />
150- </ Box >
197+ { ! hidden . has ( "search" ) && (
198+ < Box >
199+ < SearchItem onClick = { withNavTrack ( "search" , openCommandMenu ) } />
200+ </ Box >
201+ ) }
151202
152203 < Box >
153204 < InboxItem
154205 isActive = { isInboxActive }
155- onClick = { navigateToInbox }
206+ onClick = { withNavTrack ( "inbox" , navigateToInbox ) }
156207 pullRequestCount = { inboxPullRequestCount }
157208 />
158209 </ Box >
159210
160211 < Box >
161- < AgentsItem isActive = { isAgentsActive } onClick = { navigateToAgents } />
212+ < AgentsItem
213+ isActive = { isAgentsActive }
214+ onClick = { withNavTrack ( "agents" , navigateToAgents ) }
215+ />
162216 </ Box >
163217
164- < Box >
165- < SkillsItem isActive = { isSkillsActive } onClick = { goSkills } />
166- </ Box >
218+ { ! hidden . has ( "skills" ) && (
219+ < Box >
220+ < SkillsItem
221+ isActive = { isSkillsActive }
222+ onClick = { withNavTrack ( "skills" , goSkills ) }
223+ />
224+ </ Box >
225+ ) }
167226
168- < Box >
169- < McpServersItem isActive = { isMcpServersActive } onClick = { goMcpServers } />
170- </ Box >
227+ { ! hidden . has ( "mcp-servers" ) && (
228+ < Box >
229+ < McpServersItem
230+ isActive = { isMcpServersActive }
231+ onClick = { withNavTrack ( "mcp_servers" , goMcpServers ) }
232+ />
233+ </ Box >
234+ ) }
171235
172- < Box mb = { bluebirdEnabled ? undefined : "2" } >
236+ < Box >
173237 < CommandCenterItem
174238 isActive = { isCommandCenterActive }
175- onClick = { goCommandCenter }
239+ onClick = { withNavTrack ( "command_center" , goCommandCenter ) }
176240 activeCount = { commandCenterActiveCount }
177241 />
178242 </ Box >
179243
244+ { /* Everything the user shoved off the top level lives here, plus the
245+ Customize entry point. Collapsed by default; when a hidden item is
246+ the active page it takes over the More row (see takeoverLabel). */ }
247+ < Flex
248+ direction = "column"
249+ className = "gap-px"
250+ mb = { bluebirdEnabled ? undefined : "2" }
251+ >
252+ < MoreItem
253+ expanded = { moreExpanded }
254+ activeItemLabel = { takeoverLabel }
255+ onClick = { withNavTrack ( "more" , ( ) => setMoreExpanded ( ( e ) => ! e ) ) }
256+ />
257+
258+ { moreExpanded && (
259+ < >
260+ { hidden . has ( "search" ) && (
261+ < SearchItem
262+ depth = { 1 }
263+ onClick = { withNavTrack ( "search" , openCommandMenu , true ) }
264+ />
265+ ) }
266+ { hidden . has ( "skills" ) && (
267+ < SkillsItem
268+ depth = { 1 }
269+ isActive = { isSkillsActive }
270+ onClick = { withNavTrack ( "skills" , goSkills , true ) }
271+ />
272+ ) }
273+ { hidden . has ( "mcp-servers" ) && (
274+ < McpServersItem
275+ depth = { 1 }
276+ isActive = { isMcpServersActive }
277+ onClick = { withNavTrack ( "mcp_servers" , goMcpServers , true ) }
278+ />
279+ ) }
280+ < CustomizeSidebarItem
281+ depth = { 1 }
282+ onClick = { withNavTrack (
283+ "customize_sidebar" ,
284+ ( ) => setCustomizeOpen ( true ) ,
285+ true ,
286+ ) }
287+ />
288+ </ >
289+ ) }
290+ </ Flex >
291+
180292 { /* "Channels" is a toggle laid out as a nav row: the # label and Alpha
181293 badge on the left, a Switch on the right. It flips the channels
182294 feature rather than routing — enabling it reveals the Canvas row
@@ -199,6 +311,10 @@ export function SidebarNavSection({
199311 checked = { channelsEnabled }
200312 onCheckedChange = { ( checked ) => {
201313 setChannelsEnabled ( checked ) ;
314+ track ( ANALYTICS_EVENTS . SIDEBAR_NAV_ITEM_CLICKED , {
315+ item : "contexts" ,
316+ in_more : false ,
317+ } ) ;
202318 track ( ANALYTICS_EVENTS . CHANNEL_ACTION , {
203319 action_type : "toggle_channels" ,
204320 surface : "nav" ,
@@ -222,10 +338,15 @@ export function SidebarNavSection({
222338 < Box >
223339 < ActivityItem
224340 isActive = { isActivityActive }
225- onClick = { navigateToActivity }
341+ onClick = { withNavTrack ( "activity" , navigateToActivity ) }
226342 />
227343 </ Box >
228344 ) }
345+
346+ < CustomizeSidebarDialog
347+ open = { customizeOpen }
348+ onOpenChange = { setCustomizeOpen }
349+ />
229350 </ Flex >
230351 ) ;
231352}
0 commit comments