// Vecton primitives + icons for Voice-to-Form
const V = {
orange: "#FF5631", purple: "#7E43BA", brown: "#24140D",
beige: "#E8E9D7", offWhite: "#F9FAF6", white: "#FFFFFF",
brownLight: "#3D2A1F", brownMid: "#6B5147", brownFaded: "#9A8A82",
orangeSubtle: "rgba(255,86,49,0.08)", orangeLight: "rgba(255,86,49,0.15)",
purpleSubtle: "rgba(126,67,186,0.08)", purpleLight: "rgba(126,67,186,0.15)",
borderLight: "rgba(36,20,13,0.10)", borderMed: "rgba(36,20,13,0.14)",
success: "#16A34A", successBg: "rgba(22,163,74,0.10)",
warning: "#D97706", warningBg: "rgba(217,119,6,0.12)",
danger: "#DC2626", dangerBg: "rgba(220,38,38,0.10)",
radius: "7px", radiusSm: "5px", radiusLg: "11px", radiusPill: "9999px",
font: "'Instrument Sans', Arial, sans-serif",
fw: { r: 400, m: 500, sb: 600 },
shadowSm: "0 1px 2px rgba(36,20,13,.08)",
shadowMd: "0 4px 12px rgba(36,20,13,.10)",
shadowLg: "0 12px 32px rgba(36,20,13,.15)",
shadowOrange: "0 4px 16px rgba(255,86,49,.25)",
};
// Lucide-style stroke icons (1.5px, round caps)
const Icon = ({ name, size = 16, color = "currentColor", sw = 1.5, style }) => {
const p = {
width: size, height: size, viewBox: "0 0 24 24", fill: "none",
stroke: color, strokeWidth: sw, strokeLinecap: "round", strokeLinejoin: "round",
style: { display: "block", flexShrink: 0, ...style },
};
const paths = {
mic: <>>,
micOff: <>>,
upload: <>>,
play: ,
pause: <>>,
stop: ,
check: ,
checkCircle: <>>,
x: <>>,
arrowRight: <>>,
arrowLeft: <>>,
alert: <>>,
info: <>>,
sparkles: <>>,
user: <>>,
briefcase: <>>,
file: <>>,
edit: <>>,
trash: <>>,
zap: ,
refresh: <>>,
download: <>>,
phone: ,
mail: <>>,
home: <>>,
creditCard: <>>,
coins: <>>,
shield: ,
lock: <>>,
hash: <>>,
circle: ,
chevDown: ,
chevUp: ,
volume: <>>,
eye: <>>,
wand: <>>,
clock: <>>,
building: <>>,
trendingUp: <>>,
waveform: <>>,
};
return ;
};
const Button = ({ variant = "primary", size = "md", icon, iconRight, children, onClick, disabled, style, type = "button" }) => {
const sizes = {
sm: { padding: "7px 12px", fontSize: 12, gap: 6 },
md: { padding: "10px 18px", fontSize: 13, gap: 8 },
lg: { padding: "13px 24px", fontSize: 14, gap: 10 },
};
const variants = {
primary: { background: V.orange, color: V.offWhite, border: "none", boxShadow: V.shadowOrange },
secondary: { background: V.brown, color: V.beige, border: "none" },
outline: { background: V.white, color: V.brown, border: `1px solid ${V.borderMed}` },
ghost: { background: "transparent", color: V.brownMid, border: "none", boxShadow: "none" },
};
return (
);
};
const Eyebrow = ({ children, style }) => (
{children}
);
const FocusCorners = ({ color = V.orange, size = 10, thick = 1.5, inset = -4 }) => {
const s = { position: "absolute", width: size, height: size, pointerEvents: "none" };
return (
<>
>
);
};
// Confidence badge with color-coded dot + %
const ConfidenceBadge = ({ score }) => {
// score 0..1
let color, bg, label;
if (score >= 0.9) { color = V.success; bg = V.successBg; label = "High"; }
else if (score >= 0.7) { color = V.warning; bg = V.warningBg; label = "Med"; }
else { color = V.danger; bg = V.dangerBg; label = "Low"; }
const pct = Math.round(score * 100);
return (
{pct}%
);
};
// Status pill
const StatusPill = ({ kind = "neutral", children, icon }) => {
const map = {
success: { bg: V.successBg, fg: V.success },
warning: { bg: V.warningBg, fg: V.warning },
danger: { bg: V.dangerBg, fg: V.danger },
info: { bg: V.purpleSubtle, fg: V.purple },
accent: { bg: V.orangeSubtle, fg: V.orange },
neutral: { bg: "rgba(36,20,13,.06)", fg: V.brownMid },
};
const s = map[kind] || map.neutral;
return (
{icon && }
{children}
);
};
// Input field with optional confidence badge + status border
const FormField = ({
label, value, onChange, placeholder, confidence, status, note, error, icon,
type = "text", options, monospace, mask, readOnly, required,
}) => {
// status: "filled" | "missing" | "low" | "corrected" | "spelled" | null
const statusColors = {
filled: { border: V.borderMed, bg: V.white },
missing: { border: V.danger, bg: V.dangerBg },
low: { border: V.warning, bg: "rgba(217,119,6,0.04)" },
corrected: { border: V.purple, bg: V.purpleSubtle },
spelled: { border: V.purple, bg: V.purpleSubtle },
empty: { border: V.borderMed, bg: V.white },
};
const sc = statusColors[status] || statusColors.empty;
const isFilled = value !== null && value !== undefined && value !== "";
return (
{confidence !== undefined && confidence !== null && isFilled && }
{icon && (
)}
{options ? (
) : (
onChange?.(e.target.value)}
placeholder={placeholder}
readOnly={readOnly}
style={{
width: "100%", padding: icon ? "10px 12px 10px 32px" : "10px 12px",
borderRadius: V.radiusSm, border: `1px solid ${sc.border}`,
fontFamily: monospace ? "'JetBrains Mono', ui-monospace, Consolas, monospace" : V.font,
fontSize: 13, color: isFilled ? V.brown : V.brownFaded,
background: sc.bg, outline: "none", boxSizing: "border-box",
fontWeight: monospace ? V.fw.sb : V.fw.r,
letterSpacing: monospace ? "0.02em" : "normal",
fontVariantNumeric: monospace ? "tabular-nums" : "normal",
transition: "border-color 0.2s, background 0.2s",
}}
onFocus={e => e.target.style.borderColor = V.orange}
onBlur={e => e.target.style.borderColor = sc.border}
/>
)}
{note && (
{note}
)}
{error && (
{error}
)}
);
};
// Section header with eyebrow + count
const SectionHeader = ({ eyebrow, title, subtitle, count, total, right }) => (
{eyebrow &&
{eyebrow}}
{title}
{subtitle &&
{subtitle}
}
{(count !== undefined || right) && (
{count !== undefined && (
{count}
/ {total} filled
)}
{right}
)}
);
const Card = ({ children, style, pad = 28 }) => (
{children}
);
// Step indicator chip for header
const StepChip = ({ num, label, state }) => {
// state: "done" | "active" | "pending"
const cfg = {
done: { bg: V.successBg, fg: V.success, border: "transparent" },
active: { bg: V.orangeLight, fg: V.orange, border: V.orange },
pending: { bg: "transparent", fg: V.brownFaded, border: V.borderMed },
}[state];
return (
{state === "done" ? : num}
{label}
);
};
// Minimal audio player widget — visual-only play/pause + duration display.
const MiniPlayer = ({ duration }) => {
const [playing, setPlaying] = React.useState(false);
const fmt = (s) => {
if (!s && s !== 0) return "--:--";
return `${String(Math.floor(s / 60)).padStart(2, "0")}:${String(Math.floor(s % 60)).padStart(2, "0")}`;
};
return (
{fmt(duration)}
);
};
Object.assign(window, { V, Icon, Button, Eyebrow, FocusCorners, ConfidenceBadge, StatusPill, FormField, SectionHeader, Card, StepChip, MiniPlayer });