Scaffold CommiTea: yarn workspaces, Electron shell, core label schema, design system
- apps/desktop: electron-vite + React + Tailwind mapped onto design tokens (preflight off; tokens/base.css owns the reset); boots to a Reginald placeholder proving fonts/tokens/core wiring - packages/core: pure TS; gitea label schema (est/*, p/*, deadline/hard) with pessimistic conflict resolution + 15 unit tests - docs/design: full design handoff (tokens, 16 component contracts, interactive 14-screen prototype, Reginald voice rules) - docs/PLAN.md: product plan (purity rule, pm-state repo, deterministic scheduler + Monte Carlo, directive log) - Deliberate deviation from novelpad stack: no ElectricSQL/PGlite — local store is a rebuildable cache over gitea REST/webhooks (better-sqlite3 in main process, arriving in P1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
130
docs/design/ui_kits/app/StatesGallery.js.txt
Normal file
130
docs/design/ui_kits/app/StatesGallery.js.txt
Normal file
@@ -0,0 +1,130 @@
|
||||
// Shared empty/trouble states + the States gallery screen
|
||||
function EmptyState({ icon, title, line, action, onAction, compact }) {
|
||||
const DS = window.CommiTeaDesignSystem_20e63b;
|
||||
const { Button, Icon } = DS;
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', gap: 10,
|
||||
padding: compact ? '28px 20px' : '52px 24px',
|
||||
}}>
|
||||
<span style={{
|
||||
width: 44, height: 44, borderRadius: '50%', background: 'var(--paper-2)', color: 'var(--ink-3)',
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||
}}><Icon name={icon} size={19} /></span>
|
||||
<div style={{ font: '400 20px/1.25 var(--font-serif-display)', color: 'var(--ink-1)' }}>{title}</div>
|
||||
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: 0, maxWidth: 380 }}>{line}</p>
|
||||
{action ? <Button style={{ marginTop: 6 }} onClick={onAction}>{action}</Button> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OfflineBanner({ retryIn }) {
|
||||
const DS = window.CommiTeaDesignSystem_20e63b;
|
||||
const { Icon } = DS;
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
background: 'var(--warn-tint)', border: '1px solid var(--warn)', borderRadius: 'var(--radius-2)',
|
||||
padding: '9px 14px',
|
||||
}}>
|
||||
<span style={{ color: 'var(--warn)', display: 'inline-flex' }}><Icon name="triangle-alert" size={15} /></span>
|
||||
<span style={{ font: 'var(--text-small)', color: 'var(--ink-1)', flex: 1 }}>
|
||||
Gitea isn't answering. I'll keep trying and say nothing more about it.
|
||||
</span>
|
||||
<span style={{ font: '400 11.5px var(--font-mono)', color: 'var(--ink-2)', whiteSpace: 'nowrap' }}>
|
||||
retry in {retryIn || '0:12'} · reads from cache
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModelAwayState() {
|
||||
const DS = window.CommiTeaDesignSystem_20e63b;
|
||||
const { Icon, Badge } = DS;
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: '14px 16px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span style={{ color: 'var(--ink-3)', display: 'inline-flex' }}><Icon name="sparkles" size={15} /></span>
|
||||
<span style={{ font: 'var(--text-body-strong)', color: 'var(--ink-2)' }}>Reginald</span>
|
||||
<span style={{ font: '400 11px var(--font-mono)', color: 'var(--ink-3)' }}>model offline</span>
|
||||
<span style={{ marginLeft: 'auto' }}><Badge>queued: 1 directive</Badge></span>
|
||||
</div>
|
||||
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: 0 }}>
|
||||
The model is away from its desk. Reads still work; writes will wait their turn.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatesScreen({ onCapture }) {
|
||||
const DS = window.CommiTeaDesignSystem_20e63b;
|
||||
const { Button, Badge, Icon } = DS;
|
||||
|
||||
const Specimen = ({ label, children }) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<span style={{ font: '400 11px var(--font-mono)', color: 'var(--ink-3)' }}>{label}</span>
|
||||
<div style={{ border: '1px dashed var(--line-2)', borderRadius: 'var(--radius-3)', background: 'var(--surface-card)', overflow: 'hidden' }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<header style={{ borderBottom: 'var(--rule-double)', paddingBottom: 14 }}>
|
||||
<h1 style={{ font: 'var(--text-display)', color: 'var(--ink-1)', margin: 0 }}>States</h1>
|
||||
<p style={{ font: 'var(--text-data)', color: 'var(--ink-3)', margin: '6px 0 0' }}>empty & trouble · specimens as wired in the app</p>
|
||||
</header>
|
||||
|
||||
<p style={{ font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase', color: 'var(--ink-3)', margin: '4px 0 0' }}>Empty</p>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
|
||||
<Specimen label="the pot · no issues">
|
||||
<EmptyState compact icon="inbox" title="The pot is empty"
|
||||
line="Tell me what you're planning and I'll draw up the tickets."
|
||||
action="Start a capture" onAction={onCapture} />
|
||||
</Specimen>
|
||||
<Specimen label="morning service · nothing scheduled">
|
||||
<EmptyState compact icon="coffee" title="Nothing to pour"
|
||||
line="Capture some work, or enjoy the silence — it never lasts." />
|
||||
</Specimen>
|
||||
<Specimen label="directives · no entries">
|
||||
<EmptyState compact icon="flag" title="No directives yet"
|
||||
line="When you overrule the scheduler, it goes on the record here — who, when, what, why." />
|
||||
</Specimen>
|
||||
<Specimen label="board search · no match">
|
||||
<EmptyState compact icon="search" title="Nothing by that name"
|
||||
line="The pot holds 24 issues; none of them answer to that." />
|
||||
</Specimen>
|
||||
</div>
|
||||
|
||||
<p style={{ font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase', color: 'var(--ink-3)', margin: '8px 0 0' }}>Trouble</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
<Specimen label="gitea unreachable · banner above main content (toggle the rail's connection dot to see it live)">
|
||||
<div style={{ padding: 12 }}><OfflineBanner /></div>
|
||||
</Specimen>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
|
||||
<Specimen label="reginald's panel · model offline">
|
||||
<ModelAwayState />
|
||||
</Specimen>
|
||||
<Specimen label="webhooks down · poll fallback">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '14px 16px' }}>
|
||||
<Badge tone="warn" dot>webhooks down</Badge>
|
||||
<span style={{ font: '400 11.5px var(--font-mono)', color: 'var(--ink-2)' }}>polling every 2 min · updates may lag</span>
|
||||
</div>
|
||||
</Specimen>
|
||||
</div>
|
||||
<Specimen label="first reconcile failed · full-screen">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '8px 0 20px' }}>
|
||||
<EmptyState compact icon="refresh-cw" title="The reconcile failed"
|
||||
line="Gitea answered, then hung up mid-sentence. Your cache is intact; nothing human is lost." />
|
||||
<div style={{ display: 'flex', gap: 8, marginTop: -6 }}>
|
||||
<Button>Try again</Button>
|
||||
<Button variant="ghost">Work from cache</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Specimen>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Object.assign(window, { EmptyState, OfflineBanner, ModelAwayState, StatesScreen });
|
||||
Reference in New Issue
Block a user