updated design

This commit is contained in:
SinachPat
2026-04-25 22:03:25 +01:00
parent dbe7c8cbb0
commit df79ae0bca
8 changed files with 2581 additions and 785 deletions
+3 -1
View File
@@ -14,7 +14,9 @@
"Bash(curl -s \"https://github.githubassets.com/assets/primer-primitives-0f570a01cbe448fb.css\")", "Bash(curl -s \"https://github.githubassets.com/assets/primer-primitives-0f570a01cbe448fb.css\")",
"Bash(pnpm --filter @originmain/app run build)", "Bash(pnpm --filter @originmain/app run build)",
"Bash(pnpm --filter @originmain/app install)", "Bash(pnpm --filter @originmain/app install)",
"Bash(pnpm add *)" "Bash(pnpm add *)",
"Bash(xargs ls *)",
"Bash(grep -E \"\\\\.\\(ts|tsx\\)$\")"
] ]
} }
} }
+1744 -562
View File
File diff suppressed because it is too large Load Diff
+182 -35
View File
@@ -20,69 +20,156 @@ export function Artboard({ id, label, x, y, width, height }: ArtboardProps) {
style={{ position: 'absolute', top: y, left: x }} style={{ position: 'absolute', top: y, left: x }}
onClick={(e) => { e.stopPropagation(); selectArtboard(id); }} onClick={(e) => { e.stopPropagation(); selectArtboard(id); }}
> >
{/* Label above artboard */} {/* Label */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: -22, top: -24,
left: 0, left: 0,
fontSize: 11, fontSize: 11,
fontFamily: 'monospace', fontFamily: "'JetBrains Mono', 'SF Mono', ui-monospace, monospace",
color: selected ? '#0F52BA' : 'rgba(255,255,255,0.4)', fontWeight: selected ? 500 : 400,
color: selected ? '#3385FF' : 'rgba(255,255,255,0.35)',
userSelect: 'none', userSelect: 'none',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
letterSpacing: '-0.01em',
transition: 'color 0.15s',
}} }}
> >
{label} {label}
</div> </div>
{/* Artboard frame */} {/* Frame */}
<div <div
style={{ style={{
width, width,
height, height,
background: '#fff', background: '#FFFFFF',
borderRadius: 8, borderRadius: 8,
boxShadow: selected boxShadow: selected
? '0 0 0 2px #0F52BA, 0 8px 40px rgba(0,0,0,0.5)' ? '0 0 0 2px #3385FF, 0 12px 48px rgba(0,0,0,0.65)'
: '0 4px 24px rgba(0,0,0,0.4)', : '0 4px 28px rgba(0,0,0,0.55)',
overflow: 'hidden', overflow: 'hidden',
position: 'relative', position: 'relative',
cursor: 'default', cursor: selected ? 'default' : 'pointer',
transition: 'box-shadow 0.15s',
}} }}
> >
{/* Selection handles */} {/* Selection handles */}
{selected && ( {selected && (
<> <>
<Handle pos={{ top: -3, left: -3 }} /> <Handle pos={{ top: -4, left: -4 }} />
<Handle pos={{ top: -3, right: -3 }} /> <Handle pos={{ top: -4, right: -4 }} />
<Handle pos={{ bottom: -3, left: -3 }} /> <Handle pos={{ bottom: -4, left: -4 }} />
<Handle pos={{ bottom: -3, right: -3 }} /> <Handle pos={{ bottom: -4, right: -4 }} />
</> </>
)} )}
{/* Placeholder content */} {/* Per-artboard content */}
<ArtboardContent id={id} />
</div>
</div>
);
}
function ArtboardContent({ id }: { id: string }) {
switch (id) {
case 'dashboard-card': return <DashboardCard />;
case 'user-profile': return <UserProfile />;
case 'nav-sidebar': return <NavSidebar />;
case 'data-table': return <DataTable />;
default: return <Placeholder label={id} />;
}
}
// ── DashboardCard ──────────────────────────────────────────
function DashboardCard() {
return (
<div style={{ padding: 20 }}> <div style={{ padding: 20 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 14 }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<div style={{ fontSize: 11, fontWeight: 600, color: '#111', letterSpacing: '-0.01em' }}> <span style={{ fontSize: 11, fontWeight: 600, color: '#111', letterSpacing: '-0.01em' }}>Revenue Overview</span>
Revenue Overview <span style={{ fontSize: 9, background: '#ECFDF5', color: '#059669', padding: '2px 7px', borderRadius: 99, fontWeight: 600, fontFamily: 'monospace' }}>Live</span>
</div> </div>
<div style={{ fontSize: 9, background: '#ecfdf5', color: '#059669', padding: '2px 6px', borderRadius: 99, fontWeight: 600, fontFamily: 'monospace' }}> <div style={{ fontSize: 24, fontWeight: 800, color: '#0A0A0A', letterSpacing: '-0.045em', lineHeight: 1, marginBottom: 4 }}>$12,450</div>
Live <div style={{ fontSize: 10, color: '#059669', fontWeight: 500, marginBottom: 14, display: 'flex', alignItems: 'center', gap: 3 }}>
<span></span> +2.4% vs last month
</div> </div>
</div> <div style={{ height: 3, background: '#F0F0F0', borderRadius: 99, overflow: 'hidden', marginBottom: 14 }}>
<div style={{ fontSize: 22, fontWeight: 800, color: '#0c0c10', letterSpacing: '-0.04em', lineHeight: 1, marginBottom: 5 }}> <div style={{ height: '100%', width: '68%', background: 'linear-gradient(90deg, #0066FF, #3385FF)', borderRadius: 99 }} />
$12,450
</div>
<div style={{ fontSize: 10, color: '#16a34a', fontWeight: 500, marginBottom: 12 }}>
+2.4% vs last month
</div>
<div style={{ height: 3, background: '#f0f0f0', borderRadius: 99, overflow: 'hidden', marginBottom: 12 }}>
<div style={{ height: '100%', width: '68%', background: 'linear-gradient(90deg,#0F52BA,#4d84e0)', borderRadius: 99 }} />
</div> </div>
<div style={{ display: 'flex', gap: 4 }}> <div style={{ display: 'flex', gap: 4 }}>
<Chip>Q4 2024</Chip> <Tag>Q4 2024</Tag>
<Chip>MRR</Chip> <Tag>MRR</Tag>
<Tag>SaaS</Tag>
</div>
</div>
);
}
// ── UserProfile ────────────────────────────────────────────
function UserProfile() {
return (
<div style={{ padding: 20, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%' }}>
<div style={{ width: 52, height: 52, borderRadius: '50%', background: 'linear-gradient(135deg, #7C3AED, #0066FF)', marginBottom: 12 }} />
<div style={{ fontSize: 13, fontWeight: 700, color: '#0A0A0A', letterSpacing: '-0.025em', marginBottom: 3 }}>Sarah Chen</div>
<div style={{ fontSize: 10, color: '#A1A1AA', marginBottom: 16 }}>Design Engineer</div>
<div style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 7 }}>
{[['Team', 'Acme Inc'], ['Role', 'Admin'], ['Plan', 'Team']].map(([k, v]) => (
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10 }}>
<span style={{ color: '#A1A1AA' }}>{k}</span>
<span style={{ fontWeight: 500, color: '#0A0A0A' }}>{v}</span>
</div>
))}
</div>
<button style={{ marginTop: 16, width: '100%', padding: '7px 0', borderRadius: 6, border: '1px solid #E4E4E7', background: '#FAFAFA', fontSize: 10, fontWeight: 600, color: '#3F3F46', cursor: 'default' }}>
Edit profile
</button>
</div>
);
}
// ── NavSidebar ─────────────────────────────────────────────
function NavSidebar() {
const items = [
{ icon: '⊞', label: 'Dashboard', active: true },
{ icon: '◉', label: 'Origin Graph', active: false },
{ icon: '⬜', label: 'Artboards', active: false },
{ icon: '△', label: 'Diffs', active: false },
{ icon: '🔗', label: 'Integrations',active: false },
];
return (
<div style={{ height: '100%', background: '#FAFAFA', display: 'flex', flexDirection: 'column', padding: '12px 0' }}>
<div style={{ padding: '0 12px 12px', fontSize: 11, fontWeight: 800, letterSpacing: '-0.04em', color: '#0A0A0A' }}>
Origin<span style={{ color: '#0066FF' }}>main</span>
</div>
<div style={{ height: 1, background: '#EBEBEB', margin: '0 0 8px' }} />
{items.map(({ icon, label, active }) => (
<div
key={label}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '7px 12px',
margin: '1px 6px',
borderRadius: 5,
background: active ? 'rgba(0,102,255,0.07)' : 'transparent',
fontSize: 10,
fontWeight: active ? 600 : 400,
color: active ? '#0066FF' : '#52525B',
cursor: 'default',
}}
>
<span style={{ fontSize: 11 }}>{icon}</span>
{label}
</div>
))}
<div style={{ marginTop: 'auto', padding: '8px 12px 0', borderTop: '1px solid #EBEBEB' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ width: 24, height: 24, borderRadius: '50%', background: 'linear-gradient(135deg, #7C3AED, #0066FF)', flexShrink: 0 }} />
<div>
<div style={{ fontSize: 9, fontWeight: 600, color: '#0A0A0A' }}>Sarah Chen</div>
<div style={{ fontSize: 8, color: '#A1A1AA' }}>Admin</div>
</div> </div>
</div> </div>
</div> </div>
@@ -90,15 +177,75 @@ export function Artboard({ id, label, x, y, width, height }: ArtboardProps) {
); );
} }
// ── DataTable ──────────────────────────────────────────────
function DataTable() {
const rows = [
{ name: 'DashboardCard', status: 'Live', nodes: 12, tokens: 8 },
{ name: 'UserProfile', status: 'Draft', nodes: 7, tokens: 3 },
{ name: 'NavSidebar', status: 'Live', nodes: 19, tokens: 11 },
{ name: 'DataTable', status: 'Review', nodes: 24, tokens: 14 },
];
return (
<div style={{ padding: '16px 0', height: '100%', display: 'flex', flexDirection: 'column' }}>
<div style={{ padding: '0 16px 12px', fontSize: 11, fontWeight: 700, color: '#0A0A0A', letterSpacing: '-0.02em' }}>
Component Inventory
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 60px 50px 50px', padding: '0 16px 6px', gap: 4 }}>
{['Name', 'Status', 'Nodes', 'Tokens'].map(h => (
<span key={h} style={{ fontFamily: 'monospace', fontSize: 8, color: '#A1A1AA', letterSpacing: '0.05em', textTransform: 'uppercase' }}>{h}</span>
))}
</div>
<div style={{ height: 1, background: '#F0F0F0' }} />
{rows.map((row, i) => (
<div
key={row.name}
style={{
display: 'grid',
gridTemplateColumns: '1fr 60px 50px 50px',
padding: '8px 16px',
gap: 4,
background: i % 2 === 0 ? 'transparent' : '#FAFAFA',
alignItems: 'center',
}}
>
<span style={{ fontSize: 10, fontWeight: 500, color: '#0A0A0A', letterSpacing: '-0.01em' }}>{row.name}</span>
<span style={{
fontSize: 8,
fontWeight: 600,
color: row.status === 'Live' ? '#059669' : row.status === 'Draft' ? '#6B7280' : '#D97706',
background: row.status === 'Live' ? '#ECFDF5' : row.status === 'Draft' ? '#F9FAFB' : '#FFFBEB',
padding: '2px 6px',
borderRadius: 99,
width: 'fit-content',
fontFamily: 'monospace',
}}>
{row.status}
</span>
<span style={{ fontSize: 10, color: '#52525B', fontFamily: 'monospace' }}>{row.nodes}</span>
<span style={{ fontSize: 10, color: '#52525B', fontFamily: 'monospace' }}>{row.tokens}</span>
</div>
))}
</div>
);
}
function Placeholder({ label }: { label: string }) {
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: '#A1A1AA', fontSize: 11 }}>
{label}
</div>
);
}
function Handle({ pos }: { pos: React.CSSProperties }) { function Handle({ pos }: { pos: React.CSSProperties }) {
return ( return (
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
width: 7, width: 8,
height: 7, height: 8,
background: '#fff', background: '#fff',
border: '2px solid #0F52BA', border: '2px solid #3385FF',
borderRadius: 2, borderRadius: 2,
zIndex: 10, zIndex: 10,
...pos, ...pos,
@@ -107,9 +254,9 @@ function Handle({ pos }: { pos: React.CSSProperties }) {
); );
} }
function Chip({ children }: { children: string }) { function Tag({ children }: { children: string }) {
return ( return (
<span style={{ fontSize: 9, background: '#f5f5f5', color: '#666', padding: '3px 7px', borderRadius: 99 }}> <span style={{ fontSize: 9, background: '#F4F4F5', color: '#71717A', padding: '3px 8px', borderRadius: 99, fontWeight: 500 }}>
{children} {children}
</span> </span>
); );
+33 -15
View File
@@ -6,10 +6,10 @@ import { useCanvas } from '@/store/canvas';
import { Artboard } from './Artboard'; import { Artboard } from './Artboard';
const ARTBOARDS = [ const ARTBOARDS = [
{ id: 'dashboard-card', label: 'DashboardCard', x: 120, y: 120, width: 280, height: 200 }, { id: 'dashboard-card', label: 'DashboardCard', x: 120, y: 100, width: 280, height: 200 },
{ id: 'user-profile', label: 'UserProfile', x: 480, y: 120, width: 240, height: 280 }, { id: 'user-profile', label: 'UserProfile', x: 460, y: 100, width: 200, height: 260 },
{ id: 'nav-sidebar', label: 'NavSidebar', x: 120, y: 400, width: 200, height: 360 }, { id: 'nav-sidebar', label: 'NavSidebar', x: 120, y: 360, width: 200, height: 340 },
{ id: 'data-table', label: 'DataTable', x: 400, y: 420, width: 420, height: 300 }, { id: 'data-table', label: 'DataTable', x: 380, y: 380, width: 420, height: 280 },
]; ];
export function Canvas() { export function Canvas() {
@@ -23,7 +23,7 @@ export function Canvas() {
const lastPos = useRef({ x: 0, y: 0 }); const lastPos = useRef({ x: 0, y: 0 });
const spaceDown = useRef(false); const spaceDown = useRef(false);
// Wheel: pan or zoom // Wheel: pan or pinch-zoom
useEffect(() => { useEffect(() => {
const el = containerRef.current; const el = containerRef.current;
if (!el) return; if (!el) return;
@@ -32,9 +32,7 @@ export function Canvas() {
const { zoom, panX, panY, setPan, setZoom } = useViewport.getState(); const { zoom, panX, panY, setPan, setZoom } = useViewport.getState();
if (e.ctrlKey || e.metaKey) { if (e.ctrlKey || e.metaKey) {
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
const ox = e.clientX - rect.left; setZoom(zoom * (e.deltaY > 0 ? 0.92 : 1.09), e.clientX - rect.left, e.clientY - rect.top);
const oy = e.clientY - rect.top;
setZoom(zoom * (e.deltaY > 0 ? 0.92 : 1.09), ox, oy);
} else { } else {
setPan(panX - e.deltaX, panY - e.deltaY); setPan(panX - e.deltaX, panY - e.deltaY);
} }
@@ -43,7 +41,7 @@ export function Canvas() {
return () => el.removeEventListener('wheel', onWheel); return () => el.removeEventListener('wheel', onWheel);
}, []); }, []);
// Space key: temporary pan mode // Space bar temporary pan
useEffect(() => { useEffect(() => {
const down = (e: KeyboardEvent) => { if (e.code === 'Space' && e.target === document.body) spaceDown.current = true; }; const down = (e: KeyboardEvent) => { if (e.code === 'Space' && e.target === document.body) spaceDown.current = true; };
const up = (e: KeyboardEvent) => { if (e.code === 'Space') spaceDown.current = false; }; const up = (e: KeyboardEvent) => { if (e.code === 'Space') spaceDown.current = false; };
@@ -74,29 +72,48 @@ export function Canvas() {
const onMouseUp = useCallback(() => { isPanning.current = false; }, []); const onMouseUp = useCallback(() => { isPanning.current = false; }, []);
const gridSize = Math.max(8, 22 * zoom); // Dot grid that shifts with pan and scales with zoom
const gridSpacing = Math.max(6, 20 * zoom);
const cursor = activeTool === 'pan' || isPanning.current ? 'grab' : 'default'; const cursor = activeTool === 'pan' || isPanning.current ? 'grab' : 'default';
return ( return (
<div <div
ref={containerRef} ref={containerRef}
style={{ gridColumn: 2, gridRow: 2, position: 'relative', overflow: 'hidden', background: '#111115', cursor }} style={{
gridColumn: 2,
gridRow: 2,
position: 'relative',
overflow: 'hidden',
background: '#0C0C10',
cursor,
}}
onMouseDown={onMouseDown} onMouseDown={onMouseDown}
onMouseMove={onMouseMove} onMouseMove={onMouseMove}
onMouseUp={onMouseUp} onMouseUp={onMouseUp}
> >
{/* Dot grid — shifts with pan, scales with zoom */} {/* Dot grid — scales and shifts with viewport */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
inset: 0, inset: 0,
backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.07) 1px, transparent 1px)', backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.055) 1px, transparent 1px)',
backgroundSize: `${gridSize}px ${gridSize}px`, backgroundSize: `${gridSpacing}px ${gridSpacing}px`,
backgroundPosition: `${panX % gridSize}px ${panY % gridSize}px`, backgroundPosition: `${panX % gridSpacing}px ${panY % gridSpacing}px`,
pointerEvents: 'none', pointerEvents: 'none',
}} }}
/> />
{/* Subtle radial vignette */}
<div
style={{
position: 'absolute',
inset: 0,
background: 'radial-gradient(ellipse 100% 100% at 50% 50%, transparent 40%, rgba(0,0,0,0.35) 100%)',
pointerEvents: 'none',
zIndex: 1,
}}
/>
{/* Transform layer */} {/* Transform layer */}
<div <div
style={{ style={{
@@ -104,6 +121,7 @@ export function Canvas() {
inset: 0, inset: 0,
transform: `matrix(${zoom},0,0,${zoom},${panX},${panY})`, transform: `matrix(${zoom},0,0,${zoom},${panX},${panY})`,
transformOrigin: '0 0', transformOrigin: '0 0',
zIndex: 2,
}} }}
> >
{ARTBOARDS.map((ab) => ( {ARTBOARDS.map((ab) => (
@@ -10,10 +10,12 @@ export function AppChrome() {
<div <div
style={{ style={{
display: 'grid', display: 'grid',
gridTemplateRows: '40px 1fr', gridTemplateRows: '44px 1fr',
gridTemplateColumns: '240px 1fr 320px', gridTemplateColumns: '220px 1fr 272px',
height: '100dvh', height: '100dvh',
overflow: 'hidden', overflow: 'hidden',
background: '#0C0C10',
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
}} }}
> >
<Toolbar /> <Toolbar />
+193 -46
View File
@@ -4,7 +4,6 @@ import {
ToolbarButton, ToolbarButton,
ToolbarDivider, ToolbarDivider,
Tooltip, Tooltip,
Badge,
} from '@fluentui/react-components'; } from '@fluentui/react-components';
import { import {
CursorClickRegular, CursorClickRegular,
@@ -19,14 +18,28 @@ import {
import { useCanvas, type Tool } from '@/store/canvas'; import { useCanvas, type Tool } from '@/store/canvas';
import { useViewport } from '@/store/viewport'; import { useViewport } from '@/store/viewport';
// Design tokens — Fluent dark surface
const T = {
bg: '#141418',
border: 'rgba(255,255,255,0.06)',
sep: 'rgba(255,255,255,0.07)',
fg: 'rgba(255,255,255,0.88)',
fgMuted: 'rgba(255,255,255,0.38)',
fgDim: 'rgba(255,255,255,0.22)',
accent: '#3385FF',
accentBg: 'rgba(51,133,255,0.14)',
activeBg: 'rgba(255,255,255,0.07)',
live: '#10B981',
liveBg: 'rgba(16,185,129,0.1)',
liveBorder:'rgba(16,185,129,0.2)',
};
export function Toolbar() { export function Toolbar() {
const { activeTool, setActiveTool } = useCanvas(); const { activeTool, setActiveTool } = useCanvas();
const zoom = useViewport((s) => s.zoom); const zoom = useViewport((s) => s.zoom);
const setZoom = useViewport((s) => s.setZoom); const setZoom = useViewport((s) => s.setZoom);
const reset = useViewport((s) => s.reset); const reset = useViewport((s) => s.reset);
const ap = (id: Tool): 'primary' | 'subtle' => activeTool === id ? 'primary' : 'subtle';
return ( return (
<div <div
style={{ style={{
@@ -34,90 +47,224 @@ export function Toolbar() {
gridRow: 1, gridRow: 1,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
borderBottom: '1px solid rgba(0,0,0,0.08)', background: T.bg,
background: '#fff', borderBottom: `1px solid ${T.border}`,
paddingInline: 8, paddingInline: 12,
gap: 2, gap: 2,
height: 40, height: 44,
userSelect: 'none',
}} }}
> >
{/* Wordmark */} {/* Wordmark */}
<span <span
style={{ style={{
fontFamily: 'system-ui, sans-serif', fontFamily: "'Inter', system-ui, sans-serif",
fontWeight: 800, fontWeight: 800,
fontSize: '0.9375rem', fontSize: '0.875rem',
color: '#0F52BA',
letterSpacing: '-0.04em', letterSpacing: '-0.04em',
padding: '0 8px', color: T.fg,
marginRight: 4, padding: '0 10px 0 4px',
userSelect: 'none', marginRight: 2,
flexShrink: 0,
}} }}
> >
Om Om<span style={{ color: T.accent }}></span>
</span> </span>
<ToolbarDivider /> <Sep />
{/* Select */} {/* Selection tools */}
<ToolGroup>
<Tooltip content="Select V" relationship="label"> <Tooltip content="Select V" relationship="label">
<ToolbarButton appearance={ap('select')} icon={<CursorClickRegular />} onClick={() => setActiveTool('select')} /> <TBtn active={activeTool === 'select'} onClick={() => setActiveTool('select')}>
<CursorClickRegular />
</TBtn>
</Tooltip> </Tooltip>
{/* Pan */}
<Tooltip content="Pan H" relationship="label"> <Tooltip content="Pan H" relationship="label">
<ToolbarButton appearance={ap('pan')} icon={<HandLeftRegular />} onClick={() => setActiveTool('pan')} /> <TBtn active={activeTool === 'pan'} onClick={() => setActiveTool('pan')}>
</Tooltip> <HandLeftRegular />
{/* Artboard */} </TBtn>
<Tooltip content="New Artboard A" relationship="label">
<ToolbarButton appearance={ap('artboard')} icon={<SquareRegular />} onClick={() => setActiveTool('artboard')} />
</Tooltip>
{/* Zone */}
<Tooltip content="Completion Zone Z" relationship="label">
<ToolbarButton appearance={ap('zone')} icon={<SelectObjectRegular />} onClick={() => setActiveTool('zone')} />
</Tooltip> </Tooltip>
</ToolGroup>
<ToolbarDivider /> <Sep />
{/* Create tools */}
<ToolGroup>
<Tooltip content="New Artboard A" relationship="label">
<TBtn active={activeTool === 'artboard'} onClick={() => setActiveTool('artboard')}>
<SquareRegular />
</TBtn>
</Tooltip>
<Tooltip content="Completion Zone Z" relationship="label">
<TBtn active={activeTool === 'zone'} onClick={() => setActiveTool('zone')}>
<SelectObjectRegular />
</TBtn>
</Tooltip>
</ToolGroup>
<Sep />
{/* View tools */} {/* View tools */}
<ToolGroup>
<Tooltip content="Intent Diff" relationship="label"> <Tooltip content="Intent Diff" relationship="label">
<ToolbarButton appearance="subtle" icon={<DataTrendingRegular />} /> <TBtn active={false} onClick={() => {}}>
<DataTrendingRegular />
</TBtn>
</Tooltip> </Tooltip>
<Tooltip content="Origin Graph" relationship="label"> <Tooltip content="Origin Graph" relationship="label">
<ToolbarButton appearance="subtle" icon={<CircleHintHalfVerticalRegular />} /> <TBtn active={false} onClick={() => {}}>
<CircleHintHalfVerticalRegular />
</TBtn>
</Tooltip>
</ToolGroup>
{/* Right side */}
<div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 4 }}>
<Tooltip content="Zoom out" relationship="label">
<TBtn active={false} onClick={() => setZoom(zoom * 0.8)}>
<ZoomOutRegular />
</TBtn>
</Tooltip> </Tooltip>
{/* Right — zoom + live status */}
<div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 4, paddingInline: 4 }}>
<Tooltip content="Zoom out" relationship="label">
<ToolbarButton appearance="subtle" icon={<ZoomOutRegular />} onClick={() => setZoom(zoom * 0.8)} />
</Tooltip>
<button <button
onClick={reset} onClick={reset}
style={{ style={{
background: 'none', background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(0,0,0,0.1)', border: `1px solid ${T.sep}`,
borderRadius: 4, borderRadius: 5,
padding: '2px 8px', padding: '3px 9px',
fontSize: '0.6875rem', fontSize: '0.6875rem',
fontFamily: 'monospace', fontFamily: "'JetBrains Mono', 'SF Mono', ui-monospace, monospace",
color: 'rgba(0,0,0,0.5)', color: T.fgMuted,
cursor: 'pointer', cursor: 'pointer',
minWidth: 46, minWidth: 48,
textAlign: 'center',
transition: 'background 0.12s, color 0.12s',
letterSpacing: '-0.01em',
}}
onMouseEnter={e => {
(e.currentTarget as HTMLButtonElement).style.background = 'rgba(255,255,255,0.09)';
(e.currentTarget as HTMLButtonElement).style.color = T.fg;
}}
onMouseLeave={e => {
(e.currentTarget as HTMLButtonElement).style.background = 'rgba(255,255,255,0.05)';
(e.currentTarget as HTMLButtonElement).style.color = T.fgMuted;
}} }}
> >
{Math.round(zoom * 100)}% {Math.round(zoom * 100)}%
</button> </button>
<Tooltip content="Zoom in" relationship="label"> <Tooltip content="Zoom in" relationship="label">
<ToolbarButton appearance="subtle" icon={<ZoomInRegular />} onClick={() => setZoom(zoom * 1.25)} /> <TBtn active={false} onClick={() => setZoom(zoom * 1.25)}>
<ZoomInRegular />
</TBtn>
</Tooltip> </Tooltip>
<ToolbarDivider /> <Sep />
<Badge color="success" size="extra-small" /> {/* Live status pill */}
<span style={{ fontSize: '0.6875rem', fontFamily: 'monospace', color: 'rgba(0,0,0,0.4)', paddingRight: 4 }}> <div
style={{
display: 'flex',
alignItems: 'center',
gap: 6,
padding: '4px 10px',
borderRadius: 6,
background: T.liveBg,
border: `1px solid ${T.liveBorder}`,
marginLeft: 2,
marginRight: 4,
}}
>
<div
style={{
width: 6,
height: 6,
borderRadius: '50%',
background: T.live,
flexShrink: 0,
}}
/>
<span
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.625rem',
color: T.live,
fontWeight: 500,
}}
>
Live Live
</span> </span>
</div> </div>
</div> </div>
</div>
);
}
function Sep() {
return (
<div
style={{
width: 1,
height: 20,
background: 'rgba(255,255,255,0.07)',
margin: '0 5px',
flexShrink: 0,
}}
/>
);
}
function ToolGroup({ children }: { children: React.ReactNode }) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{children}
</div>
);
}
function TBtn({
active,
onClick,
children,
}: {
active: boolean;
onClick: () => void;
children: React.ReactNode;
}) {
return (
<button
onClick={onClick}
style={{
width: 32,
height: 32,
borderRadius: 6,
border: 'none',
background: active ? 'rgba(51,133,255,0.15)' : 'transparent',
color: active ? '#3385FF' : 'rgba(255,255,255,0.42)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'background 0.1s, color 0.1s',
fontSize: '1rem',
flexShrink: 0,
}}
onMouseEnter={e => {
if (!active) {
(e.currentTarget as HTMLButtonElement).style.background = 'rgba(255,255,255,0.07)';
(e.currentTarget as HTMLButtonElement).style.color = 'rgba(255,255,255,0.75)';
}
}}
onMouseLeave={e => {
if (!active) {
(e.currentTarget as HTMLButtonElement).style.background = 'transparent';
(e.currentTarget as HTMLButtonElement).style.color = 'rgba(255,255,255,0.42)';
}
}}
>
{children}
</button>
); );
} }
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { TabList, Tab, Divider } from '@fluentui/react-components'; import { useState } from 'react';
import { useCanvas } from '@/store/canvas'; import { useCanvas } from '@/store/canvas';
const PROPS = [ const PROPS = [
@@ -15,87 +15,211 @@ const DIFF = [
{ op: 'del', text: ' borderRadius: 8px' }, { op: 'del', text: ' borderRadius: 8px' },
{ op: 'add', text: '+ borderRadius: 12px' }, { op: 'add', text: '+ borderRadius: 12px' },
{ op: 'del', text: ' accentColor: #2A6CD4' }, { op: 'del', text: ' accentColor: #2A6CD4' },
{ op: 'add', text: '+ accentColor: #0F52BA' }, { op: 'add', text: '+ accentColor: #0066FF' },
{ op: 'del', text: ' padding: 16' },
{ op: 'add', text: '+ padding: 20' },
]; ];
const TYPE_COLORS: Record<string, string> = { const TYPE_COLORS: Record<string, string> = {
s: '#16a34a', s: '#7DD3A8',
n: '#2563eb', n: '#7EB8FF',
b: '#d97706', b: '#FFBA7B',
};
type TabId = 'props' | 'diff' | 'graph';
// Dark panel tokens
const T = {
bg: '#111115',
border: 'rgba(255,255,255,0.055)',
sep: 'rgba(255,255,255,0.04)',
label: 'rgba(255,255,255,0.22)',
key: 'rgba(255,255,255,0.32)',
dim: 'rgba(255,255,255,0.18)',
accent: '#3385FF',
tabFg: 'rgba(255,255,255,0.28)',
tabOn: 'rgba(255,255,255,0.88)',
}; };
export function Inspector() { export function Inspector() {
const { selectedArtboardId } = useCanvas(); const { selectedArtboardId } = useCanvas();
const [tab, setTab] = useState<TabId>('props');
return ( return (
<div <div
style={{ style={{
gridColumn: 3, gridColumn: 3,
gridRow: 2, gridRow: 2,
background: '#fafafa', background: T.bg,
borderLeft: '1px solid rgba(0,0,0,0.08)', borderLeft: `1px solid ${T.border}`,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
overflow: 'hidden', overflow: 'hidden',
fontSize: 12, fontSize: 12,
}} }}
> >
<TabList defaultSelectedValue="props" size="small" style={{ padding: '0 8px', borderBottom: '1px solid rgba(0,0,0,0.08)' }}> {/* Tab bar */}
<Tab value="props">Props</Tab> <div
<Tab value="diff">Diff</Tab> style={{
<Tab value="graph">Graph</Tab> display: 'flex',
</TabList> borderBottom: `1px solid ${T.border}`,
flexShrink: 0,
}}
>
{(['props', 'diff', 'graph'] as TabId[]).map((t) => (
<button
key={t}
onClick={() => setTab(t)}
style={{
flex: 1,
padding: '11px 0',
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.5875rem',
fontWeight: 500,
letterSpacing: '0.08em',
textTransform: 'uppercase',
color: tab === t ? T.tabOn : T.tabFg,
background: 'transparent',
border: 'none',
borderBottom: tab === t ? `2px solid ${T.accent}` : '2px solid transparent',
cursor: 'pointer',
transition: 'color 0.12s',
marginBottom: -1,
}}
>
{t}
</button>
))}
</div>
{selectedArtboardId ? ( {/* Content */}
<div style={{ flex: 1, overflow: 'auto' }}>
{!selectedArtboardId ? (
<div
style={{
padding: '32px 16px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 8,
textAlign: 'center',
}}
>
<span style={{ fontSize: '1.25rem', opacity: 0.3 }}></span>
<span
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.625rem',
color: 'rgba(255,255,255,0.22)',
letterSpacing: '0.06em',
}}
>
Select an artboard
</span>
</div>
) : tab === 'props' ? (
<> <>
<Section label="Component Props"> <Section label="Component Props">
{PROPS.map(({ key, val, type }) => ( {PROPS.map(({ key, val, type }) => (
<PropRow key={key} label={key} value={val} color={TYPE_COLORS[type] ?? '#555'} /> <PropRow key={key} label={key} value={val} color={TYPE_COLORS[type] ?? T.key} />
))} ))}
</Section> </Section>
<HSep />
<Divider style={{ margin: 0 }} /> <Section label="Render Target">
<PropRow label="file" value="dashboard.tsx:42" color="#7EB8FF" />
<PropRow label="status" value="connected" color="#7DD3A8" />
<PropRow label="agent" value="claude-code" color="#7DD3A8" />
</Section>
</>
) : tab === 'diff' ? (
<Section label="Intent Diff"> <Section label="Intent Diff">
<div
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.5875rem',
color: 'rgba(255,255,255,0.28)',
marginBottom: 10,
letterSpacing: '-0.01em',
}}
>
DashboardCard.tsx · 3 hunks
</div>
{DIFF.map((d, i) => ( {DIFF.map((d, i) => (
<div <div
key={i} key={i}
style={{ style={{
fontFamily: 'monospace', fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: 11, fontSize: '0.625rem',
padding: '3px 8px', padding: '4px 8px',
borderRadius: 3, borderRadius: 4,
marginBottom: 3, marginBottom: 3,
background: d.op === 'del' ? 'rgba(239,68,68,0.08)' : 'rgba(34,197,94,0.08)', lineHeight: 1.55,
color: d.op === 'del' ? '#dc2626' : '#16a34a', background: d.op === 'del' ? 'rgba(255,70,70,0.08)' : 'rgba(70,220,120,0.08)',
color: d.op === 'del' ? '#FF8080' : '#7DDBA0',
borderLeft: `2px solid ${d.op === 'del' ? 'rgba(255,80,80,0.3)' : 'rgba(70,220,120,0.3)'}`,
}} }}
> >
{d.text} {d.text}
</div> </div>
))} ))}
</Section> </Section>
<Divider style={{ margin: 0 }} />
<Section label="Render Target">
<PropRow label="file" value="dashboard.tsx:42" color="#2563eb" />
<PropRow label="status" value="connected" color="#16a34a" />
<PropRow label="agent" value="claude-code" color="#16a34a" />
</Section>
</>
) : ( ) : (
<div style={{ padding: 24, color: 'rgba(0,0,0,0.3)', fontSize: 12, fontFamily: 'monospace', textAlign: 'center' }}> <Section label="Origin Graph">
Select an artboard <GraphNode label="DashboardCard" depth={0} isRoot />
<GraphNode label="StatsCard" depth={1} />
<GraphNode label="ProgressBar" depth={2} />
<GraphNode label="ValueDisplay" depth={2} />
<GraphNode label="CardBase" depth={1} />
<GraphNode label="Elevation" depth={2} />
<HSep />
<div style={{ padding: '4px 0 8px' }}>
<PropRow label="nodes" value="284" color="#7EB8FF" />
<PropRow label="depth" value="4" color="#7EB8FF" />
<PropRow label="tokens used" value="12" color="#FFBA7B" />
</div> </div>
</Section>
)} )}
</div> </div>
{/* Status bar */}
<div
style={{
height: 27,
background: '#0D0D11',
borderTop: `1px solid ${T.sep}`,
display: 'flex',
alignItems: 'center',
padding: '0 12px',
gap: 12,
flexShrink: 0,
}}
>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#10B981', flexShrink: 0 }} />
<span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '0.5625rem', color: 'rgba(255,255,255,0.45)' }}>
Live render connected
</span>
<span style={{ marginLeft: 'auto', fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '0.5625rem', color: 'rgba(255,255,255,0.22)' }}>
284 nodes
</span>
</div>
</div>
); );
} }
function Section({ label, children }: { label: string; children: React.ReactNode }) { function Section({ label, children }: { label: string; children: React.ReactNode }) {
return ( return (
<div style={{ padding: '10px 12px' }}> <div style={{ padding: '12px 14px' }}>
<div style={{ fontFamily: 'monospace', fontSize: 9, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.3)', marginBottom: 8 }}> <div
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.5rem',
fontWeight: 500,
letterSpacing: '0.12em',
textTransform: 'uppercase',
color: T.label,
marginBottom: 10,
}}
>
{label} {label}
</div> </div>
{children} {children}
@@ -105,9 +229,70 @@ function Section({ label, children }: { label: string; children: React.ReactNode
function PropRow({ label, value, color }: { label: string; value: string; color: string }) { function PropRow({ label, value, color }: { label: string; value: string; color: string }) {
return ( return (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6 }}> <div
<span style={{ fontFamily: 'monospace', fontSize: 11, color: 'rgba(0,0,0,0.4)' }}>{label}</span> style={{
<span style={{ fontFamily: 'monospace', fontSize: 11, color }}>{value}</span> display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline',
marginBottom: 8,
gap: 8,
}}
>
<span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '0.625rem', color: T.key, flexShrink: 0 }}>
{label}
</span>
<span
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.625rem',
color,
textAlign: 'right',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
maxWidth: '60%',
}}
>
{value}
</span>
</div> </div>
); );
} }
function GraphNode({ label, depth, isRoot }: { label: string; depth: number; isRoot?: boolean }) {
return (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 6,
paddingLeft: depth * 14,
marginBottom: 6,
}}
>
<div
style={{
width: 6,
height: 6,
borderRadius: '50%',
background: isRoot ? T.accent : 'rgba(255,255,255,0.18)',
flexShrink: 0,
}}
/>
<span
style={{
fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontSize: '0.625rem',
color: isRoot ? T.accent : 'rgba(255,255,255,0.5)',
letterSpacing: '-0.01em',
}}
>
{label}
</span>
</div>
);
}
function HSep() {
return <div style={{ height: 1, background: 'rgba(255,255,255,0.04)', margin: '2px 0' }} />;
}
@@ -4,7 +4,6 @@ import {
Tree, Tree,
TreeItem, TreeItem,
TreeItemLayout, TreeItemLayout,
Text,
} from '@fluentui/react-components'; } from '@fluentui/react-components';
import { import {
SquareRegular, SquareRegular,
@@ -21,6 +20,19 @@ const ARTBOARDS = [
{ id: 'data-table', label: 'DataTable' }, { id: 'data-table', label: 'DataTable' },
]; ];
// Dark panel tokens
const T = {
bg: '#111115',
border: 'rgba(255,255,255,0.055)',
label: 'rgba(255,255,255,0.22)',
item: 'rgba(255,255,255,0.42)',
itemHov: 'rgba(255,255,255,0.72)',
selBg: 'rgba(51,133,255,0.12)',
selFg: 'rgba(255,255,255,0.88)',
accent: '#3385FF',
sep: 'rgba(255,255,255,0.04)',
};
export function ArtboardNavigator() { export function ArtboardNavigator() {
const { selectedArtboardId, selectArtboard } = useCanvas(); const { selectedArtboardId, selectArtboard } = useCanvas();
@@ -29,83 +41,162 @@ export function ArtboardNavigator() {
style={{ style={{
gridColumn: 1, gridColumn: 1,
gridRow: 2, gridRow: 2,
background: '#fafafa', background: T.bg,
borderRight: '1px solid rgba(0,0,0,0.08)', borderRight: `1px solid ${T.border}`,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
overflow: 'hidden', overflow: 'hidden',
fontSize: 12,
}} }}
> >
{/* Artboards section */} {/* Artboards section */}
<SectionLabel>Artboards</SectionLabel> <SectionLabel icon="⬜">Artboards</SectionLabel>
<Tree aria-label="Artboards" size="small" style={{ padding: '0 4px' }}> <div style={{ padding: '2px 6px 0' }}>
{ARTBOARDS.map((ab) => ( {ARTBOARDS.map((ab) => (
<TreeItem <div
key={ab.id} key={ab.id}
itemType="leaf"
value={ab.id}
style={{
background: selectedArtboardId === ab.id ? 'rgba(15,82,186,0.08)' : undefined,
borderRadius: 4,
}}
onClick={() => selectArtboard(ab.id)} onClick={() => selectArtboard(ab.id)}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '6px 10px',
borderRadius: 5,
cursor: 'pointer',
background: selectedArtboardId === ab.id ? T.selBg : 'transparent',
color: selectedArtboardId === ab.id ? T.selFg : T.item,
fontSize: '0.75rem',
letterSpacing: '-0.01em',
fontWeight: selectedArtboardId === ab.id ? 500 : 400,
transition: 'background 0.1s, color 0.1s',
userSelect: 'none',
marginBottom: 1,
}}
onMouseEnter={e => {
if (selectedArtboardId !== ab.id) {
(e.currentTarget as HTMLDivElement).style.background = 'rgba(255,255,255,0.04)';
(e.currentTarget as HTMLDivElement).style.color = T.itemHov;
}
}}
onMouseLeave={e => {
if (selectedArtboardId !== ab.id) {
(e.currentTarget as HTMLDivElement).style.background = 'transparent';
(e.currentTarget as HTMLDivElement).style.color = T.item;
}
}}
> >
<TreeItemLayout iconBefore={<SquareRegular style={{ color: '#0F52BA', fontSize: 12 }} />}> <SquareRegular
<Text size={200}>{ab.label}</Text> style={{
</TreeItemLayout> fontSize: 11,
</TreeItem> color: selectedArtboardId === ab.id ? T.accent : 'rgba(255,255,255,0.25)',
flexShrink: 0,
}}
/>
{ab.label}
{selectedArtboardId === ab.id && (
<span
style={{
marginLeft: 'auto',
width: 5,
height: 5,
borderRadius: '50%',
background: T.accent,
flexShrink: 0,
}}
/>
)}
</div>
))} ))}
</Tree> </div>
<Divider /> <HSep />
{/* Codebase section */} {/* Files section */}
<SectionLabel>Files</SectionLabel> <SectionLabel icon="📁">Files</SectionLabel>
<Tree aria-label="Codebase" size="small" style={{ padding: '0 4px' }}> <Tree
<TreeItem itemType="branch" value="src"> aria-label="Codebase"
<TreeItemLayout iconBefore={<FolderOpenRegular style={{ fontSize: 12 }} />}> size="small"
<Text size={200}>src</Text> style={{ padding: '2px 6px' }}
>
<TreeItem
itemType="branch"
value="src"
style={{ color: T.item }}
>
<TreeItemLayout
iconBefore={<FolderOpenRegular style={{ fontSize: 11, color: 'rgba(255,255,255,0.3)' }} />}
style={{ fontSize: '0.75rem', color: T.item, padding: '4px 4px' }}
>
src
</TreeItemLayout> </TreeItemLayout>
<Tree> <Tree>
<TreeItem itemType="branch" value="components"> <TreeItem itemType="branch" value="components">
<TreeItemLayout iconBefore={<FolderRegular style={{ fontSize: 12 }} />}> <TreeItemLayout
<Text size={200}>components</Text> iconBefore={<FolderRegular style={{ fontSize: 11, color: 'rgba(255,255,255,0.25)' }} />}
style={{ fontSize: '0.75rem', color: 'rgba(255,255,255,0.35)', padding: '4px 4px' }}
>
components
</TreeItemLayout> </TreeItemLayout>
<Tree> <Tree>
{ARTBOARDS.map((ab) => ( {ARTBOARDS.map((ab) => (
<TreeItem key={ab.id} itemType="leaf" value={`file-${ab.id}`}> <TreeItem key={ab.id} itemType="leaf" value={`file-${ab.id}`}>
<TreeItemLayout iconBefore={<DocumentRegular style={{ fontSize: 12 }} />}> <TreeItemLayout
<Text size={200} style={{ color: 'rgba(0,0,0,0.55)' }}> iconBefore={<DocumentRegular style={{ fontSize: 10, color: 'rgba(255,255,255,0.2)' }} />}
style={{ fontSize: '0.6875rem', color: 'rgba(255,255,255,0.3)', padding: '3px 4px' }}
>
{ab.label}.tsx {ab.label}.tsx
</Text>
</TreeItemLayout> </TreeItemLayout>
</TreeItem> </TreeItem>
))} ))}
</Tree> </Tree>
</TreeItem> </TreeItem>
<TreeItem itemType="leaf" value="app-page"> <TreeItem itemType="leaf" value="app-page">
<TreeItemLayout iconBefore={<DocumentRegular style={{ fontSize: 12 }} />}> <TreeItemLayout
<Text size={200} style={{ color: 'rgba(0,0,0,0.55)' }}>page.tsx</Text> iconBefore={<DocumentRegular style={{ fontSize: 10, color: 'rgba(255,255,255,0.2)' }} />}
style={{ fontSize: '0.6875rem', color: 'rgba(255,255,255,0.3)', padding: '3px 4px' }}
>
page.tsx
</TreeItemLayout> </TreeItemLayout>
</TreeItem> </TreeItem>
</Tree> </Tree>
</TreeItem> </TreeItem>
</Tree> </Tree>
<HSep />
{/* Graph nodes indicator */}
<SectionLabel icon="◉">Graph</SectionLabel>
<div
style={{
padding: '4px 16px 12px',
display: 'flex',
flexDirection: 'column',
gap: 5,
}}
>
<GraphStat label="nodes" value="284" color="#3385FF" />
<GraphStat label="components" value="47" color="rgba(255,255,255,0.45)" />
<GraphStat label="tokens" value="112" color="rgba(255,255,255,0.45)" />
</div>
</div> </div>
); );
} }
function SectionLabel({ children }: { children: string }) { function SectionLabel({ children, icon }: { children: string; icon: string }) {
return ( return (
<div <div
style={{ style={{
padding: '10px 12px 4px', padding: '12px 12px 5px',
fontSize: '0.625rem', fontFamily: "'JetBrains Mono', ui-monospace, monospace",
fontFamily: 'monospace', fontSize: '0.5625rem',
fontWeight: 500,
letterSpacing: '0.1em', letterSpacing: '0.1em',
textTransform: 'uppercase', textTransform: 'uppercase',
color: 'rgba(0,0,0,0.35)', color: 'rgba(255,255,255,0.22)',
userSelect: 'none', userSelect: 'none',
display: 'flex',
alignItems: 'center',
gap: 6,
}} }}
> >
{children} {children}
@@ -113,6 +204,28 @@ function SectionLabel({ children }: { children: string }) {
); );
} }
function Divider() { function HSep() {
return <div style={{ height: 1, background: 'rgba(0,0,0,0.06)', margin: '6px 0' }} />; return (
<div
style={{
height: 1,
background: 'rgba(255,255,255,0.04)',
margin: '8px 0',
flexShrink: 0,
}}
/>
);
}
function GraphStat({ label, value, color }: { label: string; value: string; color: string }) {
return (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
<span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '0.625rem', color: 'rgba(255,255,255,0.28)' }}>
{label}
</span>
<span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '0.625rem', color, fontWeight: 600 }}>
{value}
</span>
</div>
);
} }