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:
112
docs/design/ui_kits/app/StandupScreen.js.txt
Normal file
112
docs/design/ui_kits/app/StandupScreen.js.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
// Morning standup ritual — a typeset letter from Reginald: drift, plan, nag
|
||||
const standupCSS = `
|
||||
@keyframes ct-standup-settle {
|
||||
from { opacity: 0; transform: translateY(6px); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
.ct-standup-section { opacity: 1; }
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.ct-standup-section { animation: ct-standup-settle 320ms var(--ease-out) both; }
|
||||
}
|
||||
`;
|
||||
(function inject() {
|
||||
if (typeof document !== 'undefined' && !document.getElementById('ct-standup-css')) {
|
||||
const s = document.createElement('style'); s.id = 'ct-standup-css'; s.textContent = standupCSS;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
})();
|
||||
|
||||
function StandupScreen({ onBegin, onOpenIssue }) {
|
||||
const DS = window.CommiTeaDesignSystem_20e63b;
|
||||
const { Button, Tag, Badge, Icon } = DS;
|
||||
const s = window.CT_DATA.standup;
|
||||
|
||||
const Section = ({ overline, children, order }) => (
|
||||
<section className="ct-standup-section" style={{ animationDelay: `${order * 90}ms`, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<p style={{
|
||||
font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase',
|
||||
color: 'var(--ink-3)', margin: 0, borderTop: '1px solid var(--line-1)', paddingTop: 14,
|
||||
}}>{overline}</p>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
|
||||
const toneColor = { ok: 'var(--ok)', warn: 'var(--warn)', danger: 'var(--danger)' };
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 660, margin: '0 auto' }}>
|
||||
<article style={{
|
||||
background: 'var(--surface-card)', border: '1px solid var(--line-1)',
|
||||
borderRadius: 'var(--radius-3)', boxShadow: 'var(--shadow-jade-line), var(--shadow-1)',
|
||||
padding: '36px 44px 32px', display: 'flex', flexDirection: 'column', gap: 20,
|
||||
}}>
|
||||
{/* letterhead */}
|
||||
<header className="ct-standup-section" style={{ borderBottom: 'var(--rule-double)', paddingBottom: 16 }}>
|
||||
<p style={{ font: 'var(--text-data)', color: 'var(--ink-3)', margin: '0 0 8px' }}>{s.date} · prepared 07:00</p>
|
||||
<h1 style={{ font: 'var(--text-display)', color: 'var(--ink-1)', margin: 0 }}>Morning standup</h1>
|
||||
</header>
|
||||
|
||||
<Section overline="Overnight drift" order={1}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
{s.drift.map((d) => (
|
||||
<div key={d.text} style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
|
||||
<span style={{ width: 7, height: 7, borderRadius: '50%', background: toneColor[d.tone], flexShrink: 0, position: 'relative', top: -1 }}></span>
|
||||
<span style={{ font: 'var(--text-body)', color: 'var(--ink-1)', flex: 1 }}>{d.text}</span>
|
||||
<span style={{ font: '400 11.5px var(--font-mono)', color: 'var(--ink-3)', whiteSpace: 'nowrap' }}>{d.delta}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section overline="Today's plan" order={2}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{s.plan.map((p) => (
|
||||
<div key={p.who} style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
|
||||
<span style={{
|
||||
width: 26, height: 26, borderRadius: '50%', background: 'var(--spruce-2)', color: 'var(--accent-text)',
|
||||
font: '600 10px/26px var(--font-sans)', textAlign: 'center', flexShrink: 0,
|
||||
}}>{p.who.split(' ').map((w) => w[0]).join('')}</span>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<span style={{ font: 'var(--text-body-strong)', color: 'var(--ink-1)', whiteSpace: 'nowrap' }}>{p.who}</span>
|
||||
<span style={{ font: '400 12px var(--font-mono)', color: 'var(--ink-2)' }}>{p.pick}</span>
|
||||
<span style={{ font: 'var(--text-small)', color: 'var(--ink-2)' }}>{p.title}</span>
|
||||
</div>
|
||||
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: '4px 0 0' }}>{p.why}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section overline="Stale blockers" order={3}>
|
||||
<div
|
||||
onClick={() => onOpenIssue({ id: s.nag.id, title: 'Fix lifecycle inference on merge events', labels: ['est/2d', 'p/1'], days: s.nag.days })}
|
||||
style={{
|
||||
display: 'flex', gap: 10, alignItems: 'flex-start', cursor: 'pointer',
|
||||
background: 'var(--warn-tint)', borderRadius: 'var(--radius-2)', padding: '12px 14px',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'var(--warn)', display: 'inline-flex', marginTop: 2 }}><Icon name="clock" size={15} /></span>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<span style={{ font: '500 12.5px var(--font-mono)', color: 'var(--ink-1)' }}>#{s.nag.id}</span>
|
||||
<Badge tone="warn" dot>steeping {s.nag.days}</Badge>
|
||||
<span style={{ font: '400 11.5px var(--font-mono)', color: 'var(--ink-3)' }}>blocks {s.nag.blocks.join(', ')}</span>
|
||||
</div>
|
||||
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: '5px 0 0' }}>{s.nag.text}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* sign-off */}
|
||||
<footer className="ct-standup-section" style={{ animationDelay: '360ms', borderTop: '1px solid var(--line-1)', paddingTop: 16, display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<p style={{ font: 'var(--text-agent-lg)', color: 'var(--ink-1)', margin: 0, flex: 1 }}>The kettle's on. — R.</p>
|
||||
<Button variant="ghost" onClick={onBegin}>Ask about the drift</Button>
|
||||
<Button iconRight="arrow-right" onClick={onBegin}>Begin the day</Button>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Object.assign(window, { StandupScreen });
|
||||
Reference in New Issue
Block a user