- 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>
59 lines
1.9 KiB
Plaintext
59 lines
1.9 KiB
Plaintext
import React from 'react';
|
|
|
|
const CSS = `
|
|
.ct-card {
|
|
background: var(--surface-card);
|
|
border: 1px solid var(--border-hairline);
|
|
border-radius: var(--radius-3);
|
|
box-shadow: var(--shadow-1);
|
|
}
|
|
.ct-card--jade { box-shadow: var(--shadow-jade-line), var(--shadow-1); }
|
|
.ct-card__header {
|
|
display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
|
|
padding: 16px 20px 0;
|
|
}
|
|
.ct-card__title {
|
|
font: var(--text-title);
|
|
color: var(--ink-1);
|
|
margin: 0;
|
|
}
|
|
.ct-card__overline {
|
|
font: var(--text-overline);
|
|
letter-spacing: var(--letter-spacing-wide);
|
|
text-transform: uppercase;
|
|
color: var(--ink-3);
|
|
margin: 0 0 6px;
|
|
}
|
|
.ct-card__body { padding: 14px 20px 18px; }
|
|
.ct-card__body--flush { padding: 0; }
|
|
.ct-card__footer {
|
|
display: flex; align-items: center; gap: 8px;
|
|
padding: 12px 20px;
|
|
border-top: 1px solid var(--border-hairline);
|
|
}
|
|
`;
|
|
(function inject() {
|
|
if (typeof document !== 'undefined' && !document.getElementById('ct-card-css')) {
|
|
const s = document.createElement('style'); s.id = 'ct-card-css'; s.textContent = CSS;
|
|
document.head.appendChild(s);
|
|
}
|
|
})();
|
|
|
|
export function Card({ title, overline, actions, footer, jade = false, flush = false, children, style, ...rest }) {
|
|
return (
|
|
<section className={`ct-card${jade ? ' ct-card--jade' : ''}`} style={style} {...rest}>
|
|
{(title || overline || actions) ? (
|
|
<header className="ct-card__header">
|
|
<div>
|
|
{overline ? <p className="ct-card__overline">{overline}</p> : null}
|
|
{title ? <h2 className="ct-card__title">{title}</h2> : null}
|
|
</div>
|
|
{actions ? <div style={{ display: 'flex', gap: '4px', flexShrink: 0 }}>{actions}</div> : null}
|
|
</header>
|
|
) : null}
|
|
<div className={`ct-card__body${flush ? ' ct-card__body--flush' : ''}`}>{children}</div>
|
|
{footer ? <footer className="ct-card__footer">{footer}</footer> : null}
|
|
</section>
|
|
);
|
|
}
|