**Scope:** Design system refactor + server UI migration
## Problem
The current `Sidebar` component in `@cameleer/design-system` is monolithic — it hardcodes three navigation sections (Applications, Agents, Routes), a starred section, bottom links (Admin, API Docs), and all tree-building logic. This makes it impossible for the consuming application to:
1. Add new sections (e.g., Admin sub-pages) without modifying the DS
2. Control section ordering or visibility based on application state
3. Implement accordion behavior (expanding one section collapses others)
4. Collapse the sidebar to an icon rail
## Solution
Refactor the Sidebar into a **composable compound component** where the DS provides the shell and building blocks, and the consuming application controls all content.
**Search state ownership:** The DS renders the search input and calls `onSearchChange` on every keystroke. The consuming app owns the state (`filterQuery`) and passes it to each `SidebarTree`. This lets the app control filtering behavior (e.g., clear search on section switch, filter only certain sections).
- When collapsed, clicking a section icon fires both `onCollapseToggle` and that section's `onToggle` simultaneously. The sidebar expands and the section opens in one motion. No navigation occurs — the user clicks a tree item after the section is visible to navigate.
| `active` | `boolean` | auto | Override active state highlight. Auto-detected if omitted (any descendant matches current path). |
**Expanded state:**
```
v APPLICATIONS <- clickable header, chevron rotates
> backend-app 3.4k
> caller-app 891
> sample-app 9.6k
```
**Collapsed state:**
```
> APPLICATIONS <- single line, clickable
```
**In sidebar collapsed (icon rail) mode:**
```
[B] <- icon only, tooltip "Applications"
```
Header styling: uppercase label, muted color, chevron left of label, icon left of chevron. Active section gets amber accent (same as current active highlighting pattern).
#### `<Sidebar.Footer>`
Pinned to the bottom of the sidebar. Renders children (typically `Sidebar.FooterLink` items).
In collapsed mode, footer links render as centered icons.
#### `<Sidebar.FooterLink>`
A single bottom link item.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `icon` | `ReactNode` | - | Link icon |
| `label` | `string` | - | Link text (hidden when sidebar collapsed, shown as tooltip) |
| `onClick` | `() => void` | - | Click handler |
| `active` | `boolean` | `false` | Active state highlight |
#### `<SidebarTree>` (unchanged, newly exported)
The existing `SidebarTree` component stays as-is. It already accepts all its data via props (`nodes`, `selectedPath`, `filterQuery`, `onNavigate`, `persistKey`, `autoRevealPath`, `isStarred`, `onToggleStar`). It just needs to be exported from the package.
The `SidebarTreeNode` type is also exported so consuming apps can build tree data.
The existing `useStarred` hook stays as-is. Export it so the consuming app can pass `isStarred`/`onToggleStar` to `SidebarTree`.
### What Gets Removed from DS
The current monolithic `Sidebar` component contains ~300 lines of application-specific logic that moves to the server UI:
1.**Tree-building functions**: `buildAppTreeNodes()`, `buildRouteTreeNodes()`, `buildAgentTreeNodes()` — these transform `SidebarApp[]` into `SidebarTreeNode[]`. They move to the server UI.
2.**Starred section rendering**: `collectStarredItems()` and `StarredGroup` — starred items become a regular `Sidebar.Section` in the server UI (or inline within sections via `SidebarTree`'s existing star support).
3.**Hardcoded bottom links**: Admin and API Docs links — move to `Sidebar.Footer` + `Sidebar.FooterLink` in server UI.
4.**Section collapse state management**: localStorage persistence of `cameleer:sidebar:*-collapsed` — moves to server UI.
5.**Auto-reveal logic**: `sidebarRevealPath` effect that auto-expands sections — moves to server UI.
6.**`SidebarApp` / `SidebarRoute` / `SidebarAgent` types**: These are application-domain types, not DS types. Move to server UI. DS only exports `SidebarTreeNode`.
### New DS Exports
```typescript
// layout/index.ts — updated exports
export { Sidebar } from './Sidebar/Sidebar'
export { SidebarTree } from './Sidebar/SidebarTree'
export type { SidebarTreeNode } from './Sidebar/SidebarTree'
export { useStarred } from './Sidebar/useStarred'
```
The `SidebarApp`, `SidebarRoute`, `SidebarAgent` type exports are removed (they move to the consuming application).
## Server UI Migration
### LayoutShell.tsx Changes
The current `LayoutShell` already does most of the data preparation (building `sidebarApps`, handling `handleSidebarNavigate`). After migration:
1. Move tree-building functions (`buildAppTreeNodes`, etc.) from DS into a local `sidebar-utils.ts`
2. Manage section collapse states with localStorage persistence
3. Implement accordion logic: when on `/admin/*`, expand Admin section and collapse operational sections; when navigating away, restore previous states
4. Pass `filterQuery` from search to each `SidebarTree`
5. Compose the new `<Sidebar>` with sections
### AdminLayout.tsx Changes
Remove the `<Tabs>` navigation — sidebar now handles admin sub-page navigation. Keep just the content wrapper:
```tsx
export default function AdminLayout() {
return (
<div style={{ padding: '20px 24px 40px' }}>
<Outlet />
</div>
);
}
```
### ContentTabs
Still hidden on admin pages (existing `isAdminPage` guard). The tab strip is irrelevant when the sidebar shows the admin context.
### TopBar
No changes. Stays visible on all pages.
## Accordion Behavior (Server UI Logic)
The accordion is not a DS concept — it's application logic in the server UI:
```typescript
// When navigating to /admin/*:
// 1. Remember current operational collapse states
// 2. Collapse all operational sections
// 3. Expand Admin section
// When navigating away from /admin/*:
// 1. Collapse Admin section
// 2. Restore operational collapse states from memory
```
This means the DS sections are purely controlled components. The app decides which are open/closed based on current route.