feat: redesign Sidebar with hierarchical trees, starring, and collapsible sections

Replace flat app/route/agent lists with expandable tree navigation.
Apps contain their routes and agents hierarchically. Add localStorage-
backed starring with composite keys for uniqueness. Persist expand
state to sessionStorage across page navigations. Add collapsible
section headers, remove button on starred items, and parent app
context labels. Create stub pages for /apps/:id, /agents/:id,
/admin, /api-docs. Consolidate duplicated sidebar data into
shared mock. Widen sidebar from 220px to 260px.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 17:50:41 +01:00
parent 4aeb5be6ab
commit e69e5ab5fe
23 changed files with 1809 additions and 484 deletions

View File

@@ -1,5 +1,5 @@
.sidebar {
width: 220px;
width: 260px;
flex-shrink: 0;
background: var(--sidebar-bg);
display: flex;
@@ -102,7 +102,7 @@
padding: 0 6px;
}
/* Nav item */
/* Nav item (flat links like Dashboards) */
.item {
display: flex;
align-items: center;
@@ -129,16 +129,6 @@
border-left-color: var(--amber);
}
.item.active .itemCount {
background: rgba(198, 130, 14, 0.2);
color: var(--amber-light);
}
/* Indented route items */
.indented {
padding-left: 22px;
}
.navIcon {
font-size: 14px;
width: 18px;
@@ -154,6 +144,7 @@
.routeArrow {
color: var(--sidebar-muted);
font-size: 10px;
flex-shrink: 0;
}
/* Item sub-elements */
@@ -169,143 +160,278 @@
text-overflow: ellipsis;
}
.itemMeta {
font-size: 11px;
/* No results */
.noResults {
padding: 16px 18px;
font-size: 12px;
color: var(--sidebar-muted);
font-family: var(--font-mono);
text-align: center;
}
.itemCount {
font-family: var(--font-mono);
font-size: 11px;
color: var(--sidebar-muted);
background: rgba(255, 255, 255, 0.06);
padding: 1px 6px;
border-radius: 10px;
flex-shrink: 0;
/* ── SidebarTree styles ──────────────────────────────────────────────────── */
.treeSection {
padding: 0 6px;
margin-bottom: 4px;
}
/* Health dots */
.healthDot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
.healthLive {
background: #5db866;
box-shadow: 0 0 6px rgba(93, 184, 102, 0.4);
}
.healthStale {
background: var(--warning);
}
.healthDead {
background: var(--sidebar-muted);
}
/* Divider */
.divider {
height: 1px;
background: rgba(255, 255, 255, 0.06);
margin: 6px 12px;
}
/* Agents header */
.agentsHeader {
padding: 14px 12px 6px;
.treeSectionLabel {
padding: 10px 12px 4px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.2px;
letter-spacing: 1px;
color: var(--sidebar-muted);
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.agentBadge {
font-family: var(--font-mono);
/* Collapsible section toggle */
.treeSectionToggle {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
padding: 8px 12px 4px;
font-size: 10px;
padding: 1px 6px;
border-radius: 10px;
background: rgba(93, 184, 102, 0.15);
color: #5db866;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--sidebar-muted);
background: none;
border: none;
cursor: pointer;
text-align: left;
transition: color 0.12s;
}
/* Agents list */
.agentsList {
padding: 0 0 6px;
overflow-y: auto;
max-height: 180px;
flex-shrink: 0;
.treeSectionToggle:hover {
color: var(--sidebar-text);
}
.agentItem {
.treeSectionChevron {
font-size: 9px;
width: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.tree {
list-style: none;
margin: 0;
padding: 0;
}
.treeGroup {
list-style: none;
margin: 0;
padding: 0;
}
.treeRow {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
margin: 0 6px 2px;
gap: 6px;
padding: 5px 8px;
border-radius: var(--radius-sm);
font-size: 11px;
color: var(--sidebar-text);
transition: background 0.1s;
font-size: 12px;
cursor: pointer;
transition: background 0.12s;
border-left: 3px solid transparent;
margin-bottom: 1px;
user-select: none;
position: relative;
}
.agentItem:hover {
.treeRow:hover {
background: var(--sidebar-hover);
}
.agentDot {
width: 6px;
height: 6px;
border-radius: 50%;
flex-shrink: 0;
.treeRowActive {
background: var(--sidebar-active);
color: var(--amber-light);
border-left-color: var(--amber);
}
.agentInfo {
.treeRowActive .treeBadge {
background: rgba(198, 130, 14, 0.2);
color: var(--amber-light);
}
/* Chevron */
.treeChevronSlot {
width: 14px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.treeChevron {
background: none;
border: none;
padding: 0;
margin: 0;
color: var(--sidebar-muted);
font-size: 11px;
cursor: pointer;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
}
.treeChevron:hover {
color: var(--sidebar-text);
}
/* Icon slot */
.treeIcon {
flex-shrink: 0;
display: flex;
align-items: center;
}
/* Label */
.treeLabel {
flex: 1;
min-width: 0;
}
.agentName {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.agentDetail {
font-size: 10px;
color: var(--sidebar-muted);
}
.agentStats {
text-align: right;
/* Badge */
.treeBadge {
font-family: var(--font-mono);
font-size: 10px;
color: var(--sidebar-muted);
background: rgba(255, 255, 255, 0.06);
padding: 1px 6px;
border-radius: 10px;
flex-shrink: 0;
white-space: nowrap;
}
.agentTps {
color: var(--sidebar-text);
}
.agentLastSeen {
/* Star button */
.treeStar {
background: none;
border: none;
padding: 0;
margin: 0;
color: var(--sidebar-muted);
cursor: pointer;
opacity: 0;
transition: opacity 0.15s, color 0.15s;
display: flex;
align-items: center;
flex-shrink: 0;
}
.agentError {
.treeStarActive {
opacity: 1;
color: var(--amber);
}
.treeRow:hover .treeStar {
opacity: 1;
}
.treeStar:hover {
color: var(--amber-light);
}
/* ── Starred section ─────────────────────────────────────────────────────── */
.starredSection {
border-top: 1px solid rgba(255, 255, 255, 0.06);
margin-top: 4px;
}
.starredHeader {
color: var(--amber);
}
.starredList {
padding: 0 6px 6px;
}
.starredGroup {
margin-bottom: 4px;
}
.starredGroupLabel {
padding: 4px 12px 2px;
font-size: 10px;
color: var(--sidebar-muted);
font-weight: 500;
}
.starredItem {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 12px;
border-radius: var(--radius-sm);
color: var(--sidebar-text);
font-size: 12px;
cursor: pointer;
transition: background 0.12s;
user-select: none;
}
.starredItem:hover {
background: var(--sidebar-hover);
}
.starredItemInfo {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.starredItemName {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
}
.starredItemContext {
font-size: 10px;
color: var(--sidebar-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Remove button */
.starredRemove {
background: none;
border: none;
padding: 2px;
margin: 0;
color: var(--sidebar-muted);
cursor: pointer;
opacity: 0;
transition: opacity 0.15s, color 0.15s;
display: flex;
align-items: center;
flex-shrink: 0;
}
.starredItem:hover .starredRemove {
opacity: 1;
}
.starredRemove:hover {
color: var(--error);
}
/* Bottom links */
/* ── Bottom links ────────────────────────────────────────────────────────── */
.bottom {
border-top: 1px solid rgba(255, 255, 255, 0.06);
padding: 6px;
@@ -331,6 +457,12 @@
color: var(--sidebar-text);
}
.bottomItemActive {
background: var(--sidebar-active);
color: var(--amber-light);
border-left-color: var(--amber);
}
.bottomIcon {
font-size: 13px;
width: 18px;