feat(app): sidebar matches the plain mockup

Flame logo + orange CodeBurn wordmark, an outline icon on every nav item
(active icon in accent), and an About + GitHub/Discord/X/YouTube social
footer replacing the price status line (per the approved design). Bundles
the flame asset. typecheck + 82 tests green.
This commit is contained in:
iamtoruk 2026-07-11 08:58:16 -07:00
parent 6775592716
commit 3ebeb38d67
3 changed files with 51 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -1,20 +1,40 @@
import type { ReactNode } from 'react'
import flameLogo from '../assets/flame.png'
export type Section = 'overview' | 'spend' | 'optimize' | 'models' | 'plans' | 'settings'
export const NAV_ITEMS: Array<{ id: Section; label: string; key: string }> = [
{ id: 'overview', label: 'Overview', key: '⌘1' },
{ id: 'spend', label: 'Spend', key: '⌘2' },
{ id: 'optimize', label: 'Optimize', key: '⌘3' },
{ id: 'models', label: 'Models', key: '⌘4' },
{ id: 'plans', label: 'Plans', key: '⌘5' },
{ id: 'settings', label: 'Settings', key: '⌘,' },
export const NAV_ITEMS: Array<{ id: Section; label: string; key: string; icon: ReactNode }> = [
{ id: 'overview', label: 'Overview', key: '⌘1', icon: (
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="9" rx="1" /><rect x="14" y="3" width="7" height="5" rx="1" /><rect x="14" y="12" width="7" height="9" rx="1" /><rect x="3" y="16" width="7" height="5" rx="1" /></svg>
) },
{ id: 'spend', label: 'Spend', key: '⌘2', icon: (
<svg viewBox="0 0 24 24"><line x1="6" y1="20" x2="6" y2="13" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="18" y1="20" x2="18" y2="9" /></svg>
) },
{ id: 'optimize', label: 'Optimize', key: '⌘3', icon: (
<svg viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" /></svg>
) },
{ id: 'models', label: 'Models', key: '⌘4', icon: (
<svg viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.7l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.7l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /><path d="M3.3 7 12 12l8.7-5M12 22V12" /></svg>
) },
{ id: 'plans', label: 'Plans', key: '⌘5', icon: (
<svg viewBox="0 0 24 24"><rect x="2" y="5" width="20" height="14" rx="2" /><line x1="2" y1="10" x2="22" y2="10" /></svg>
) },
{ id: 'settings', label: 'Settings', key: '⌘,', icon: (
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /></svg>
) },
]
const SOCIALS: Array<{ label: string; icon: ReactNode }> = [
{ label: 'GitHub', icon: <svg viewBox="0 0 24 24"><path d="M12 .5C5.37.5 0 5.87 0 12.5c0 5.3 3.44 9.8 8.21 11.39.6.11.82-.26.82-.58 0-.29-.01-1.05-.02-2.06-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.5.99.11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.29-1.55 3.3-1.23 3.3-1.23.66 1.66.24 2.88.12 3.18.77.84 1.23 1.91 1.23 3.22 0 4.61-2.8 5.62-5.48 5.92.43.37.81 1.1.81 2.22 0 1.6-.01 2.9-.01 3.29 0 .32.22.7.83.58A12 12 0 0 0 24 12.5C24 5.87 18.63.5 12 .5z" /></svg> },
{ label: 'Discord', icon: <svg viewBox="0 0 24 24"><path d="M20.32 4.37A19.8 19.8 0 0 0 15.45 3c-.21.38-.46.9-.63 1.31a18.3 18.3 0 0 0-5.47 0C8.71 3.9 8.45 3.38 8.24 3a19.7 19.7 0 0 0-4.88 1.37C.86 8.75.05 13.02.45 17.23a19.9 19.9 0 0 0 6 3.03c.48-.66.91-1.36 1.28-2.11-.7-.26-1.37-.58-2-.96.17-.12.33-.25.49-.38a14.2 14.2 0 0 0 12.16 0c.16.14.32.26.49.38-.63.38-1.31.7-2 .96.37.75.8 1.45 1.28 2.11a19.8 19.8 0 0 0 6-3.03c.47-4.87-.8-9.1-3.83-12.86zM8.02 14.65c-1.18 0-2.15-1.08-2.15-2.41 0-1.33.95-2.42 2.15-2.42 1.2 0 2.17 1.09 2.15 2.42 0 1.33-.95 2.41-2.15 2.41zm7.96 0c-1.18 0-2.15-1.08-2.15-2.41 0-1.33.95-2.42 2.15-2.42 1.2 0 2.17 1.09 2.15 2.42 0 1.33-.95 2.41-2.15 2.41z" /></svg> },
{ label: 'X', icon: <svg viewBox="0 0 24 24"><path d="M18.9 1.15h3.68l-8.04 9.19L24 22.85h-7.4l-5.8-7.58-6.63 7.58H.49l8.6-9.83L0 1.15h7.59l5.24 6.93 6.07-6.93zm-1.29 19.5h2.04L6.49 3.24H4.3l13.31 17.41z" /></svg> },
{ label: 'YouTube', icon: <svg viewBox="0 0 24 24"><path d="M23.5 6.2a3.02 3.02 0 0 0-2.12-2.14C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.38.56A3.02 3.02 0 0 0 .5 6.2 31.5 31.5 0 0 0 0 12a31.5 31.5 0 0 0 .5 5.8 3.02 3.02 0 0 0 2.12 2.14C4.5 20.5 12 20.5 12 20.5s7.5 0 9.38-.56A3.02 3.02 0 0 0 23.5 17.8 31.5 31.5 0 0 0 24 12a31.5 31.5 0 0 0-.5-5.8zM9.55 15.57V8.43L15.82 12l-6.27 3.57z" /></svg> },
]
export function Sidebar({
active,
onNavigate,
status,
}: {
active: Section
onNavigate: (section: Section) => void
@ -22,8 +42,7 @@ export function Sidebar({
}) {
return (
<nav className="sb">
<div className="lights"><i /><i /><i /></div>
<div className="app"><b>CodeBurn</b></div>
<div className="app"><img className="logo" src={flameLogo} alt="" /><b>CodeBurn</b></div>
{NAV_ITEMS.map(item => (
<div
key={item.id}
@ -35,12 +54,20 @@ export function Sidebar({
if (e.key === 'Enter' || e.key === ' ') onNavigate(item.id)
}}
>
{item.icon}
{item.label}
<span className="k">{item.key}</span>
</div>
))}
<div className="push" />
<div className="status">{status}</div>
<div className="foot">
<a className="about">About</a>
<div className="social">
{SOCIALS.map(social => (
<a key={social.label} title={social.label} aria-label={social.label}>{social.icon}</a>
))}
</div>
</div>
</nav>
)
}

View file

@ -102,6 +102,19 @@ body { overflow: hidden; background: var(--bg); color: var(--ink); }
.lights { display: none; }
.sb { padding-top: 12px; }
/* Sidebar brand (flame logo + wordmark), nav icons, and About/social footer. */
.app { gap: 6px; }
.app .logo { width: 20px; height: 20px; object-fit: contain; flex: 0 0 auto; }
.ni svg { width: 16px; height: 16px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ni.on svg { stroke: var(--accent); }
.foot { display: flex; align-items: center; gap: 10px; margin-top: 4px; padding: 11px 8px 2px; border-top: 1px solid var(--hair); }
.foot .about { color: var(--t2); text-decoration: none; font-size: 12px; font-weight: 520; cursor: pointer; }
.foot .about:hover { color: var(--t1); }
.foot .social { display: flex; gap: 2px; margin-left: auto; }
.foot .social a { color: var(--t3); display: flex; padding: 5px; border-radius: 6px; cursor: pointer; }
.foot .social a:hover { color: var(--accent); background: var(--hover); }
.foot .social svg { width: 15px; height: 15px; fill: currentColor; stroke: none; }
/* macOS integrated title bar (hiddenInset): clear the traffic lights and make
the top chrome draggable, keeping interactive controls clickable. */
:root[data-platform='darwin'] .sb { padding-top: 34px; -webkit-app-region: drag; }