Files
scarf/design/static-site/ui-kit/Projects.jsx
T
Alan Wizemann 8a2d89654b feat(design): adopt ScarfDesign system across Mac UI
Add a typed design-system package (Packages/ScarfDesign) with rust-tone
color tokens, type scale, spacing/radius tokens, ScarfPageHeader and
component primitives (ScarfCard, ScarfBadge, ScarfTextField,
ScarfSectionHeader, ScarfDivider, four button styles). Both Mac and iOS
targets `import ScarfDesign`.

Sidebar redesigned per design/static-site/ui-kit/Sidebar.jsx — glassy
translucent background, 224 px width, app-icon header with server pill,
custom tokenized rows with rust accent-tint when active, footer with
live Hermes-running indicator (wired to ServerLiveStatusRegistry).

14 mockup-backed feature screens redesigned: Settings, Dashboard,
Sessions, Memory, Chat (visual sweep), Activity, Cron, Insights,
MCPServers, Health, Logs, Tools (full); Projects light-touch.
Non-mockup features inherit rust through AccentColor.colorset repoint.

Mac AppIcon.appiconset replaced with the rust set. AccentColor.colorset
repointed to BrandRust hex (light + dark variants).

Visual sweep: every multi-button page-header / action-bar cluster now
wraps in .fixedSize(horizontal: true, vertical: false) so labels can't
wrap letter-by-letter at narrow widths (regression seen on the MCP
detail pane with 4 buttons).

Follow-ups landed:
- Sidebar Hermes-running probe wired to per-window
  ServerLiveStatusRegistry (no more placeholder green).
- Sessions: today filter predicate (isDateInToday(startedAt)); pill
  count reflects real count. Starred stays a no-op pending an upstream
  pinned/starred field on HermesSession.
- Dashboard: Recent activity column rendered alongside Recent sessions
  in a ViewThatFits 2-col grid. Populated from
  HermesDataService.fetchRecentToolCalls(limit:) flattened to
  ActivityEntry. ActivityEntry gains a public memberwise init.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:27:54 +02:00

84 lines
4.6 KiB
React

// Projects — list of project folders the agent operates in.
function Projects() {
const projects = [
{ id: 1, name: 'hermes-blog', dir: '~/code/hermes-blog', template: 'awizemann/hermes-blog', sessions: 142, lastRun: '14m ago', cron: 2, status: 'healthy' },
{ id: 2, name: 'scarf', dir: '~/code/scarf', template: '—', sessions: 89, lastRun: '42m ago', cron: 0, status: 'healthy' },
{ id: 3, name: 'inbox-sweep', dir: '~/code/inbox-sweep', template: 'community/inbox-sweep', sessions: 38, lastRun: '3h ago', cron: 1, status: 'healthy' },
{ id: 4, name: 'twitter-recap', dir: '~/code/twitter-recap', template: 'awizemann/twitter-recap', sessions: 14, lastRun: '2d ago', cron: 1, status: 'paused' },
{ id: 5, name: 'pr-watcher', dir: '~/code/pr-watcher', template: 'community/pr-watcher', sessions: 4, lastRun: '5d ago', cron: 1, status: 'errored' },
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<ContentHeader title="Projects"
subtitle="Each project pins context, AGENTS.md, cron jobs, and session history"
actions={<><Btn icon="folder-plus">Add Existing</Btn><Btn kind="primary" icon="plus">New from Template</Btn></>} />
<div style={{ flex: 1, overflowY: 'auto', padding: '20px 28px' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 14 }}>
{projects.map(p => (
<Card key={p.id} padding={16} style={{ display: 'flex', flexDirection: 'column', gap: 10, cursor: 'pointer' }}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
<div style={{
width: 36, height: 36, borderRadius: 8, background: 'var(--accent-tint)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: 'var(--accent)', flexShrink: 0,
}}>
<i data-lucide="folder" style={{ width: 18, height: 18 }}></i>
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 14, fontWeight: 600 }}>{p.name}</div>
<div style={{ fontSize: 11, color: 'var(--fg-muted)',
fontFamily: 'var(--font-mono)', overflow: 'hidden',
textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.dir}</div>
</div>
{p.status === 'healthy' && <Pill tone="green" dot>healthy</Pill>}
{p.status === 'paused' && <Pill tone="gray" dot>paused</Pill>}
{p.status === 'errored' && <Pill tone="red" dot>errored</Pill>}
</div>
{p.template !== '—' && (
<div style={{ fontSize: 11, color: 'var(--fg-muted)',
display: 'flex', alignItems: 'center', gap: 5 }}>
<i data-lucide="package" style={{ width: 11, height: 11 }}></i>
<span style={{ fontFamily: 'var(--font-mono)' }}>{p.template}</span>
</div>
)}
<div style={{ display: 'flex', gap: 16, paddingTop: 8,
borderTop: '0.5px solid var(--border)', fontSize: 11 }}>
<div>
<div style={{ color: 'var(--fg-muted)' }}>Sessions</div>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 600, marginTop: 1 }}>{p.sessions}</div>
</div>
<div>
<div style={{ color: 'var(--fg-muted)' }}>Cron jobs</div>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 600, marginTop: 1 }}>{p.cron}</div>
</div>
<div style={{ marginLeft: 'auto', textAlign: 'right' }}>
<div style={{ color: 'var(--fg-muted)' }}>Last run</div>
<div style={{ fontSize: 12, marginTop: 1 }}>{p.lastRun}</div>
</div>
</div>
</Card>
))}
<Card padding={16} style={{
display: 'flex', alignItems: 'center', justifyContent: 'center',
border: '1px dashed var(--border-strong)', boxShadow: 'none',
background: 'transparent', minHeight: 140, cursor: 'pointer',
color: 'var(--fg-muted)', flexDirection: 'column', gap: 8,
}}>
<i data-lucide="plus" style={{ width: 24, height: 24 }}></i>
<div style={{ fontSize: 13, fontWeight: 500 }}>New project</div>
<div style={{ fontSize: 11, textAlign: 'center', maxWidth: 180 }}>From template, GitHub repo, or empty folder</div>
</Card>
</div>
</div>
</div>
);
}
window.Projects = Projects;