Files
nihalashetty ae67bff5a3 feat: deep-agent canvas, live observability, and multi-environment tooling
Self-hosted platform for building, testing, and shipping LangChain/LangGraph agents. Deep-agent sub-agents on the canvas, a live tracing/observability timeline, auto-provisioned built-in tools with import/export, per-environment tool variables, streamed evaluations, and per-user auth token forwarding.
2026-07-28 01:49:19 +05:30

318 lines
17 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/* Forge shared UI primitives - ported from the design handoff (primitives.jsx). */
import { CSSProperties, ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { Icon } from "./icons";
/* ---------------- Sparkline ---------------- */
export function Sparkline({
data, w = 80, h = 24, color = "var(--accent)", fill = true, strokeW = 1.5,
}: { data: number[]; w?: number; h?: number; color?: string; fill?: boolean; strokeW?: number }) {
const max = Math.max(...data, 1), min = Math.min(...data, 0);
const rng = max - min || 1;
const pts = data.map((v, i) => [(i / (data.length - 1)) * w, h - 2 - ((v - min) / rng) * (h - 4)]);
const d = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
const area = d + ` L${w} ${h} L0 ${h} Z`;
const gid = useMemo(() => "sg" + Math.random().toString(36).slice(2, 7), []);
return (
<svg width={w} height={h} style={{ display: "block", overflow: "visible" }}>
{fill && (
<defs>
<linearGradient id={gid} x1={0} y1={0} x2={0} y2={1}>
<stop offset={0} stopColor={color} stopOpacity={0.22} />
<stop offset={1} stopColor={color} stopOpacity={0} />
</linearGradient>
</defs>
)}
{fill && <path d={area} fill={`url(#${gid})`} stroke="none" />}
<path d={d} fill="none" stroke={color} strokeWidth={strokeW} strokeLinejoin="round" strokeLinecap="round" />
</svg>
);
}
/* ---------------- Donut ---------------- */
export function Donut({
segments, size = 120, thickness = 16, center,
}: { segments: { value: number; color: string }[]; size?: number; thickness?: number; center?: ReactNode }) {
const total = segments.reduce((a, s) => a + s.value, 0) || 1;
const R = (size - thickness) / 2, C = 2 * Math.PI * R;
let off = 0;
return (
<div style={{ position: "relative", width: size, height: size }}>
<svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
<circle cx={size / 2} cy={size / 2} r={R} fill="none" stroke="var(--bg-3)" strokeWidth={thickness} />
{segments.map((s, i) => {
const len = (s.value / total) * C;
const el = (
<circle key={i} cx={size / 2} cy={size / 2} r={R} fill="none" stroke={s.color}
strokeWidth={thickness} strokeDasharray={`${len} ${C - len}`} strokeDashoffset={-off}
style={{ transition: "stroke-dasharray .6s var(--ease)" }} />
);
off += len;
return el;
})}
</svg>
{center && (
<div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
{center}
</div>
)}
</div>
);
}
/* ---------------- TokenMeter (signature) ---------------- */
export function TokenMeter({
raw, projected, max, compact = false, animateKey,
}: { raw: number; projected: number; max?: number; compact?: boolean; animateKey?: any }) {
const cap = max || Math.max(raw, projected, 1) * 1.1;
const [shown, setShown] = useState<"raw" | "proj">("raw");
useEffect(() => {
setShown("raw");
const t = setTimeout(() => setShown("proj"), 420);
return () => clearTimeout(t);
}, [animateKey]);
const pctRaw = Math.min(100, (raw / cap) * 100);
const pctProj = Math.min(100, (projected / cap) * 100);
const saved = raw > 0 ? Math.round((1 - projected / raw) * 100) : 0;
if (compact) {
return (
<div className="row gap2" style={{ fontSize: 11 }}>
<div style={{ position: "relative", width: 64, height: 6, borderRadius: 999, background: "var(--bg-3)", overflow: "hidden" }}>
<i style={{ position: "absolute", inset: 0, width: pctProj + "%", background: "var(--signal)", borderRadius: 999, transition: "width .5s var(--ease)" }} />
</div>
<span className="mono-sm" style={{ color: "var(--fg-1)" }}>{projected}</span>
{saved > 0 && <span className="pill pill-ok" style={{ height: 16 }}>{"" + saved + "%"}</span>}
</div>
);
}
return (
<div className="col gap2">
<div className="row spread" style={{ fontSize: 11 }}>
<span className="t-micro">Context cost</span>
<span className="mono-sm" style={{ color: shown === "proj" ? "var(--signal)" : "var(--fg-1)" }}>
{(shown === "proj" ? projected : raw).toLocaleString()} tok
</span>
</div>
<div style={{ position: "relative", height: 10, borderRadius: 999, background: "var(--bg-3)", overflow: "hidden" }}>
<div style={{ position: "absolute", top: 0, bottom: 0, left: 0, width: pctRaw + "%", background: "var(--line-strong)", borderRadius: 999 }} />
<div style={{ position: "absolute", top: 0, bottom: 0, left: 0, width: (shown === "proj" ? pctProj : pctRaw) + "%", background: shown === "proj" ? "var(--signal)" : "var(--accent)", borderRadius: 999, transition: "width .55s var(--ease), background .3s", boxShadow: shown === "proj" ? "0 0 12px var(--signal-glow)" : "none" }} />
</div>
<div className="row spread" style={{ fontSize: 11, color: "var(--fg-2)" }}>
<span>raw <b className="mono-sm" style={{ color: "var(--fg-1)" }}>{raw.toLocaleString()}</b></span>
<span> projected <b className="mono-sm" style={{ color: "var(--signal)" }}>{projected.toLocaleString()}</b></span>
{saved > 0 && <span className="pill pill-ok"><Icon name="bolt" size={11} />{saved + "% saved"}</span>}
</div>
</div>
);
}
/* ---------------- StatusPill ---------------- */
export function StatusPill({ status, label }: { status: string; label?: string }) {
// Minimal, professional statuses: neutral pills with no colour dots. Only problem
// states (error / fail / interrupted) keep a subtle tint so they still stand out.
const map: Record<string, [string, string]> = {
done: ["pill-muted", "Done"], pass: ["pill-muted", "Passing"], active: ["pill-muted", "Active"], ready: ["pill-muted", "Ready"],
running: ["pill-muted", "Running"], processing: ["pill-muted", "Processing"], draft: ["pill-muted", "Draft"], untested: ["pill-muted", "Untested"],
error: ["pill-err", "Error"], fail: ["pill-err", "Failing"], interrupted: ["pill-warn", "Interrupted"],
};
const [cls, def] = map[status] || ["pill-muted", status];
return <span className={"pill " + cls}>{label || def}</span>;
}
/* ---------------- Avatar ---------------- */
export function Avatar({ name, size = 26, color }: { name: string; size?: number; color?: string }) {
const init = (name || "?").split(/\s|_|-/).filter(Boolean).slice(0, 2).map((s) => s[0]).join("").toUpperCase();
const hue = useMemo(() => { let h = 0; for (const c of name || "") h = (h * 31 + c.charCodeAt(0)) % 360; return h; }, [name]);
return (
<div style={{ width: size, height: size, borderRadius: "50%", flex: "none", background: color || `oklch(0.58 0.045 ${hue})`, color: "#fff", display: "flex", alignItems: "center", justifyContent: "center", fontSize: size * 0.4, fontWeight: 700, fontFamily: "var(--font-display)", letterSpacing: "-.02em" }}>
{init}
</div>
);
}
/* ---------------- Toggle ---------------- */
export function Toggle({ on, onChange, signal }: { on: boolean; onChange?: (v: boolean) => void; signal?: boolean }) {
return (
<button className={"toggle" + (on ? " on" : "") + (signal ? " signal" : "")}
onClick={(e) => { e.stopPropagation(); onChange && onChange(!on); }} aria-pressed={on} />
);
}
/* ---------------- Segmented ---------------- */
export function Segmented({ options, value, onChange }: { options: (string | { value: string; label: string; icon?: string })[]; value: string; onChange: (v: string) => void }) {
return (
<div className="segmented">
{options.map((o) => {
const val = typeof o === "string" ? o : o.value;
const lab = typeof o === "string" ? o : o.label;
return (
<button key={val} className={value === val ? "active" : ""} onClick={() => onChange(val)}>
{typeof o === "object" && o.icon && <Icon name={o.icon} size={13} style={{ marginRight: 5, verticalAlign: "-2px" }} />}
{lab}
</button>
);
})}
</div>
);
}
/* ---------------- Modal ---------------- */
export function Modal({ open, onClose, children, width = 520, title, footer }: { open: boolean; onClose: () => void; children: ReactNode; width?: number; title?: string; footer?: ReactNode }) {
// Portal to <body> so the fixed overlay escapes any transformed ancestor (e.g. the
// `.fade-up` screen wrapper). A transformed ancestor becomes the containing block for
// `position:fixed`, which otherwise traps the modal inside the content column and clips
// its header. `mounted` avoids touching `document` during SSR / first hydration.
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
useEffect(() => {
if (!open) return;
const h = (e: KeyboardEvent) => e.key === "Escape" && onClose();
window.addEventListener("keydown", h);
return () => window.removeEventListener("keydown", h);
}, [open, onClose]);
if (!open || !mounted) return null;
return createPortal(
<div className="fade-in" style={{ position: "fixed", inset: 0, zIndex: 8000, background: "rgba(8,10,14,.5)", backdropFilter: "blur(3px)", display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }} onMouseDown={onClose}>
<div className="card fade-up" style={{ width, maxWidth: "94vw", maxHeight: "88vh", boxShadow: "var(--sh-pop)", display: "flex", flexDirection: "column" }} onMouseDown={(e) => e.stopPropagation()}>
{title && (
<div className="row spread" style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)" }}>
<div className="t-h1">{title}</div>
<button className="iconbtn" onClick={onClose}><Icon name="x" size={17} /></button>
</div>
)}
<div className="scroll-y" style={{ padding: 18, flex: 1 }}>{children}</div>
{footer && <div className="row gap2" style={{ padding: "12px 18px", borderTop: "1px solid var(--line)", justifyContent: "flex-end" }}>{footer}</div>}
</div>
</div>,
document.body,
);
}
/* ---------------- Drawer ---------------- */
export function Drawer({ open, onClose, children, width = 440, title, sub }: { open: boolean; onClose: () => void; children: ReactNode; width?: number; title?: string; sub?: string }) {
return (
<div style={{ position: "fixed", inset: 0, zIndex: 7000, pointerEvents: open ? "auto" : "none" }}>
<div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(8,10,14,.4)", opacity: open ? 1 : 0, transition: "opacity var(--dur)" }} />
<div style={{ position: "absolute", top: 0, right: 0, bottom: 0, width, maxWidth: "94vw", background: "var(--bg-1)", borderLeft: "1px solid var(--line)", boxShadow: "var(--sh-pop)", transform: open ? "none" : "translateX(100%)", transition: "transform var(--dur-slow) var(--ease)", display: "flex", flexDirection: "column" }}>
{title && (
<div className="row spread" style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)" }}>
<div>
<div className="t-h1">{title}</div>
{sub && <div className="fg-2 t-caption" style={{ marginTop: 2 }}>{sub}</div>}
</div>
<button className="iconbtn" onClick={onClose}><Icon name="x" size={17} /></button>
</div>
)}
<div className="scroll-y" style={{ flex: 1 }}>{children}</div>
</div>
</div>
);
}
/* ---------------- EmptyState ---------------- */
export function EmptyState({ icon, title, sub, action }: { icon: string; title: string; sub?: string; action?: ReactNode }) {
return (
<div className="col center" style={{ padding: "48px 24px", textAlign: "center", gap: 10 }}>
<div style={{ width: 48, height: 48, borderRadius: 12, background: "var(--bg-3)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--fg-2)" }}>
<Icon name={icon} size={24} />
</div>
<div className="t-h1">{title}</div>
{sub && <div className="fg-2" style={{ maxWidth: 320 }}>{sub}</div>}
{action}
</div>
);
}
/* ---------------- Field ---------------- */
export function Field({ label, help, children, required }: { label?: string; help?: string; children: ReactNode; required?: boolean }) {
return (
<div className="col" style={{ marginBottom: 14 }}>
{label && <label className="field-label">{label}{required && <span style={{ color: "var(--accent)", marginLeft: 3 }}>*</span>}</label>}
{children}
{help && <div className="field-help">{help}</div>}
</div>
);
}
/* ---------------- Tabs ---------------- */
export function Tabs({ tabs, value, onChange, equal }: { tabs: (string | { value: string; label: string; count?: number })[]; value: string; onChange: (v: string) => void; equal?: boolean }) {
return (
<div className="row" style={{ gap: 2, borderBottom: "1px solid var(--line)" }}>
{tabs.map((t) => {
const val = typeof t === "string" ? t : t.value;
const lab = typeof t === "string" ? t : t.label;
const active = value === val;
return (
<button key={val} onClick={() => onChange(val)}
style={{ flex: equal ? "1 1 0" : "0 0 auto", minWidth: 0, textAlign: "center", background: "none", border: "none", cursor: "pointer", padding: "9px 12px", fontSize: 13, fontWeight: 600, fontFamily: "var(--font-ui)", color: active ? "var(--fg-0)" : "var(--fg-2)", borderBottom: "2px solid " + (active ? "var(--accent)" : "transparent"), marginBottom: -1, transition: "color var(--dur-fast)" }}>
{lab}
{typeof t === "object" && t.count != null && <span className="badge" style={{ marginLeft: 6 }}>{t.count}</span>}
</button>
);
})}
</div>
);
}
/* ---------------- CodeBlock ---------------- */
export function CodeBlock({ code, copyable = true, maxHeight }: { code: string; lang?: string; copyable?: boolean; maxHeight?: number }) {
const [copied, setCopied] = useState(false);
return (
<div style={{ position: "relative", background: "var(--bg-0)", border: "1px solid var(--line)", borderRadius: "var(--r-md)", overflow: "hidden" }}>
{copyable && (
<button className="iconbtn" style={{ position: "absolute", top: 6, right: 6, zIndex: 2, background: "var(--bg-1)" }}
onClick={() => { navigator.clipboard?.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 1200); }}>
<Icon name={copied ? "check" : "copy"} size={14} />
</button>
)}
<pre className="mono no-scrollbar" style={{ margin: 0, padding: "12px 14px", overflow: "auto", maxHeight, color: "var(--fg-1)", fontSize: 12 }}>
<code>{code}</code>
</pre>
</div>
);
}
/* ---------------- Tile ---------------- */
export function Tile({ icon, color = "var(--accent)", size = 36 }: { icon: string; color?: string; size?: number; glow?: boolean }) {
// Bare icon - no background, no border (minimal look, applied app-wide).
return (
<div style={{ width: size, height: size, flex: "none", display: "flex", alignItems: "center", justifyContent: "center", color }}>
<Icon name={icon} size={Math.round(size * 0.58)} />
</div>
);
}
/* ---------------- Menu ---------------- */
export function Menu({ trigger, items, align = "right" }: { trigger: ReactNode; items: { label?: string; icon?: string; onClick?: () => void; danger?: boolean; divider?: boolean }[]; align?: "left" | "right" }) {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
const h = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); };
window.addEventListener("mousedown", h);
return () => window.removeEventListener("mousedown", h);
}, [open]);
const alignStyle: CSSProperties = align === "right" ? { right: 0 } : { left: 0 };
return (
<div ref={ref} style={{ position: "relative" }}>
<div onClick={() => setOpen((o) => !o)}>{trigger}</div>
{open && (
<div className="card fade-in" style={{ position: "absolute", top: "100%", marginTop: 4, ...alignStyle, zIndex: 6000, minWidth: 168, padding: 4, boxShadow: "var(--sh-pop)" }}>
{items.map((it, i) =>
it.divider ? (
<div key={i} className="divider" style={{ margin: "4px 0" }} />
) : (
<button key={i} onClick={() => { setOpen(false); it.onClick && it.onClick(); }}
style={{ display: "flex", alignItems: "center", gap: 9, width: "100%", textAlign: "left", padding: "7px 9px", border: "none", background: "none", cursor: "pointer", borderRadius: 5, fontSize: 13, fontFamily: "var(--font-ui)", color: it.danger ? "var(--err)" : "var(--fg-1)" }}>
{it.icon && <Icon name={it.icon} size={15} />}
{it.label}
</button>
),
)}
</div>
)}
</div>
);
}