@usetheo/ui 0.23.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/components/composites/annotation-input/annotation-input.d.ts +38 -0
  2. package/dist/components/composites/annotation-input/index.d.ts +4 -0
  3. package/dist/components/composites/annotation-input/types.d.ts +31 -0
  4. package/dist/components/composites/annotation-summary-group/annotation-summary-group.d.ts +28 -0
  5. package/dist/components/composites/annotation-summary-group/index.d.ts +1 -0
  6. package/dist/components/composites/chat-message-card/chat-message-card.d.ts +14 -0
  7. package/dist/components/composites/chat-message-card/index.d.ts +1 -0
  8. package/dist/components/composites/comment-thread/comment-thread.d.ts +24 -0
  9. package/dist/components/composites/comment-thread/index.d.ts +2 -0
  10. package/dist/components/composites/dataset-item-diff/dataset-item-diff.d.ts +19 -0
  11. package/dist/components/composites/dataset-item-diff/index.d.ts +4 -0
  12. package/dist/components/composites/dataset-item-diff/to-diff-text.d.ts +22 -0
  13. package/dist/components/composites/diff-view/diff-section.d.ts +19 -0
  14. package/dist/components/composites/diff-view/diff-view.d.ts +39 -0
  15. package/dist/components/composites/diff-view/index.d.ts +2 -0
  16. package/dist/components/composites/evaluator-form/evaluator-form.d.ts +25 -0
  17. package/dist/components/composites/evaluator-form/index.d.ts +3 -0
  18. package/dist/components/composites/evaluator-form/types.d.ts +41 -0
  19. package/dist/components/composites/histogram/histogram.d.ts +19 -0
  20. package/dist/components/composites/histogram/index.d.ts +2 -0
  21. package/dist/components/composites/message-branch-selector/index.d.ts +1 -0
  22. package/dist/components/composites/message-branch-selector/message-branch-selector.d.ts +27 -0
  23. package/dist/components/composites/percentile-chart/index.d.ts +2 -0
  24. package/dist/components/composites/percentile-chart/percentile-chart.d.ts +37 -0
  25. package/dist/components/composites/price-breakdown/index.d.ts +2 -0
  26. package/dist/components/composites/price-breakdown/price-breakdown.d.ts +21 -0
  27. package/dist/components/composites/prompt-template-editor/extract-vars.d.ts +1 -0
  28. package/dist/components/composites/prompt-template-editor/index.d.ts +2 -0
  29. package/dist/components/composites/prompt-template-editor/prompt-template-editor.d.ts +24 -0
  30. package/dist/components/composites/prompt-version-diff/index.d.ts +4 -0
  31. package/dist/components/composites/prompt-version-diff/prompt-version-diff.d.ts +20 -0
  32. package/dist/components/composites/prompt-version-diff/to-diff-text.d.ts +26 -0
  33. package/dist/components/composites/session-summary/index.d.ts +1 -0
  34. package/dist/components/composites/session-summary/session-summary.d.ts +18 -0
  35. package/dist/components/composites/session-timeline/index.d.ts +1 -0
  36. package/dist/components/composites/session-timeline/session-timeline.d.ts +22 -0
  37. package/dist/components/composites/severity-badge/index.d.ts +2 -0
  38. package/dist/components/composites/severity-badge/severity-badge.d.ts +33 -0
  39. package/dist/components/composites/tag-input/index.d.ts +2 -0
  40. package/dist/components/composites/tag-input/tag-input.d.ts +32 -0
  41. package/dist/components/composites/token-cost-breakdown/index.d.ts +2 -0
  42. package/dist/components/composites/token-cost-breakdown/token-cost-breakdown.d.ts +26 -0
  43. package/dist/index.d.ts +43 -6
  44. package/dist/index.js +1533 -1
  45. package/dist/lib/chart/histogram.d.ts +21 -0
  46. package/dist/lib/chart/index.d.ts +1 -0
  47. package/dist/lib/diff/diff-lines.d.ts +23 -0
  48. package/dist/lib/diff/index.d.ts +1 -0
  49. package/dist/lib/session/aggregate.d.ts +18 -0
  50. package/dist/lib/session/index.d.ts +2 -0
  51. package/dist/lib/session/types.d.ts +30 -0
  52. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7535,4 +7535,1536 @@ function DeltaCell({
7535
7535
  ] });
7536
7536
  }
7537
7537
 
7538
- export { AccountMenu, ActionBar, Alert, AttributesTable, Avatar, BadgeWithDot as Badge, Breadcrumb, Button, Card, Checkbox, CodeBlock, Combobox, CommandPalette, ConfirmDialog, CopyButton, DangerZone, DataTable, DeploymentRow, DescriptionList, Dialog, DomainConfig, DropdownMenu, EmptyState, EnvVarEditor, FileDropzone, FormField, GRAPH_NODE_CAP, IOCards, Input, JsonViewer, KIND_LABEL, Label, LoginSplit, MessageCard, MessageItem, MetricCard, MetricsPanel, PageShell, Pagination, PinInput, PlanBadge, PreviewEnvCard, Progress, ProjectCard, RadioGroup, RollbackUI, ScrollArea, Select, Sheet, Sidebar, Skeleton, Slider, SocialAuthRow, SpanGraph, SpanTree, SpanTreeRow, SpanWaterfall, StatTile, StatusDot, StatusIndicator, Stepper, Switch, Table, Tabs, TaskHeader, Textarea, Timestamp, Toast, Toaster, TooltipWithStatics as Tooltip, TopNav, TraceCompare, TraceTranscript, TrendChart, UpdateBanner, VIRTUALIZE_THRESHOLD, aggregateCost, alignSpanTrees, asChat, avatarVariants, badgeVariants, buildLayeredGraph, buttonVariants, cn, computeBarLayout, computePageRange, computeTraceBounds, contentText, deriveSpanKind, deriveSteps, durationMs, flattenAll, flattenVisible, groupByNamespace, isNearBottom, isRedactedThinking, isSpanError, linScale, matchesAccept, niceAxisTicks, niceMax, packRows, prettyValue, seriesPath, sheetVariants, spanCostUsd, toNs, toTranscriptRows, traceMetrics, useStickToBottom, useToast, validateFiles };
7538
+ // src/lib/session/aggregate.ts
7539
+ function sessionBounds(items) {
7540
+ let minStart = null;
7541
+ let maxEnd = null;
7542
+ for (const it of items) {
7543
+ const start = toNs(it.startTime);
7544
+ if (start === null) continue;
7545
+ if (minStart === null || start < minStart) minStart = start;
7546
+ const end = toNs(it.endTime ?? null) ?? start;
7547
+ const eff = end >= start ? end : start;
7548
+ if (maxEnd === null || eff > maxEnd) maxEnd = eff;
7549
+ }
7550
+ const s = minStart ?? 0n;
7551
+ return { startNs: s, endNs: maxEnd ?? s };
7552
+ }
7553
+ function aggregateSession(items) {
7554
+ let totalCostUsd = 0;
7555
+ let totalTokens = 0;
7556
+ let errorCount = 0;
7557
+ const models = /* @__PURE__ */ new Set();
7558
+ for (const it of items) {
7559
+ if (typeof it.costUsd === "number" && Number.isFinite(it.costUsd) && it.costUsd > 0) {
7560
+ totalCostUsd += it.costUsd;
7561
+ }
7562
+ if (typeof it.totalTokens === "number" && Number.isFinite(it.totalTokens) && it.totalTokens > 0) {
7563
+ totalTokens += it.totalTokens;
7564
+ }
7565
+ if (it.status === "ERROR") errorCount++;
7566
+ if (it.model) models.add(it.model);
7567
+ }
7568
+ const { startNs, endNs } = sessionBounds(items);
7569
+ const windowMs = Number((endNs - startNs) / 1000000n);
7570
+ return {
7571
+ traceCount: items.length,
7572
+ windowMs,
7573
+ totalCostUsd,
7574
+ totalTokens,
7575
+ errorCount,
7576
+ models: [...models].sort()
7577
+ };
7578
+ }
7579
+ var defaultCost4 = (usd) => `$${usd.toFixed(4)}`;
7580
+ function SessionSummary({
7581
+ items,
7582
+ metrics,
7583
+ formatCostUsd = defaultCost4,
7584
+ className,
7585
+ "aria-label": ariaLabel = "Session summary"
7586
+ }) {
7587
+ const m = useMemo(() => metrics ?? aggregateSession(items ?? []), [metrics, items]);
7588
+ return /* @__PURE__ */ jsxs(
7589
+ "dl",
7590
+ {
7591
+ "data-slot": "session-summary",
7592
+ "aria-label": ariaLabel,
7593
+ className: cn(
7594
+ "grid grid-cols-2 gap-3 rounded-md border border-border bg-muted/20 p-3 text-xs sm:grid-cols-3 lg:grid-cols-6",
7595
+ className
7596
+ ),
7597
+ children: [
7598
+ /* @__PURE__ */ jsx(Stat2, { label: "Traces", value: String(m.traceCount) }),
7599
+ /* @__PURE__ */ jsx(
7600
+ Stat2,
7601
+ {
7602
+ label: "Duration",
7603
+ value: m.windowMs > 0 ? formatDurationMs(m.windowMs) : "\u2014",
7604
+ testId: "session-duration"
7605
+ }
7606
+ ),
7607
+ /* @__PURE__ */ jsx(Stat2, { label: "Cost", value: formatCostUsd(m.totalCostUsd), testId: "session-cost" }),
7608
+ /* @__PURE__ */ jsx(Stat2, { label: "Tokens", value: String(m.totalTokens), testId: "session-tokens" }),
7609
+ /* @__PURE__ */ jsxs("div", { children: [
7610
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Errors" }),
7611
+ /* @__PURE__ */ jsx(
7612
+ "dd",
7613
+ {
7614
+ "data-slot": "session-error",
7615
+ "data-error": m.errorCount > 0 ? "true" : void 0,
7616
+ className: cn(
7617
+ "font-medium font-mono tabular-nums",
7618
+ m.errorCount > 0 && "text-destructive"
7619
+ ),
7620
+ children: m.errorCount
7621
+ }
7622
+ )
7623
+ ] }),
7624
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
7625
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Models" }),
7626
+ /* @__PURE__ */ jsx("dd", { "data-testid": "session-models", className: "flex flex-wrap gap-1", children: m.models.length > 0 ? m.models.map((model) => /* @__PURE__ */ jsx(BadgeWithDot, { variant: "outline", className: "font-mono", children: model }, model)) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" }) })
7627
+ ] })
7628
+ ]
7629
+ }
7630
+ );
7631
+ }
7632
+ function Stat2({ label, value, testId }) {
7633
+ return /* @__PURE__ */ jsxs("div", { children: [
7634
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: label }),
7635
+ /* @__PURE__ */ jsx("dd", { "data-testid": testId, className: "font-medium font-mono tabular-nums", children: value })
7636
+ ] });
7637
+ }
7638
+ var defaultTimestamp2 = (ns) => new Date(Number(ns / 1000000n)).toISOString();
7639
+ var defaultCost5 = (usd) => `$${usd.toFixed(4)}`;
7640
+ function SessionTimeline({
7641
+ items,
7642
+ selectedId,
7643
+ onSelect,
7644
+ formatTimestamp = defaultTimestamp2,
7645
+ formatCostUsd = defaultCost5,
7646
+ virtualizeThreshold = 200,
7647
+ className,
7648
+ "aria-label": ariaLabel = "Session timeline"
7649
+ }) {
7650
+ const sorted = useMemo(() => {
7651
+ const withNs = items.map((it) => ({ it, ns: toNs(it.startTime) ?? 0n }));
7652
+ withNs.sort((a, b) => a.ns < b.ns ? -1 : a.ns > b.ns ? 1 : a.it.id < b.it.id ? -1 : 1);
7653
+ return withNs.map((w) => w.it);
7654
+ }, [items]);
7655
+ const bounds = useMemo(() => sessionBounds(sorted), [sorted]);
7656
+ if (sorted.length === 0) {
7657
+ return /* @__PURE__ */ jsx(EmptyState, { title: "No traces", description: "This session has no traces to replay." });
7658
+ }
7659
+ const virtualized = sorted.length >= virtualizeThreshold;
7660
+ const ctx = { bounds, selectedId, onSelect, formatTimestamp, formatCostUsd };
7661
+ return (
7662
+ // biome-ignore lint/a11y/useSemanticElements: role="list" keeps the virtualized wrapper divs valid (a <div> cannot be an <li>)
7663
+ /* @__PURE__ */ jsx(
7664
+ "div",
7665
+ {
7666
+ role: "list",
7667
+ "data-slot": "session-timeline",
7668
+ "aria-label": ariaLabel,
7669
+ className: cn("space-y-1", className),
7670
+ children: virtualized ? /* @__PURE__ */ jsx(VirtualRows, { items: sorted, ...ctx }) : sorted.map((it) => /* @__PURE__ */ jsx(Row4, { item: it, ...ctx }, it.id))
7671
+ }
7672
+ )
7673
+ );
7674
+ }
7675
+ function Row4({ item, ...ctx }) {
7676
+ const { leftPct, widthPct, unbounded } = computeBarLayout(
7677
+ item.startTime,
7678
+ item.endTime,
7679
+ ctx.bounds.startNs,
7680
+ ctx.bounds.endNs
7681
+ );
7682
+ const error = isSpanError(item.status);
7683
+ const selected = item.id === ctx.selectedId;
7684
+ const startNs = toNs(item.startTime);
7685
+ const dur = durationMs({ startTime: item.startTime, endTime: item.endTime });
7686
+ return (
7687
+ // biome-ignore lint/a11y/useSemanticElements: pairs with the role="list" container (virtualized wrappers forbid <li>)
7688
+ /* @__PURE__ */ jsx("div", { role: "listitem", children: /* @__PURE__ */ jsxs(
7689
+ "button",
7690
+ {
7691
+ type: "button",
7692
+ "data-slot": "session-timeline-row",
7693
+ "aria-current": selected ? "true" : void 0,
7694
+ onClick: () => ctx.onSelect(item.id),
7695
+ className: cn(
7696
+ "flex w-full items-center gap-3 rounded-md p-2 text-left hover:bg-muted/40",
7697
+ selected && "bg-muted ring-1 ring-primary"
7698
+ ),
7699
+ children: [
7700
+ /* @__PURE__ */ jsx("span", { className: "w-32 shrink-0 font-mono text-[10px] text-muted-foreground", children: startNs !== null ? ctx.formatTimestamp(startNs) : "\u2014" }),
7701
+ /* @__PURE__ */ jsx("span", { "data-testid": "session-trace-name", className: "w-40 shrink-0 truncate font-mono text-xs", children: item.name ?? item.id }),
7702
+ /* @__PURE__ */ jsx(
7703
+ "span",
7704
+ {
7705
+ className: "relative block h-4 flex-1 overflow-hidden rounded-sm bg-muted/30",
7706
+ "aria-hidden": "true",
7707
+ children: /* @__PURE__ */ jsx(
7708
+ "span",
7709
+ {
7710
+ "data-slot": "session-timeline-bar",
7711
+ "data-error": error ? "true" : void 0,
7712
+ "data-unbounded": unbounded ? "true" : void 0,
7713
+ style: { left: `${leftPct}%`, width: `${widthPct}%`, minWidth: "2px" },
7714
+ className: cn(
7715
+ "absolute top-0.5 bottom-0.5 rounded-sm",
7716
+ error ? "bg-destructive" : "bg-primary/60",
7717
+ unbounded && "border border-primary/60 border-dashed bg-transparent"
7718
+ )
7719
+ }
7720
+ )
7721
+ }
7722
+ ),
7723
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground tabular-nums", children: dur !== null ? formatDurationMs(dur) : "\u2014" }),
7724
+ item.costUsd !== void 0 ? /* @__PURE__ */ jsx(BadgeWithDot, { variant: "outline", className: "shrink-0 font-mono text-[10px]", children: ctx.formatCostUsd(item.costUsd) }) : null,
7725
+ error ? /* @__PURE__ */ jsx(BadgeWithDot, { variant: "destructive", className: "shrink-0", children: "error" }) : null
7726
+ ]
7727
+ }
7728
+ ) })
7729
+ );
7730
+ }
7731
+ function VirtualRows({ items, ...ctx }) {
7732
+ const parentRef = useRef(null);
7733
+ const virtualizer = useVirtualizer({
7734
+ count: items.length,
7735
+ getScrollElement: () => parentRef.current,
7736
+ estimateSize: () => 44,
7737
+ overscan: 12,
7738
+ initialRect: { width: 640, height: 640 }
7739
+ });
7740
+ return /* @__PURE__ */ jsx(
7741
+ "div",
7742
+ {
7743
+ ref: parentRef,
7744
+ "data-slot": "session-timeline-virtual",
7745
+ className: "max-h-[70vh] overflow-auto",
7746
+ children: /* @__PURE__ */ jsx("div", { style: { height: virtualizer.getTotalSize(), position: "relative", width: "100%" }, children: virtualizer.getVirtualItems().map((vi) => {
7747
+ const item = items[vi.index];
7748
+ if (!item) return null;
7749
+ return /* @__PURE__ */ jsx(
7750
+ "div",
7751
+ {
7752
+ "data-index": vi.index,
7753
+ ref: virtualizer.measureElement,
7754
+ style: {
7755
+ position: "absolute",
7756
+ top: 0,
7757
+ left: 0,
7758
+ width: "100%",
7759
+ transform: `translateY(${vi.start}px)`
7760
+ },
7761
+ children: /* @__PURE__ */ jsx(Row4, { item, ...ctx })
7762
+ },
7763
+ `${vi.index}-${item.id}`
7764
+ );
7765
+ }) })
7766
+ }
7767
+ );
7768
+ }
7769
+
7770
+ // src/lib/chart/histogram.ts
7771
+ function computeHistogram(values, binCount) {
7772
+ if (!Number.isFinite(binCount) || binCount <= 0) return [];
7773
+ const finite = values.filter((v) => Number.isFinite(v));
7774
+ if (finite.length === 0) return [];
7775
+ const n = Math.floor(binCount);
7776
+ let min = Number.POSITIVE_INFINITY;
7777
+ let max = Number.NEGATIVE_INFINITY;
7778
+ for (const v of finite) {
7779
+ if (v < min) min = v;
7780
+ if (v > max) max = v;
7781
+ }
7782
+ const span = max - min;
7783
+ const width = span / n;
7784
+ const bins = Array.from({ length: n }, (_, i) => ({
7785
+ lo: span === 0 ? min : min + i * width,
7786
+ hi: span === 0 ? min : min + (i + 1) * width,
7787
+ count: 0
7788
+ }));
7789
+ for (const v of finite) {
7790
+ const idx = span === 0 ? 0 : Math.min(n - 1, Math.floor((v - min) / width));
7791
+ const bin = bins[idx];
7792
+ if (bin) bin.count += 1;
7793
+ }
7794
+ return bins;
7795
+ }
7796
+
7797
+ // src/lib/diff/diff-lines.ts
7798
+ function toLines(text) {
7799
+ return text === "" ? [] : text.split("\n");
7800
+ }
7801
+ function diffLines(oldText, newText) {
7802
+ const a = toLines(oldText);
7803
+ const b = toLines(newText);
7804
+ const n = a.length;
7805
+ const m = b.length;
7806
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
7807
+ for (let i2 = n - 1; i2 >= 0; i2--) {
7808
+ const row = lcs[i2];
7809
+ const next = lcs[i2 + 1];
7810
+ for (let j2 = m - 1; j2 >= 0; j2--) {
7811
+ row[j2] = a[i2] === b[j2] ? next[j2 + 1] + 1 : Math.max(next[j2], row[j2 + 1]);
7812
+ }
7813
+ }
7814
+ const rows = [];
7815
+ let i = 0;
7816
+ let j = 0;
7817
+ while (i < n && j < m) {
7818
+ const ai = a[i];
7819
+ const bj = b[j];
7820
+ if (ai === bj) {
7821
+ rows.push({ kind: "eq", text: ai, leftNo: i + 1, rightNo: j + 1 });
7822
+ i++;
7823
+ j++;
7824
+ continue;
7825
+ }
7826
+ const skipA = lcs[i + 1][j];
7827
+ const skipB = lcs[i][j + 1];
7828
+ if (skipA >= skipB) {
7829
+ rows.push({ kind: "del", text: ai, leftNo: i + 1 });
7830
+ i++;
7831
+ } else {
7832
+ rows.push({ kind: "add", text: bj, rightNo: j + 1 });
7833
+ j++;
7834
+ }
7835
+ }
7836
+ while (i < n) {
7837
+ rows.push({ kind: "del", text: a[i], leftNo: i + 1 });
7838
+ i++;
7839
+ }
7840
+ while (j < m) {
7841
+ rows.push({ kind: "add", text: b[j], rightNo: j + 1 });
7842
+ j++;
7843
+ }
7844
+ return rows;
7845
+ }
7846
+ var MARKER = { add: "+", del: "-", eq: " " };
7847
+ var ROW_CLASS = {
7848
+ add: "bg-success/10 text-success",
7849
+ del: "bg-destructive/10 text-destructive",
7850
+ eq: "text-muted-foreground"
7851
+ };
7852
+ var NO_COL = "select-none pr-2 text-right align-top text-muted-foreground/60 tabular-nums";
7853
+ var MARKER_COL = "select-none pr-1 text-center align-top";
7854
+ var TEXT_COL = "align-top whitespace-pre-wrap break-words";
7855
+ var HEAD_ROW = "text-left text-label-caps text-muted-foreground uppercase";
7856
+ var NumHead = () => /* @__PURE__ */ jsx("th", { className: "pr-2", scope: "col", children: "#" });
7857
+ var SideHead = ({ children }) => /* @__PURE__ */ jsx("th", { "data-slot": "diff-view-side", scope: "col", children });
7858
+ function DiffCell({ no, marker, text }) {
7859
+ const empty = text === null;
7860
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
7861
+ /* @__PURE__ */ jsx("td", { className: NO_COL, children: no ?? "" }),
7862
+ /* @__PURE__ */ jsx("td", { className: cn(TEXT_COL, empty && "bg-muted/30"), children: !empty && /* @__PURE__ */ jsxs(Fragment, { children: [
7863
+ /* @__PURE__ */ jsx("span", { className: MARKER_COL, "aria-hidden": "true", children: marker }),
7864
+ text
7865
+ ] }) })
7866
+ ] });
7867
+ }
7868
+ function columnsFor(mode, oldLabel, newLabel) {
7869
+ if (mode === "unified") {
7870
+ const header = oldLabel && newLabel ? `${oldLabel} \u2192 ${newLabel}` : oldLabel ?? newLabel ?? "Changes";
7871
+ return [
7872
+ {
7873
+ header,
7874
+ // Unified keeps both line numbers; the number cells are rendered
7875
+ // separately, so here we only carry the new-side number for the row.
7876
+ cell: (row) => ({
7877
+ no: row.rightNo ?? row.leftNo,
7878
+ marker: MARKER[row.kind],
7879
+ text: row.text
7880
+ })
7881
+ }
7882
+ ];
7883
+ }
7884
+ return [
7885
+ {
7886
+ header: oldLabel ?? "Old",
7887
+ cell: (row) => ({
7888
+ no: row.leftNo,
7889
+ marker: row.kind === "del" ? "-" : " ",
7890
+ text: row.kind === "add" ? null : row.text
7891
+ })
7892
+ },
7893
+ {
7894
+ header: newLabel ?? "New",
7895
+ cell: (row) => ({
7896
+ no: row.rightNo,
7897
+ marker: row.kind === "add" ? "+" : " ",
7898
+ text: row.kind === "del" ? null : row.text
7899
+ })
7900
+ }
7901
+ ];
7902
+ }
7903
+ function DiffBody({ diff, mode, oldLabel, newLabel }) {
7904
+ const columns = columnsFor(mode, oldLabel, newLabel);
7905
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
7906
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: HEAD_ROW, children: columns.map((col) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
7907
+ /* @__PURE__ */ jsx(NumHead, {}),
7908
+ /* @__PURE__ */ jsx(SideHead, { children: col.header })
7909
+ ] }, col.header)) }) }),
7910
+ /* @__PURE__ */ jsx("tbody", { children: diff.map((row, i) => /* @__PURE__ */ jsx(
7911
+ "tr",
7912
+ {
7913
+ "data-diff": row.kind,
7914
+ className: ROW_CLASS[row.kind],
7915
+ children: columns.map((col) => /* @__PURE__ */ jsx(DiffCell, { ...col.cell(row) }, col.header))
7916
+ },
7917
+ i
7918
+ )) })
7919
+ ] });
7920
+ }
7921
+ var DiffView = forwardRef((props, ref) => {
7922
+ const { oldText, newText, mode = "split", oldLabel, newLabel, className, ...rest } = props;
7923
+ const diff = diffLines(oldText, newText);
7924
+ const hasChanges = diff.some((r) => r.kind !== "eq");
7925
+ const caption = `Diff${oldLabel ? ` of ${oldLabel}` : ""}${newLabel ? ` vs ${newLabel}` : ""}`;
7926
+ let body;
7927
+ if (!hasChanges) {
7928
+ body = /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { "data-slot": "diff-view-empty", className: "p-3 text-muted-foreground italic", children: "No changes." }) }) });
7929
+ } else {
7930
+ body = /* @__PURE__ */ jsx(DiffBody, { diff, mode, oldLabel, newLabel });
7931
+ }
7932
+ return /* @__PURE__ */ jsxs(
7933
+ "table",
7934
+ {
7935
+ "data-slot": "diff-view",
7936
+ "data-mode": mode,
7937
+ ref,
7938
+ className: cn("w-full border-collapse font-mono text-xs", className),
7939
+ ...rest,
7940
+ children: [
7941
+ /* @__PURE__ */ jsx("caption", { className: "sr-only", children: hasChanges ? caption : `${caption} \u2014 no changes` }),
7942
+ body
7943
+ ]
7944
+ }
7945
+ );
7946
+ });
7947
+ DiffView.displayName = "DiffView";
7948
+ function DiffSection({ title, oldText, newText, ...rest }) {
7949
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-1", children: [
7950
+ /* @__PURE__ */ jsx("h4", { className: "font-semibold text-label-caps text-muted-foreground uppercase", children: title }),
7951
+ /* @__PURE__ */ jsx(DiffView, { oldText, newText, ...rest })
7952
+ ] });
7953
+ }
7954
+
7955
+ // src/components/composites/prompt-version-diff/to-diff-text.ts
7956
+ function templateToText(template) {
7957
+ if (typeof template === "string") return template;
7958
+ return template.map((m) => `${m.role}: ${m.content}`).join("\n\n");
7959
+ }
7960
+ function configToText(config) {
7961
+ if (!config || Object.keys(config).length === 0) return "";
7962
+ return JSON.stringify(config, null, 2);
7963
+ }
7964
+ function PromptVersionDiff(props) {
7965
+ const { oldPrompt, newPrompt, mode, oldLabel, newLabel, className, ...rest } = props;
7966
+ const shared = { mode, oldLabel, newLabel };
7967
+ const oldConfig = configToText(oldPrompt.config);
7968
+ const newConfig = configToText(newPrompt.config);
7969
+ const hasConfig = oldConfig !== "" || newConfig !== "";
7970
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "prompt-version-diff", className: cn("space-y-4", className), ...rest, children: [
7971
+ /* @__PURE__ */ jsx(
7972
+ DiffSection,
7973
+ {
7974
+ title: "Content",
7975
+ oldText: templateToText(oldPrompt.template),
7976
+ newText: templateToText(newPrompt.template),
7977
+ ...shared
7978
+ }
7979
+ ),
7980
+ hasConfig && /* @__PURE__ */ jsx(DiffSection, { title: "Config", oldText: oldConfig, newText: newConfig, ...shared })
7981
+ ] });
7982
+ }
7983
+
7984
+ // src/components/composites/dataset-item-diff/to-diff-text.ts
7985
+ function fieldToText(value) {
7986
+ if (value === void 0 || value === null) return "";
7987
+ if (typeof value === "string") return value;
7988
+ return JSON.stringify(value, null, 2);
7989
+ }
7990
+ function fieldPresent(a, b) {
7991
+ return a !== void 0 && a !== null || b !== void 0 && b !== null;
7992
+ }
7993
+ var FIELDS = [
7994
+ { key: "input", title: "Input" },
7995
+ { key: "expectedOutput", title: "Expected output" },
7996
+ { key: "metadata", title: "Metadata" }
7997
+ ];
7998
+ function DatasetItemDiff(props) {
7999
+ const { oldItem, newItem, mode, oldLabel, newLabel, className, ...rest } = props;
8000
+ const shared = { mode, oldLabel, newLabel };
8001
+ return /* @__PURE__ */ jsx("div", { "data-slot": "dataset-item-diff", className: cn("space-y-4", className), ...rest, children: FIELDS.filter((f) => fieldPresent(oldItem[f.key], newItem[f.key])).map((f) => /* @__PURE__ */ jsx(
8002
+ DiffSection,
8003
+ {
8004
+ title: f.title,
8005
+ oldText: fieldToText(oldItem[f.key]),
8006
+ newText: fieldToText(newItem[f.key]),
8007
+ ...shared
8008
+ },
8009
+ f.key
8010
+ )) });
8011
+ }
8012
+ var VIEW_W2 = 600;
8013
+ var PAD3 = { top: 8, right: 8, bottom: 4, left: 40 };
8014
+ var BAR_GAP = 2;
8015
+ var Histogram = forwardRef((props, ref) => {
8016
+ const {
8017
+ title,
8018
+ height = 180,
8019
+ valueFormatter = (v) => String(v),
8020
+ className,
8021
+ bins: binsProp,
8022
+ values,
8023
+ binCount,
8024
+ ...rest
8025
+ } = props;
8026
+ const bins = binsProp ?? computeHistogram(values ?? [], binCount ?? 0);
8027
+ const hasData = bins.length > 0 && bins.some((b) => b.count > 0);
8028
+ if (!hasData) {
8029
+ return /* @__PURE__ */ jsxs(
8030
+ "figure",
8031
+ {
8032
+ "data-slot": "histogram",
8033
+ ref,
8034
+ className: cn("space-y-1", className),
8035
+ "aria-label": `${title} \u2014 no data`,
8036
+ ...rest,
8037
+ children: [
8038
+ /* @__PURE__ */ jsx("figcaption", { className: "font-semibold text-label-caps text-muted-foreground uppercase", children: title }),
8039
+ /* @__PURE__ */ jsx(
8040
+ "div",
8041
+ {
8042
+ "data-slot": "histogram-empty",
8043
+ className: "flex items-center justify-center rounded-md border border-dashed text-body-sm text-muted-foreground",
8044
+ style: { height },
8045
+ children: "No data to distribute."
8046
+ }
8047
+ )
8048
+ ]
8049
+ }
8050
+ );
8051
+ }
8052
+ const countMax = niceMax(bins.map((b) => b.count)) || 1;
8053
+ const yScale = linScale([0, countMax], [height - PAD3.bottom, PAD3.top]);
8054
+ const plotW = VIEW_W2 - PAD3.left - PAD3.right;
8055
+ const slotW = plotW / bins.length;
8056
+ const barW = Math.max(1, slotW - BAR_GAP);
8057
+ return /* @__PURE__ */ jsxs("figure", { "data-slot": "histogram", ref, className: cn("space-y-1", className), ...rest, children: [
8058
+ /* @__PURE__ */ jsx("figcaption", { className: "font-semibold text-label-caps text-muted-foreground uppercase", children: title }),
8059
+ /* @__PURE__ */ jsxs(
8060
+ "svg",
8061
+ {
8062
+ viewBox: `0 0 ${VIEW_W2} ${height}`,
8063
+ preserveAspectRatio: "none",
8064
+ role: "img",
8065
+ "aria-label": `${title} distribution`,
8066
+ className: "w-full",
8067
+ style: { height },
8068
+ children: [
8069
+ /* @__PURE__ */ jsx(
8070
+ "line",
8071
+ {
8072
+ x1: PAD3.left,
8073
+ y1: height - PAD3.bottom,
8074
+ x2: VIEW_W2 - PAD3.right,
8075
+ y2: height - PAD3.bottom,
8076
+ className: "stroke-border",
8077
+ strokeWidth: 1
8078
+ }
8079
+ ),
8080
+ /* @__PURE__ */ jsx("text", { x: 4, y: PAD3.top + 8, className: "fill-muted-foreground text-[10px]", children: valueFormatter(countMax) }),
8081
+ /* @__PURE__ */ jsx("text", { x: 4, y: height - PAD3.bottom, className: "fill-muted-foreground text-[10px]", children: "0" }),
8082
+ bins.map((b, i) => {
8083
+ const y = yScale(b.count);
8084
+ const barH = Math.max(0, height - PAD3.bottom - y);
8085
+ return /* @__PURE__ */ jsx(
8086
+ "rect",
8087
+ {
8088
+ "data-slot": "histogram-bar",
8089
+ "data-count": b.count,
8090
+ x: PAD3.left + i * slotW + BAR_GAP / 2,
8091
+ y,
8092
+ width: barW,
8093
+ height: barH,
8094
+ className: "fill-primary",
8095
+ children: /* @__PURE__ */ jsx("title", { children: `${valueFormatter(b.lo)}\u2013${valueFormatter(b.hi)}: ${b.count}` })
8096
+ },
8097
+ i
8098
+ );
8099
+ })
8100
+ ]
8101
+ }
8102
+ ),
8103
+ /* @__PURE__ */ jsxs("table", { "data-slot": "histogram-table", className: "sr-only", children: [
8104
+ /* @__PURE__ */ jsxs("caption", { children: [
8105
+ title,
8106
+ " \u2014 data table"
8107
+ ] }),
8108
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
8109
+ /* @__PURE__ */ jsx("th", { children: "Range" }),
8110
+ /* @__PURE__ */ jsx("th", { children: "Count" })
8111
+ ] }) }),
8112
+ /* @__PURE__ */ jsx("tbody", { children: bins.map((b, i) => (
8113
+ // biome-ignore lint/suspicious/noArrayIndexKey: bins are positional buckets — index IS the identity
8114
+ /* @__PURE__ */ jsxs("tr", { children: [
8115
+ /* @__PURE__ */ jsx("td", { children: `${valueFormatter(b.lo)}\u2013${valueFormatter(b.hi)}` }),
8116
+ /* @__PURE__ */ jsx("td", { children: b.count })
8117
+ ] }, i)
8118
+ )) })
8119
+ ] })
8120
+ ] });
8121
+ });
8122
+ Histogram.displayName = "Histogram";
8123
+ var VIEW_W3 = 600;
8124
+ var PAD4 = { top: 8, right: 8, bottom: 4, left: 40 };
8125
+ function bandPath(upper, lower, xScale, yScale) {
8126
+ const runs = [];
8127
+ let run = [];
8128
+ for (let i = 0; i < upper.length; i++) {
8129
+ const u = upper[i];
8130
+ const l = lower[i];
8131
+ if (u !== void 0 && l !== void 0 && Number.isFinite(u) && Number.isFinite(l)) {
8132
+ run.push(i);
8133
+ } else if (run.length > 0) {
8134
+ runs.push(run);
8135
+ run = [];
8136
+ }
8137
+ }
8138
+ if (run.length > 0) runs.push(run);
8139
+ return runs.map((idxs) => {
8140
+ const top = idxs.map(
8141
+ (i, k) => `${k === 0 ? "M" : "L"}${xScale(i).toFixed(2)},${yScale(upper[i]).toFixed(2)}`
8142
+ );
8143
+ const bottom = idxs.slice().reverse().map((i) => `L${xScale(i).toFixed(2)},${yScale(lower[i]).toFixed(2)}`);
8144
+ return `${top.join(" ")} ${bottom.join(" ")} Z`;
8145
+ }).join(" ");
8146
+ }
8147
+ var PercentileChart = forwardRef((props, ref) => {
8148
+ const {
8149
+ title,
8150
+ buckets,
8151
+ height = 180,
8152
+ valueFormatter = (v) => String(v),
8153
+ className,
8154
+ ...rest
8155
+ } = props;
8156
+ const hasData = buckets.length > 0;
8157
+ if (!hasData) {
8158
+ return /* @__PURE__ */ jsxs(
8159
+ "figure",
8160
+ {
8161
+ "data-slot": "percentile-chart",
8162
+ ref,
8163
+ className: cn("space-y-1", className),
8164
+ "aria-label": `${title} \u2014 no data`,
8165
+ ...rest,
8166
+ children: [
8167
+ /* @__PURE__ */ jsx("figcaption", { className: "font-semibold text-label-caps text-muted-foreground uppercase", children: title }),
8168
+ /* @__PURE__ */ jsx(
8169
+ "div",
8170
+ {
8171
+ "data-slot": "percentile-empty",
8172
+ className: "flex items-center justify-center rounded-md border border-dashed text-body-sm text-muted-foreground",
8173
+ style: { height },
8174
+ children: "No data in this time range."
8175
+ }
8176
+ )
8177
+ ]
8178
+ }
8179
+ );
8180
+ }
8181
+ const p50 = buckets.map((b) => b.p50);
8182
+ const p95 = buckets.map((b) => b.p95);
8183
+ const p99 = buckets.map((b) => b.p99);
8184
+ const yMax = niceMax([...p99]) || 1;
8185
+ const xScale = linScale([0, Math.max(1, buckets.length - 1)], [PAD4.left, VIEW_W3 - PAD4.right]);
8186
+ const yScale = linScale([0, yMax], [height - PAD4.bottom, PAD4.top]);
8187
+ const p50Points = p50.map((y, x) => ({ x, y }));
8188
+ return /* @__PURE__ */ jsxs("figure", { "data-slot": "percentile-chart", ref, className: cn("space-y-1", className), ...rest, children: [
8189
+ /* @__PURE__ */ jsx("figcaption", { className: "font-semibold text-label-caps text-muted-foreground uppercase", children: title }),
8190
+ /* @__PURE__ */ jsxs(
8191
+ "svg",
8192
+ {
8193
+ viewBox: `0 0 ${VIEW_W3} ${height}`,
8194
+ preserveAspectRatio: "none",
8195
+ role: "img",
8196
+ "aria-label": `${title} percentiles`,
8197
+ className: "w-full",
8198
+ style: { height },
8199
+ children: [
8200
+ /* @__PURE__ */ jsx(
8201
+ "line",
8202
+ {
8203
+ x1: PAD4.left,
8204
+ y1: height - PAD4.bottom,
8205
+ x2: VIEW_W3 - PAD4.right,
8206
+ y2: height - PAD4.bottom,
8207
+ className: "stroke-border",
8208
+ strokeWidth: 1
8209
+ }
8210
+ ),
8211
+ /* @__PURE__ */ jsx("text", { x: 4, y: PAD4.top + 8, className: "fill-muted-foreground text-[10px]", children: valueFormatter(yMax) }),
8212
+ /* @__PURE__ */ jsx("text", { x: 4, y: height - PAD4.bottom, className: "fill-muted-foreground text-[10px]", children: "0" }),
8213
+ /* @__PURE__ */ jsx(
8214
+ "path",
8215
+ {
8216
+ "data-slot": "percentile-band",
8217
+ "data-band": "p95-p99",
8218
+ d: bandPath(p99, p95, xScale, yScale),
8219
+ className: "fill-primary",
8220
+ fillOpacity: 0.15,
8221
+ children: /* @__PURE__ */ jsx("title", { children: "p95\u2013p99" })
8222
+ }
8223
+ ),
8224
+ /* @__PURE__ */ jsx(
8225
+ "path",
8226
+ {
8227
+ "data-slot": "percentile-band",
8228
+ "data-band": "p50-p95",
8229
+ d: bandPath(p95, p50, xScale, yScale),
8230
+ className: "fill-primary",
8231
+ fillOpacity: 0.3,
8232
+ children: /* @__PURE__ */ jsx("title", { children: "p50\u2013p95" })
8233
+ }
8234
+ ),
8235
+ /* @__PURE__ */ jsx(
8236
+ "path",
8237
+ {
8238
+ "data-slot": "percentile-p50-line",
8239
+ d: seriesPath(p50Points, xScale, yScale),
8240
+ fill: "none",
8241
+ className: "stroke-primary",
8242
+ strokeWidth: 1.5,
8243
+ children: /* @__PURE__ */ jsx("title", { children: "p50" })
8244
+ }
8245
+ )
8246
+ ]
8247
+ }
8248
+ ),
8249
+ /* @__PURE__ */ jsxs("table", { "data-slot": "percentile-table", className: "sr-only", children: [
8250
+ /* @__PURE__ */ jsxs("caption", { children: [
8251
+ title,
8252
+ " \u2014 data table"
8253
+ ] }),
8254
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
8255
+ /* @__PURE__ */ jsx("th", { children: "Bucket" }),
8256
+ /* @__PURE__ */ jsx("th", { children: "p50" }),
8257
+ /* @__PURE__ */ jsx("th", { children: "p95" }),
8258
+ /* @__PURE__ */ jsx("th", { children: "p99" })
8259
+ ] }) }),
8260
+ /* @__PURE__ */ jsx("tbody", { children: buckets.map((b, i) => (
8261
+ // biome-ignore lint/suspicious/noArrayIndexKey: buckets are positional time slots — index IS the identity
8262
+ /* @__PURE__ */ jsxs("tr", { children: [
8263
+ /* @__PURE__ */ jsx("td", { children: b.label ?? i + 1 }),
8264
+ /* @__PURE__ */ jsx("td", { children: Number.isFinite(b.p50) ? valueFormatter(b.p50) : "\u2014" }),
8265
+ /* @__PURE__ */ jsx("td", { children: Number.isFinite(b.p95) ? valueFormatter(b.p95) : "\u2014" }),
8266
+ /* @__PURE__ */ jsx("td", { children: Number.isFinite(b.p99) ? valueFormatter(b.p99) : "\u2014" })
8267
+ ] }, i)
8268
+ )) })
8269
+ ] })
8270
+ ] });
8271
+ });
8272
+ PercentileChart.displayName = "PercentileChart";
8273
+ function AnnotationInput(props) {
8274
+ const { name, description, required, disabled, className } = props;
8275
+ const generatedId = useId();
8276
+ const controlId = props.id ?? generatedId;
8277
+ const descId = description ? `${controlId}-desc` : void 0;
8278
+ const isCategorical = props.config.type === "categorical";
8279
+ const labelId = isCategorical ? `${controlId}-label` : void 0;
8280
+ const label = /* @__PURE__ */ jsx(Label, { id: labelId, htmlFor: isCategorical ? void 0 : controlId, required, children: name });
8281
+ const a11y = {
8282
+ "aria-required": required || void 0,
8283
+ "aria-describedby": descId,
8284
+ disabled
8285
+ };
8286
+ let control = null;
8287
+ if (props.config.type === "categorical") {
8288
+ const { config, value, onValueChange } = props;
8289
+ control = /* @__PURE__ */ jsx(
8290
+ RadioGroup,
8291
+ {
8292
+ value: value ?? "",
8293
+ onValueChange: (v) => onValueChange(v === "" ? null : v),
8294
+ "aria-labelledby": labelId,
8295
+ className: "gap-1.5",
8296
+ ...a11y,
8297
+ children: config.options.map((opt) => {
8298
+ const optId = `${controlId}-${opt.label}`;
8299
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
8300
+ /* @__PURE__ */ jsx(RadioGroup.Item, { value: opt.label, id: optId, disabled }),
8301
+ /* @__PURE__ */ jsx(Label, { htmlFor: optId, className: "font-normal", children: opt.label })
8302
+ ] }, opt.label);
8303
+ })
8304
+ }
8305
+ );
8306
+ } else if (props.config.type === "continuous") {
8307
+ const { config, value, onValueChange } = props;
8308
+ const onNumber = (e) => {
8309
+ const raw = e.target.value;
8310
+ if (raw === "") return onValueChange(null);
8311
+ const n = Number(raw);
8312
+ onValueChange(Number.isFinite(n) ? n : null);
8313
+ };
8314
+ control = /* @__PURE__ */ jsx(
8315
+ Input,
8316
+ {
8317
+ ...a11y,
8318
+ id: controlId,
8319
+ type: "number",
8320
+ min: config.min,
8321
+ max: config.max,
8322
+ step: config.step,
8323
+ value: value ?? "",
8324
+ onChange: onNumber
8325
+ }
8326
+ );
8327
+ } else if (props.config.type === "freeform") {
8328
+ const { config, value, onValueChange } = props;
8329
+ const onText = (e) => onValueChange(e.target.value === "" ? null : e.target.value);
8330
+ control = /* @__PURE__ */ jsx(
8331
+ Textarea,
8332
+ {
8333
+ ...a11y,
8334
+ id: controlId,
8335
+ maxLength: config.maxLength,
8336
+ value: value ?? "",
8337
+ onChange: onText
8338
+ }
8339
+ );
8340
+ }
8341
+ return /* @__PURE__ */ jsxs(
8342
+ "div",
8343
+ {
8344
+ "data-slot": "annotation-input",
8345
+ "data-type": props.config.type,
8346
+ className: cn("space-y-1.5", className),
8347
+ children: [
8348
+ label,
8349
+ description ? /* @__PURE__ */ jsx("p", { id: descId, className: "text-body-sm text-muted-foreground", children: description }) : null,
8350
+ control
8351
+ ]
8352
+ }
8353
+ );
8354
+ }
8355
+
8356
+ // src/components/composites/annotation-input/types.ts
8357
+ function isCategoricalConfig(c) {
8358
+ return c.type === "categorical";
8359
+ }
8360
+ function isContinuousConfig(c) {
8361
+ return c.type === "continuous";
8362
+ }
8363
+ function isFreeformConfig(c) {
8364
+ return c.type === "freeform";
8365
+ }
8366
+
8367
+ // src/components/composites/evaluator-form/types.ts
8368
+ function isTargetEvaluator(c) {
8369
+ return c.type === "exact_match" || c.type === "contains";
8370
+ }
8371
+ function isRegexEvaluator(c) {
8372
+ return c.type === "regex";
8373
+ }
8374
+ function isThresholdEvaluator(c) {
8375
+ return c.type === "levenshtein" || c.type === "json_distance";
8376
+ }
8377
+ function defaultEvaluatorConfig(type) {
8378
+ switch (type) {
8379
+ case "exact_match":
8380
+ return { type, target: "" };
8381
+ case "contains":
8382
+ return { type, target: "" };
8383
+ case "regex":
8384
+ return { type, pattern: "", flags: "" };
8385
+ case "levenshtein":
8386
+ return { type, threshold: 0 };
8387
+ case "json_distance":
8388
+ return { type, threshold: 0 };
8389
+ }
8390
+ }
8391
+ var EVALUATOR_TYPES = [
8392
+ { value: "exact_match", label: "Exact match" },
8393
+ { value: "contains", label: "Contains" },
8394
+ { value: "regex", label: "Regex" },
8395
+ { value: "levenshtein", label: "Levenshtein" },
8396
+ { value: "json_distance", label: "JSON distance" }
8397
+ ];
8398
+ var EvaluatorForm = forwardRef(
8399
+ ({ value, onChange, disabled, className }, ref) => {
8400
+ const baseId = useId();
8401
+ const typeId = `${baseId}-type`;
8402
+ const fieldId = `${baseId}-field`;
8403
+ const onThreshold = (e) => {
8404
+ const n = Number(e.target.value);
8405
+ const threshold = Number.isFinite(n) ? n : 0;
8406
+ const cfg = value;
8407
+ onChange({ ...cfg, threshold });
8408
+ };
8409
+ let fields = null;
8410
+ if (value.type === "exact_match" || value.type === "contains") {
8411
+ const cfg = value;
8412
+ fields = /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8413
+ /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: "Target" }),
8414
+ /* @__PURE__ */ jsx(
8415
+ Input,
8416
+ {
8417
+ id: fieldId,
8418
+ value: cfg.target,
8419
+ disabled,
8420
+ onChange: (e) => onChange({ ...cfg, target: e.target.value })
8421
+ }
8422
+ )
8423
+ ] });
8424
+ } else if (value.type === "regex") {
8425
+ const cfg = value;
8426
+ const flagsId = `${baseId}-flags`;
8427
+ fields = /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
8428
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8429
+ /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: "Pattern" }),
8430
+ /* @__PURE__ */ jsx(
8431
+ Input,
8432
+ {
8433
+ id: fieldId,
8434
+ value: cfg.pattern,
8435
+ disabled,
8436
+ onChange: (e) => onChange({ ...cfg, pattern: e.target.value })
8437
+ }
8438
+ )
8439
+ ] }),
8440
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8441
+ /* @__PURE__ */ jsx(Label, { htmlFor: flagsId, children: "Flags" }),
8442
+ /* @__PURE__ */ jsx(
8443
+ Input,
8444
+ {
8445
+ id: flagsId,
8446
+ value: cfg.flags ?? "",
8447
+ disabled,
8448
+ onChange: (e) => onChange({ ...cfg, flags: e.target.value })
8449
+ }
8450
+ )
8451
+ ] })
8452
+ ] });
8453
+ } else {
8454
+ const cfg = value;
8455
+ fields = /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8456
+ /* @__PURE__ */ jsx(Label, { htmlFor: fieldId, children: "Threshold" }),
8457
+ /* @__PURE__ */ jsx(
8458
+ Input,
8459
+ {
8460
+ id: fieldId,
8461
+ type: "number",
8462
+ value: cfg.threshold,
8463
+ disabled,
8464
+ onChange: onThreshold
8465
+ }
8466
+ )
8467
+ ] });
8468
+ }
8469
+ return /* @__PURE__ */ jsxs(
8470
+ "div",
8471
+ {
8472
+ ref,
8473
+ "data-slot": "evaluator-form",
8474
+ "data-eval-type": value.type,
8475
+ className: cn("space-y-3", className),
8476
+ children: [
8477
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8478
+ /* @__PURE__ */ jsx(Label, { htmlFor: typeId, children: "Evaluator type" }),
8479
+ /* @__PURE__ */ jsx(
8480
+ "select",
8481
+ {
8482
+ id: typeId,
8483
+ "data-slot": "evaluator-form-type",
8484
+ value: value.type,
8485
+ disabled,
8486
+ onChange: (e) => onChange(defaultEvaluatorConfig(e.target.value)),
8487
+ className: cn(
8488
+ "flex h-[var(--theo-control-h,2.25rem)] w-full rounded-md border border-input bg-card px-[var(--theo-control-px,0.875rem)] py-1.5 text-body-sm text-foreground",
8489
+ "focus-visible:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
8490
+ "disabled:cursor-not-allowed disabled:opacity-50"
8491
+ ),
8492
+ children: EVALUATOR_TYPES.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
8493
+ }
8494
+ )
8495
+ ] }),
8496
+ fields
8497
+ ]
8498
+ }
8499
+ );
8500
+ }
8501
+ );
8502
+ EvaluatorForm.displayName = "EvaluatorForm";
8503
+ function finiteMean(values) {
8504
+ const nums = values.map(Number).filter((n) => Number.isFinite(n));
8505
+ if (nums.length === 0) return null;
8506
+ return nums.reduce((a, b) => a + b, 0) / nums.length;
8507
+ }
8508
+ function fmt(n) {
8509
+ return Number(n.toFixed(3)).toString();
8510
+ }
8511
+ function countByOption(options, values) {
8512
+ return options.map((opt) => ({
8513
+ label: opt.label,
8514
+ count: values.filter((v) => v === opt.label).length
8515
+ }));
8516
+ }
8517
+ var AnnotationSummaryGroup = forwardRef(
8518
+ ({ config, values, label, defaultOpen, className }, ref) => {
8519
+ const heading = label ?? config.type;
8520
+ if (values.length === 0) {
8521
+ return /* @__PURE__ */ jsxs(
8522
+ Card,
8523
+ {
8524
+ "data-slot": "annotation-summary-group-empty",
8525
+ className: cn("p-4 text-body-sm text-muted-foreground", className),
8526
+ children: [
8527
+ heading,
8528
+ ": no annotations yet"
8529
+ ]
8530
+ }
8531
+ );
8532
+ }
8533
+ let body = null;
8534
+ if (config.type === "continuous") {
8535
+ const mean = finiteMean(values);
8536
+ body = /* @__PURE__ */ jsxs("p", { className: "text-body-sm text-muted-foreground", children: [
8537
+ "Mean ",
8538
+ mean === null ? "\u2014" : fmt(mean),
8539
+ " \xB7 ",
8540
+ values.length,
8541
+ " annotation",
8542
+ values.length === 1 ? "" : "s"
8543
+ ] });
8544
+ } else if (config.type === "categorical") {
8545
+ const rows = countByOption(config.options, values);
8546
+ body = /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: rows.map((row) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-3", children: [
8547
+ /* @__PURE__ */ jsx("span", { className: "text-body-sm text-foreground", children: row.label }),
8548
+ /* @__PURE__ */ jsx(BadgeWithDot, { size: "sm", children: row.count })
8549
+ ] }, row.label)) });
8550
+ } else {
8551
+ const nonEmpty = values.filter((v) => String(v).trim() !== "").length;
8552
+ body = /* @__PURE__ */ jsxs("p", { className: "text-body-sm text-muted-foreground", children: [
8553
+ nonEmpty,
8554
+ " annotation",
8555
+ nonEmpty === 1 ? "" : "s"
8556
+ ] });
8557
+ }
8558
+ return /* @__PURE__ */ jsxs(
8559
+ "details",
8560
+ {
8561
+ ref,
8562
+ "data-slot": "annotation-summary-group",
8563
+ "data-type": config.type,
8564
+ open: defaultOpen,
8565
+ className: cn(
8566
+ "rounded-xl border border-border bg-card text-card-foreground shadow-md",
8567
+ className
8568
+ ),
8569
+ children: [
8570
+ /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer items-center justify-between gap-3 rounded-xl p-4 font-medium text-body-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background", children: [
8571
+ /* @__PURE__ */ jsx("span", { children: heading }),
8572
+ /* @__PURE__ */ jsx(BadgeWithDot, { size: "sm", variant: "outline", children: values.length })
8573
+ ] }),
8574
+ /* @__PURE__ */ jsx("div", { className: "p-4 pt-0", children: body })
8575
+ ]
8576
+ }
8577
+ );
8578
+ }
8579
+ );
8580
+ AnnotationSummaryGroup.displayName = "AnnotationSummaryGroup";
8581
+ var defaultCost6 = (usd) => `$${usd.toFixed(4)}`;
8582
+ var EM_DASH = "\u2014";
8583
+ var tokens = (n) => n === void 0 ? EM_DASH : String(n);
8584
+ var TokenCostBreakdown = forwardRef((props, ref) => {
8585
+ const {
8586
+ inputTokens,
8587
+ outputTokens,
8588
+ cacheTokens,
8589
+ totalTokens,
8590
+ costUsd,
8591
+ formatCostUsd = defaultCost6,
8592
+ className,
8593
+ ...rest
8594
+ } = props;
8595
+ const allAbsent = inputTokens === void 0 && outputTokens === void 0 && cacheTokens === void 0 && totalTokens === void 0 && costUsd === void 0;
8596
+ if (allAbsent) {
8597
+ return /* @__PURE__ */ jsx(
8598
+ "div",
8599
+ {
8600
+ "data-slot": "token-cost-breakdown-empty",
8601
+ className: cn(
8602
+ "flex items-center justify-center rounded-md border border-dashed p-3 text-body-sm text-muted-foreground",
8603
+ className
8604
+ ),
8605
+ ...rest,
8606
+ children: "No token usage to report."
8607
+ }
8608
+ );
8609
+ }
8610
+ return /* @__PURE__ */ jsxs(
8611
+ "dl",
8612
+ {
8613
+ "data-slot": "token-cost-breakdown",
8614
+ ref,
8615
+ className: cn(
8616
+ "grid grid-cols-2 gap-3 rounded-md border border-border bg-muted/20 p-3 text-xs sm:grid-cols-3 lg:grid-cols-5",
8617
+ className
8618
+ ),
8619
+ ...rest,
8620
+ children: [
8621
+ /* @__PURE__ */ jsx(Metric, { label: "Input", value: tokens(inputTokens), testId: "tcb-input" }),
8622
+ /* @__PURE__ */ jsx(Metric, { label: "Output", value: tokens(outputTokens), testId: "tcb-output" }),
8623
+ /* @__PURE__ */ jsx(Metric, { label: "Cache", value: tokens(cacheTokens), testId: "tcb-cache" }),
8624
+ /* @__PURE__ */ jsx(Metric, { label: "Total", value: tokens(totalTokens), testId: "tcb-total" }),
8625
+ /* @__PURE__ */ jsx(
8626
+ Metric,
8627
+ {
8628
+ label: "Cost",
8629
+ value: costUsd === void 0 ? EM_DASH : formatCostUsd(costUsd),
8630
+ testId: "tcb-cost"
8631
+ }
8632
+ )
8633
+ ]
8634
+ }
8635
+ );
8636
+ });
8637
+ TokenCostBreakdown.displayName = "TokenCostBreakdown";
8638
+ function Metric({ label, value, testId }) {
8639
+ return /* @__PURE__ */ jsxs("div", { children: [
8640
+ /* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: label }),
8641
+ /* @__PURE__ */ jsx("dd", { "data-testid": testId, className: "font-medium font-mono tabular-nums", children: value })
8642
+ ] });
8643
+ }
8644
+ function formatPrice(n) {
8645
+ if (!Number.isFinite(n)) return "\u2014";
8646
+ if (n === 0) return "0";
8647
+ const fixed = n.toFixed(12);
8648
+ return fixed.includes(".") ? fixed.replace(/0+$/, "").replace(/\.$/, "") : fixed;
8649
+ }
8650
+ var PriceBreakdown = forwardRef((props, ref) => {
8651
+ const { prices, unit = "token", className, ...rest } = props;
8652
+ const entries = Object.entries(prices);
8653
+ if (entries.length === 0) {
8654
+ return /* @__PURE__ */ jsx(
8655
+ "div",
8656
+ {
8657
+ "data-slot": "price-breakdown-empty",
8658
+ className: cn(
8659
+ "flex items-center justify-center rounded-md border border-dashed p-3 text-body-sm text-muted-foreground",
8660
+ className
8661
+ ),
8662
+ ...rest,
8663
+ children: "No prices to break down."
8664
+ }
8665
+ );
8666
+ }
8667
+ return /* @__PURE__ */ jsxs(
8668
+ "table",
8669
+ {
8670
+ "data-slot": "price-breakdown",
8671
+ ref,
8672
+ className: cn("w-full text-xs", className),
8673
+ ...rest,
8674
+ children: [
8675
+ /* @__PURE__ */ jsxs("caption", { className: "sr-only", children: [
8676
+ "Price breakdown per ",
8677
+ unit,
8678
+ ", per 1K, and per 1M"
8679
+ ] }),
8680
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-left text-muted-foreground", children: [
8681
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "py-1 pr-3 font-medium", children: "Usage" }),
8682
+ /* @__PURE__ */ jsxs("th", { scope: "col", className: "py-1 pr-3 text-right font-medium", children: [
8683
+ "Per ",
8684
+ unit
8685
+ ] }),
8686
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "py-1 pr-3 text-right font-medium", children: "Per 1K" }),
8687
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "py-1 text-right font-medium", children: "Per 1M" })
8688
+ ] }) }),
8689
+ /* @__PURE__ */ jsx("tbody", { children: entries.map(([label, price]) => {
8690
+ const id = label.replace(/\s+/g, "-").toLowerCase();
8691
+ return /* @__PURE__ */ jsxs("tr", { "data-slot": "price-breakdown-row", className: "border-border border-t", children: [
8692
+ /* @__PURE__ */ jsx("th", { scope: "row", className: "py-1 pr-3 text-left font-normal", children: label }),
8693
+ /* @__PURE__ */ jsx(
8694
+ "td",
8695
+ {
8696
+ "data-testid": `price-${id}-unit`,
8697
+ className: "py-1 pr-3 text-right font-mono tabular-nums",
8698
+ children: formatPrice(price)
8699
+ }
8700
+ ),
8701
+ /* @__PURE__ */ jsx(
8702
+ "td",
8703
+ {
8704
+ "data-testid": `price-${id}-1k`,
8705
+ className: "py-1 pr-3 text-right font-mono tabular-nums",
8706
+ children: formatPrice(price * 1e3)
8707
+ }
8708
+ ),
8709
+ /* @__PURE__ */ jsx("td", { "data-testid": `price-${id}-1m`, className: "py-1 text-right font-mono tabular-nums", children: formatPrice(price * 1e6) })
8710
+ ] }, label);
8711
+ }) })
8712
+ ]
8713
+ }
8714
+ );
8715
+ });
8716
+ PriceBreakdown.displayName = "PriceBreakdown";
8717
+ var DEFAULT_VARIANT_MAP = {
8718
+ alert: "destructive",
8719
+ warning: "warning",
8720
+ ok: "success",
8721
+ no_data: "outline",
8722
+ unknown: "outline",
8723
+ paused: "outline"
8724
+ };
8725
+ var DEFAULT_LABEL = {
8726
+ ok: "OK",
8727
+ warning: "Warning",
8728
+ alert: "Alert",
8729
+ no_data: "No data",
8730
+ unknown: "Unknown",
8731
+ paused: "Paused"
8732
+ };
8733
+ var NEUTRAL_VARIANT = "outline";
8734
+ function deriveLabel(severity) {
8735
+ return DEFAULT_LABEL[severity] ?? severity;
8736
+ }
8737
+ var SeverityBadge = forwardRef(
8738
+ ({ severity, label, variantMap, className, ...rest }, ref) => {
8739
+ const variant = variantMap?.[severity] ?? DEFAULT_VARIANT_MAP[severity] ?? NEUTRAL_VARIANT;
8740
+ const text = label ?? deriveLabel(severity);
8741
+ return /* @__PURE__ */ jsx(
8742
+ BadgeWithDot,
8743
+ {
8744
+ "data-slot": "severity-badge",
8745
+ "data-severity": severity,
8746
+ ref,
8747
+ variant,
8748
+ className: cn(className),
8749
+ ...rest,
8750
+ children: text
8751
+ }
8752
+ );
8753
+ }
8754
+ );
8755
+ SeverityBadge.displayName = "SeverityBadge";
8756
+ var TagInput = forwardRef(
8757
+ ({ value, onChange, suggestions, placeholder, disabled, className }, ref) => {
8758
+ const [draft, setDraft] = useState("");
8759
+ const listId = useId();
8760
+ const hasSuggestions = suggestions !== void 0 && suggestions.length > 0;
8761
+ const addTag = () => {
8762
+ const tag = draft.trim();
8763
+ if (tag === "" || value.includes(tag)) {
8764
+ setDraft("");
8765
+ return;
8766
+ }
8767
+ onChange([...value, tag]);
8768
+ setDraft("");
8769
+ };
8770
+ const removeTag = (tag) => {
8771
+ if (disabled) return;
8772
+ onChange(value.filter((t) => t !== tag));
8773
+ };
8774
+ const onKeyDown = (event) => {
8775
+ if (event.key === "Enter") {
8776
+ event.preventDefault();
8777
+ addTag();
8778
+ }
8779
+ };
8780
+ return /* @__PURE__ */ jsxs(
8781
+ "div",
8782
+ {
8783
+ "data-slot": "tag-input",
8784
+ ref,
8785
+ className: cn("flex flex-wrap items-center gap-1.5", className),
8786
+ children: [
8787
+ value.map((tag) => /* @__PURE__ */ jsxs(BadgeWithDot, { "data-testid": "tag-input-chip", variant: "primary", className: "gap-1 pr-1", children: [
8788
+ /* @__PURE__ */ jsx("span", { className: "normal-case tracking-normal", children: tag }),
8789
+ /* @__PURE__ */ jsx(
8790
+ "button",
8791
+ {
8792
+ type: "button",
8793
+ disabled,
8794
+ onClick: () => removeTag(tag),
8795
+ "aria-label": `Remove ${tag}`,
8796
+ className: cn(
8797
+ "inline-flex size-4 items-center justify-center rounded-full leading-none",
8798
+ "text-current/70 transition-colors hover:bg-primary/20 hover:text-current",
8799
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
8800
+ "disabled:cursor-not-allowed disabled:opacity-50"
8801
+ ),
8802
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xD7" })
8803
+ }
8804
+ )
8805
+ ] }, tag)),
8806
+ /* @__PURE__ */ jsx(
8807
+ Input,
8808
+ {
8809
+ size: "sm",
8810
+ className: "w-auto min-w-[8rem] flex-1",
8811
+ value: draft,
8812
+ disabled,
8813
+ placeholder,
8814
+ list: hasSuggestions ? listId : void 0,
8815
+ onChange: (e) => setDraft(e.target.value),
8816
+ onKeyDown,
8817
+ onBlur: addTag
8818
+ }
8819
+ ),
8820
+ hasSuggestions ? /* @__PURE__ */ jsx("datalist", { id: listId, children: suggestions.map((s) => /* @__PURE__ */ jsx("option", { value: s }, s)) }) : null
8821
+ ]
8822
+ }
8823
+ );
8824
+ }
8825
+ );
8826
+ TagInput.displayName = "TagInput";
8827
+ function initialOf(name) {
8828
+ return name.trim().charAt(0).toUpperCase() || "?";
8829
+ }
8830
+ var CommentThread = forwardRef(
8831
+ ({ comments, onSubmit, currentUser, disabled, className }, ref) => {
8832
+ const [draft, setDraft] = useState("");
8833
+ const composerId = useId();
8834
+ const submit = () => {
8835
+ const body = draft.trim();
8836
+ if (body === "" || disabled) return;
8837
+ onSubmit(body);
8838
+ setDraft("");
8839
+ };
8840
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "comment-thread", ref, className: cn("flex flex-col gap-4", className), children: [
8841
+ comments.length === 0 ? /* @__PURE__ */ jsx("p", { "data-slot": "comment-thread-empty", className: "text-body-sm text-muted-foreground", children: "No comments yet." }) : /* @__PURE__ */ jsx("ol", { className: "flex flex-col gap-4", children: comments.map((c) => /* @__PURE__ */ jsxs("li", { "data-slot": "comment-thread-item", className: "flex gap-3", children: [
8842
+ /* @__PURE__ */ jsxs(Avatar, { size: "sm", children: [
8843
+ c.avatarUrl ? /* @__PURE__ */ jsx(Avatar.Image, { src: c.avatarUrl, alt: c.author }) : null,
8844
+ /* @__PURE__ */ jsx(Avatar.Fallback, { delayMs: 0, children: initialOf(c.author) })
8845
+ ] }),
8846
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
8847
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
8848
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-body-sm text-foreground", children: c.author }),
8849
+ /* @__PURE__ */ jsx(Timestamp, { value: c.createdAt, className: "text-label text-muted-foreground" })
8850
+ ] }),
8851
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-body-sm text-foreground", children: c.body })
8852
+ ] })
8853
+ ] }, c.id)) }),
8854
+ /* @__PURE__ */ jsxs("div", { "data-slot": "comment-thread-composer", className: "flex flex-col gap-2", children: [
8855
+ /* @__PURE__ */ jsx("label", { htmlFor: composerId, className: "sr-only", children: "Add a comment" }),
8856
+ /* @__PURE__ */ jsx(
8857
+ Textarea,
8858
+ {
8859
+ id: composerId,
8860
+ value: draft,
8861
+ disabled,
8862
+ placeholder: currentUser ? `Comment as ${currentUser}\u2026` : "Add a comment\u2026",
8863
+ onChange: (e) => setDraft(e.target.value)
8864
+ }
8865
+ ),
8866
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", disabled, onClick: submit, children: "Comment" }) })
8867
+ ] })
8868
+ ] });
8869
+ }
8870
+ );
8871
+ CommentThread.displayName = "CommentThread";
8872
+ var roleVariant2 = {
8873
+ user: "primary",
8874
+ assistant: "success",
8875
+ tool: "accent",
8876
+ system: "outline"
8877
+ };
8878
+ function jsonText(value) {
8879
+ if (typeof value === "string") return value;
8880
+ try {
8881
+ return JSON.stringify(value, null, 2);
8882
+ } catch {
8883
+ return String(value);
8884
+ }
8885
+ }
8886
+ var ChatMessageCard = forwardRef(
8887
+ ({ className, role, content, toolCalls, toolResults, ...props }, ref) => {
8888
+ const hasContent = content !== void 0 && content !== "";
8889
+ const hasCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
8890
+ const hasResults = Array.isArray(toolResults) && toolResults.length > 0;
8891
+ const isEmpty = !hasContent && !hasCalls && !hasResults;
8892
+ return /* @__PURE__ */ jsxs(
8893
+ "div",
8894
+ {
8895
+ "data-slot": "chat-message-card",
8896
+ "data-role": role,
8897
+ ref,
8898
+ className: cn(
8899
+ "flex flex-col gap-2 rounded-lg border border-border/40 bg-card p-3",
8900
+ className
8901
+ ),
8902
+ ...props,
8903
+ children: [
8904
+ /* @__PURE__ */ jsx(BadgeWithDot, { variant: roleVariant2[role] ?? "outline", size: "sm", className: "self-start", children: role }),
8905
+ hasContent ? /* @__PURE__ */ jsx(
8906
+ "p",
8907
+ {
8908
+ "data-slot": "chat-message-card-content",
8909
+ className: "whitespace-pre-wrap text-body-sm text-foreground",
8910
+ children: content
8911
+ }
8912
+ ) : null,
8913
+ hasCalls ? toolCalls?.map((call) => /* @__PURE__ */ jsx(
8914
+ CodeBlock,
8915
+ {
8916
+ caption: call.function?.name ?? "tool call",
8917
+ code: call.function?.arguments ?? ""
8918
+ },
8919
+ call.id
8920
+ )) : null,
8921
+ hasResults ? toolResults?.map((result, i) => /* @__PURE__ */ jsx(
8922
+ CodeBlock,
8923
+ {
8924
+ caption: "result",
8925
+ code: jsonText(result)
8926
+ },
8927
+ i
8928
+ )) : null,
8929
+ isEmpty ? /* @__PURE__ */ jsx(
8930
+ "p",
8931
+ {
8932
+ "data-slot": "chat-message-card-empty",
8933
+ className: "text-body-sm text-muted-foreground italic",
8934
+ children: "No content."
8935
+ }
8936
+ ) : null
8937
+ ]
8938
+ }
8939
+ );
8940
+ }
8941
+ );
8942
+ ChatMessageCard.displayName = "ChatMessageCard";
8943
+ function NavButton(props) {
8944
+ return /* @__PURE__ */ jsx(
8945
+ Button,
8946
+ {
8947
+ type: "button",
8948
+ variant: "ghost",
8949
+ size: "icon",
8950
+ "aria-label": props.label,
8951
+ disabled: props.disabled,
8952
+ onClick: props.onClick,
8953
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: props.glyph })
8954
+ }
8955
+ );
8956
+ }
8957
+ var MessageBranchSelector = forwardRef(
8958
+ ({ className, index, count, onPrev, onNext, disabled = false, ...props }, ref) => {
8959
+ if (count <= 1) return null;
8960
+ return /* @__PURE__ */ jsxs(
8961
+ "div",
8962
+ {
8963
+ "data-slot": "message-branch-selector",
8964
+ ref,
8965
+ className: cn(
8966
+ "inline-flex items-center gap-1 text-body-sm text-muted-foreground",
8967
+ className
8968
+ ),
8969
+ ...props,
8970
+ children: [
8971
+ /* @__PURE__ */ jsx(
8972
+ NavButton,
8973
+ {
8974
+ label: "Previous branch",
8975
+ glyph: "\u2039",
8976
+ disabled: disabled || index <= 0,
8977
+ onClick: onPrev
8978
+ }
8979
+ ),
8980
+ /* @__PURE__ */ jsxs("span", { "data-slot": "message-branch-selector-label", className: "tabular-nums", children: [
8981
+ index + 1,
8982
+ " / ",
8983
+ count
8984
+ ] }),
8985
+ /* @__PURE__ */ jsx(
8986
+ NavButton,
8987
+ {
8988
+ label: "Next branch",
8989
+ glyph: "\u203A",
8990
+ disabled: disabled || index >= count - 1,
8991
+ onClick: onNext
8992
+ }
8993
+ )
8994
+ ]
8995
+ }
8996
+ );
8997
+ }
8998
+ );
8999
+ MessageBranchSelector.displayName = "MessageBranchSelector";
9000
+
9001
+ // src/components/composites/prompt-template-editor/extract-vars.ts
9002
+ var PLACEHOLDER = /\{\{\s*([^{}]*?)\s*\}\}|\{\s*([^{}]*?)\s*\}/g;
9003
+ function extractVars(template) {
9004
+ const seen = /* @__PURE__ */ new Set();
9005
+ const out = [];
9006
+ for (const match of template.matchAll(PLACEHOLDER)) {
9007
+ const name = (match[1] ?? match[2] ?? "").trim();
9008
+ if (name === "" || seen.has(name)) continue;
9009
+ seen.add(name);
9010
+ out.push(name);
9011
+ }
9012
+ return out;
9013
+ }
9014
+ function deriveChips(value, variables) {
9015
+ const used = new Set(extractVars(value));
9016
+ const declared = new Set(variables);
9017
+ const names = [...declared, ...[...used].filter((v) => !declared.has(v))];
9018
+ return names.map((name) => ({
9019
+ name,
9020
+ used: used.has(name),
9021
+ missing: used.has(name) && !declared.has(name)
9022
+ }));
9023
+ }
9024
+ function chipVariant(chip) {
9025
+ if (chip.missing) return "warning";
9026
+ return chip.used ? "success" : "outline";
9027
+ }
9028
+ var PromptTemplateEditor = forwardRef(
9029
+ ({ className, value, onChange, variables = [], disabled = false, ...props }, ref) => {
9030
+ const labelId = useId();
9031
+ const chips = deriveChips(value, variables);
9032
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "prompt-template-editor", className: cn("flex flex-col gap-2", className), children: [
9033
+ /* @__PURE__ */ jsx(
9034
+ "span",
9035
+ {
9036
+ id: labelId,
9037
+ className: "font-semibold text-label-caps text-muted-foreground uppercase",
9038
+ children: "Prompt template"
9039
+ }
9040
+ ),
9041
+ /* @__PURE__ */ jsx(
9042
+ Textarea,
9043
+ {
9044
+ ref,
9045
+ "aria-labelledby": labelId,
9046
+ value,
9047
+ disabled,
9048
+ onChange: (e) => onChange(e.target.value),
9049
+ ...props
9050
+ }
9051
+ ),
9052
+ chips.length > 0 ? /* @__PURE__ */ jsx("div", { "data-slot": "prompt-template-editor-vars", className: "flex flex-wrap gap-1.5", children: chips.map((chip) => /* @__PURE__ */ jsx(
9053
+ BadgeWithDot,
9054
+ {
9055
+ "data-slot": "prompt-template-editor-var",
9056
+ "data-var": chip.name,
9057
+ "data-used": String(chip.used),
9058
+ "data-missing": String(chip.missing),
9059
+ variant: chipVariant(chip),
9060
+ size: "sm",
9061
+ children: chip.missing ? `${chip.name} (undeclared)` : chip.name
9062
+ },
9063
+ chip.name
9064
+ )) }) : null
9065
+ ] });
9066
+ }
9067
+ );
9068
+ PromptTemplateEditor.displayName = "PromptTemplateEditor";
9069
+
9070
+ export { AccountMenu, ActionBar, Alert, AnnotationInput, AnnotationSummaryGroup, AttributesTable, Avatar, BadgeWithDot as Badge, Breadcrumb, Button, Card, ChatMessageCard, Checkbox, CodeBlock, Combobox, CommandPalette, CommentThread, ConfirmDialog, CopyButton, DangerZone, DataTable, DatasetItemDiff, DeploymentRow, DescriptionList, Dialog, DiffView, DomainConfig, DropdownMenu, EmptyState, EnvVarEditor, EvaluatorForm, FileDropzone, FormField, GRAPH_NODE_CAP, Histogram, IOCards, Input, JsonViewer, KIND_LABEL, Label, LoginSplit, MessageBranchSelector, MessageCard, MessageItem, MetricCard, MetricsPanel, PageShell, Pagination, PercentileChart, PinInput, PlanBadge, PreviewEnvCard, PriceBreakdown, Progress, ProjectCard, PromptTemplateEditor, PromptVersionDiff, RadioGroup, RollbackUI, ScrollArea, Select, SessionSummary, SessionTimeline, SeverityBadge, Sheet, Sidebar, Skeleton, Slider, SocialAuthRow, SpanGraph, SpanTree, SpanTreeRow, SpanWaterfall, StatTile, StatusDot, StatusIndicator, Stepper, Switch, Table, Tabs, TagInput, TaskHeader, Textarea, Timestamp, Toast, Toaster, TokenCostBreakdown, TooltipWithStatics as Tooltip, TopNav, TraceCompare, TraceTranscript, TrendChart, UpdateBanner, VIRTUALIZE_THRESHOLD, aggregateCost, aggregateSession, alignSpanTrees, asChat, avatarVariants, badgeVariants, buildLayeredGraph, buttonVariants, cn, computeBarLayout, computeHistogram, computePageRange, computeTraceBounds, configToText, contentText, defaultEvaluatorConfig, deriveSpanKind, deriveSteps, diffLines, durationMs, extractVars, fieldPresent, fieldToText, flattenAll, flattenVisible, groupByNamespace, isCategoricalConfig, isContinuousConfig, isFreeformConfig, isNearBottom, isRedactedThinking, isRegexEvaluator, isSpanError, isTargetEvaluator, isThresholdEvaluator, linScale, matchesAccept, niceAxisTicks, niceMax, packRows, prettyValue, seriesPath, sessionBounds, sheetVariants, spanCostUsd, templateToText, toNs, toTranscriptRows, traceMetrics, useStickToBottom, useToast, validateFiles };