// Shared UI atoms
const UiAvatar = ({ avatar, size = 36, ring = false, style }) => {
const safeAvatar = avatar || avatarFor("?");
const s = {
width: size, height: size, borderRadius: size * 0.32,
background: safeAvatar.bg, color: safeAvatar.fg,
display: "grid", placeItems: "center",
fontSize: size * 0.45, fontWeight: 600,
flexShrink: 0,
boxShadow: ring ? `0 0 0 2px var(--bg-elev), 0 0 0 3.5px ${safeAvatar.ring}` : "none",
fontFamily: "var(--font-cn)",
...style,
};
return
{safeAvatar.initial}
;
};
const Pill = ({ children, tone = "neutral", style, icon }) => {
const tones = {
neutral: { bg: "var(--bg-sunk)", fg: "var(--ink-2)", bd: "var(--line)" },
accent: { bg: "var(--accent-soft)", fg: "var(--accent-ink)", bd: "transparent" },
sage: { bg: "var(--sage-soft)", fg: "oklch(0.35 0.06 155)", bd: "transparent" },
amber: { bg: "var(--amber-soft)", fg: "oklch(0.40 0.11 70)", bd: "transparent" },
rose: { bg: "var(--rose-soft)", fg: "oklch(0.40 0.11 20)", bd: "transparent" },
sky: { bg: "var(--sky-soft)", fg: "oklch(0.38 0.08 240)", bd: "transparent" },
violet: { bg: "var(--violet-soft)", fg: "oklch(0.40 0.09 300)", bd: "transparent" },
outline: { bg: "transparent", fg: "var(--ink-2)", bd: "var(--line-strong)" },
};
const t = tones[tone] || tones.neutral;
return (
{icon}{children}
);
};
const Btn = ({ children, variant = "ghost", size = "md", icon, iconRight, onClick, style, disabled, title }) => {
const sizes = {
sm: { padding: "5px 10px", fontSize: 12.5, radius: 8, gap: 5 },
md: { padding: "8px 14px", fontSize: 13, radius: 10, gap: 6 },
lg: { padding: "11px 18px", fontSize: 14, radius: 12, gap: 7 },
};
const variants = {
primary: {
background: "var(--accent)", color: "white", border: "1px solid transparent",
boxShadow: "0 1px 2px oklch(0.3 0.02 60 / 0.2), inset 0 1px 0 oklch(1 0 0 / 0.15)",
},
soft: {
background: "var(--accent-soft)", color: "var(--accent-ink)",
border: "1px solid transparent",
},
ghost: {
background: "transparent", color: "var(--ink-2)",
border: "1px solid transparent",
},
outline: {
background: "var(--bg-elev)", color: "var(--ink)",
border: "1px solid var(--line-strong)",
},
subtle: {
background: "var(--bg-sunk)", color: "var(--ink-2)",
border: "1px solid transparent",
},
};
const sz = sizes[size];
const v = variants[variant];
return (
);
};
const Card = ({ children, style, as = "div", padded = true, ...rest }) => {
const Tag = as;
return (
{children}
);
};
// progress bar with gradient fill
const Progress = ({ value, total, color = "var(--accent)", height = 6, showLabel = false }) => {
const pct = total > 0 ? Math.max(0, Math.min(1, value / total)) : 0;
return (
{showLabel && (
{Math.round(pct * 100)}%
)}
);
};
// sparkline
const UiSparkline = ({ values, color = "var(--accent)", height = 28, width = 80, fill = true }) => {
if (!values || !values.length) return null;
const max = Math.max(...values);
const min = Math.min(...values);
const range = max - min || 1;
const step = width / (values.length - 1);
const points = values.map((v, i) => {
const x = i * step;
const y = height - ((v - min) / range) * (height - 4) - 2;
return [x, y];
});
const path = points.map((p, i) => `${i ? "L" : "M"}${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(" ");
const area = `${path} L ${width} ${height} L 0 ${height} Z`;
return (
);
};
// tiny tooltip
const Tip = ({ text, children }) => {
const [shown, setShown] = React.useState(false);
return (
setShown(true)} onMouseLeave={() => setShown(false)}>
{children}
{shown && (
{text}
)}
);
};
Object.assign(window, { UiAvatar, Pill, Btn, Card, Progress, UiSparkline, Tip });