/* global React */ // Shared icons + UI primitives for Content Radar // All icons follow Lucide stroke conventions (2px stroke, currentColor) const Icon = ({ d, size = 16, stroke = 2, fill = "none", children, style }) => ( React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill, stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", style, }, children || (d && React.createElement("path", { d }))) ); const Icons = { Inbox: (p) => , Doc: (p) => , Send: (p) => , Settings: (p) => , Plus: (p) => , Check: (p) => , X: (p) => , Sun: (p) => , Moon: (p) => , ChevronDown: (p) => , ChevronUp: (p) => , ChevronRight:(p) => , ChevronLeft: (p) => , Refresh: (p) => , Edit: (p) => , Sparkles: (p) => , Copy: (p) => , Trash: (p) => , AlertTriangle: (p) => , Info: (p) => , Search: (p) => , Link: (p) => , Stop: (p) => , Spinner: ({ size = 14 }) => ( ), Clipboard: (p) => , Branch: (p) => , Dot: ({ color = "currentColor", size = 6 }) => ( ), }; // Inject keyframes once if (typeof document !== "undefined" && !document.getElementById("cr-keyframes")) { const style = document.createElement("style"); style.id = "cr-keyframes"; style.textContent = `@keyframes cr-spin { to { transform: rotate(360deg); } }`; document.head.appendChild(style); } // ---- Buttons ---- const Btn = ({ kind = "secondary", icon, children, onClick, disabled, size, type, title }) => { const cls = ["btn"]; cls.push(`btn--${kind}`); if (size) cls.push(`btn--${size}`); if (!children && icon) cls.push("btn--icon"); return ( ); }; // ---- Tag / Badge ---- const Tag = ({ tone = "default", children, icon }) => { const cls = ["tag"]; if (tone !== "default") cls.push(`tag--${tone}`); return {icon}{children}; }; // ---- Empty state ---- const EmptyState = ({ icon, title, body, actions }) => (
{icon}
{title}
{body}
{actions ?
{actions}
: null}
); // ---- Modal ---- const Modal = ({ title, onClose, children, footer, width }) => { React.useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [onClose]); return (
e.stopPropagation()}>

{title}

{children}
{footer ?
{footer}
: null}
); }; // ---- Toast ---- const Toast = ({ message, hash }) => (
{message} {hash ? {hash} : null}
); // ---- Source favicon-style chip ---- const SourceChip = ({ domain }) => { const letter = domain.replace(/^www\./, "")[0]?.toUpperCase() || "?"; return ( {letter} {domain} ); }; // ---- Time formatting ---- const formatRelative = (ts) => { const diff = Date.now() - ts; const m = Math.round(diff / 60_000); if (m < 1) return "только что"; if (m < 60) return `${m} мин назад`; const h = Math.round(m / 60); if (h < 24) return `${h} ${h === 1 ? "час" : h < 5 ? "часа" : "часов"} назад`; const d = Math.round(h / 24); return `${d} ${d === 1 ? "день" : d < 5 ? "дня" : "дней"} назад`; }; window.CR_UI = { Icons, Btn, Tag, EmptyState, Modal, Toast, SourceChip, formatRelative };