Draw the sidebar with Reicon glyphs

Navigation icons now come from reicon-react (tree-shaken SVG components,
currentColor by default): Home, Briefcase, Users, ChatRoundDots, Send,
Folder, ShieldCheck, DocumentText2, Plug2, Buildings2, and Gear map to their
routes through one NavIcon component, so the glyph set lives in a single
place and NavSpec no longer carries per-item icon names.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-06 23:02:33 -04:00
1 parent a11d2dfa07
commit 4775b23d94
4 files changed
+73 -14

No files matched your search

+1
View File
@@ -34,6 +34,7 @@
"lucide-react": "0.468.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"reicon-react": "^1.1.2",
"zod": "4.3.6"
},
"devDependencies": {
+25
View File
@@ -23,6 +23,9 @@ importers:
react-dom:
specifier: 19.2.4
version: 19.2.4(react@19.2.4)
reicon-react:
specifier: ^1.1.2
version: 1.1.2(react@19.2.4)
zod:
specifier: 4.3.6
version: 4.3.6
@@ -707,66 +710,79 @@ packages:
resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.60.4':
resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.60.4':
resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.60.4':
resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.60.4':
resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.60.4':
resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.60.4':
resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.60.4':
resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.60.4':
resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.60.4':
resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.60.4':
resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.60.4':
resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.60.4':
resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.60.4':
resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==}
@@ -2164,6 +2180,11 @@ packages:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
reicon-react@1.1.2:
resolution: {integrity: sha512-dbp1CPEPKFjWfG41lGRMWi74YKfKJlb2JcB/TRBAUQF6jalZYW1dRZisRPCpze5TfQwRBD30p40qnoY+oYi7MQ==}
peerDependencies:
react: '>=16.8.0'
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -4824,6 +4845,10 @@ snapshots:
string_decoder: 1.1.1
util-deprecate: 1.0.2
reicon-react@1.1.2(react@19.2.4):
dependencies:
react: 19.2.4
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@@ -0,0 +1,34 @@
import {
Briefcase,
Buildings2,
ChatRoundDots,
DocumentText2,
Folder,
Gear,
Home,
Plug2,
Send,
ShieldCheck,
Users,
} from "reicon-react";
import type { RouteId } from "./Shell";
/** One glyph per navigation surface, from the Reicon set. */
const NAV_ICONS: Record<RouteId, typeof Home> = {
home: Home,
work: Briefcase,
team: Users,
chat: ChatRoundDots,
runs: Send,
files: Folder,
approvals: ShieldCheck,
docs: DocumentText2,
social: Plug2,
office: Buildings2,
settings: Gear,
};
export function NavIcon({ route, size = 18 }: { route: RouteId; size?: number }) {
const Glyph = NAV_ICONS[route];
return <Glyph size={size} />;
}
@@ -1,7 +1,7 @@
import { useEffect, useState, type ReactNode } from "react";
import type { AppSnapshot } from "../../../shared/domain";
import { Avatar, Icon, IconButton } from "./ui";
import type { IconName } from "./Icon";
import { NavIcon } from "./NavIcon";
import { OrgSwitcher } from "./OrgSwitcher";
import { SearchDialog } from "./SearchDialog";
import { TopbarSlotContext } from "./topbar-context";
@@ -22,7 +22,6 @@ export type RouteId =
interface NavSpec {
id: RouteId;
label: string;
icon: IconName;
}
/** Routes whose pages own a full-height, multi-pane layout with their own
@@ -31,20 +30,20 @@ const FLUSH_ROUTES = new Set<RouteId>(["chat", "settings", "files", "social", "r
/** The daily-driver surfaces — the core operating loop. */
const primaryNav: NavSpec[] = [
{ id: "home", label: "Home", icon: "home" },
{ id: "work", label: "Work", icon: "work" },
{ id: "team", label: "Team", icon: "team" },
{ id: "chat", label: "Messages", icon: "chat" },
{ id: "runs", label: "Runs", icon: "send" },
{ id: "files", label: "Files", icon: "folder" },
{ id: "approvals", label: "Approvals", icon: "approve" },
{ id: "home", label: "Home" },
{ id: "work", label: "Work" },
{ id: "team", label: "Team" },
{ id: "chat", label: "Messages" },
{ id: "runs", label: "Runs" },
{ id: "files", label: "Files" },
{ id: "approvals", label: "Approvals" },
];
/** Less-frequent surfaces, demoted under a divider so the primary set leads. */
const secondaryNav: NavSpec[] = [
{ id: "docs", label: "Documents", icon: "file" },
{ id: "social", label: "Social", icon: "social" },
{ id: "office", label: "Office", icon: "team" },
{ id: "docs", label: "Documents" },
{ id: "social", label: "Social" },
{ id: "office", label: "Office" },
];
const allNav: NavSpec[] = [...primaryNav, ...secondaryNav];
@@ -123,7 +122,7 @@ export function Shell({
className={`nav-item ${route === "settings" ? "active" : ""}`.trim()}
onClick={() => setRoute("settings")}
>
<span className="icon-box"><Icon name="settings" size={18} /></span>
<span className="icon-box"><NavIcon route="settings" /></span>
<span>Settings</span>
</button>
</nav>
@@ -184,7 +183,7 @@ function NavButton({
className={`nav-item ${active ? "active" : ""}`.trim()}
onClick={() => onSelect(item.id)}
>
<span className="icon-box"><Icon name={item.icon} size={18} /></span>
<span className="icon-box"><NavIcon route={item.id} /></span>
<span>{item.label}</span>
{renderBadge(badge)}
</button>