@usetheo/ui 0.26.0 → 0.28.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 (38) hide show
  1. package/dist/components/composites/annotation-summary-group/annotation-summary-group.d.ts +28 -0
  2. package/dist/components/composites/annotation-summary-group/index.d.ts +1 -0
  3. package/dist/components/composites/chat-message-card/chat-message-card.d.ts +14 -0
  4. package/dist/components/composites/chat-message-card/index.d.ts +1 -0
  5. package/dist/components/composites/comment-thread/comment-thread.d.ts +24 -0
  6. package/dist/components/composites/comment-thread/index.d.ts +2 -0
  7. package/dist/components/composites/dataset-item-diff/dataset-item-diff.d.ts +19 -0
  8. package/dist/components/composites/dataset-item-diff/index.d.ts +4 -0
  9. package/dist/components/composites/dataset-item-diff/to-diff-text.d.ts +22 -0
  10. package/dist/components/composites/diff-view/diff-section.d.ts +19 -0
  11. package/dist/components/composites/diff-view/diff-view.d.ts +39 -0
  12. package/dist/components/composites/diff-view/index.d.ts +2 -0
  13. package/dist/components/composites/evaluator-form/evaluator-form.d.ts +25 -0
  14. package/dist/components/composites/evaluator-form/index.d.ts +3 -0
  15. package/dist/components/composites/evaluator-form/types.d.ts +41 -0
  16. package/dist/components/composites/message-branch-selector/index.d.ts +1 -0
  17. package/dist/components/composites/message-branch-selector/message-branch-selector.d.ts +27 -0
  18. package/dist/components/composites/price-breakdown/index.d.ts +2 -0
  19. package/dist/components/composites/price-breakdown/price-breakdown.d.ts +21 -0
  20. package/dist/components/composites/prompt-template-editor/extract-vars.d.ts +1 -0
  21. package/dist/components/composites/prompt-template-editor/index.d.ts +2 -0
  22. package/dist/components/composites/prompt-template-editor/prompt-template-editor.d.ts +24 -0
  23. package/dist/components/composites/prompt-version-diff/index.d.ts +4 -0
  24. package/dist/components/composites/prompt-version-diff/prompt-version-diff.d.ts +20 -0
  25. package/dist/components/composites/prompt-version-diff/to-diff-text.d.ts +26 -0
  26. package/dist/components/composites/severity-badge/index.d.ts +2 -0
  27. package/dist/components/composites/severity-badge/severity-badge.d.ts +33 -0
  28. package/dist/components/composites/tag-input/index.d.ts +2 -0
  29. package/dist/components/composites/tag-input/tag-input.d.ts +32 -0
  30. package/dist/components/composites/token-cost-breakdown/index.d.ts +2 -0
  31. package/dist/components/composites/token-cost-breakdown/token-cost-breakdown.d.ts +26 -0
  32. package/dist/components/primitives/input/input.d.ts +1 -1
  33. package/dist/index.d.ts +32 -9
  34. package/dist/index.js +928 -9
  35. package/dist/lib/diff/diff-lines.d.ts +23 -0
  36. package/dist/lib/diff/index.d.ts +1 -0
  37. package/dist/tailwind-preset.js +1 -1
  38. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -583,9 +583,9 @@ var buttonVariants = cva(
583
583
  // md: tier ajustável via density (CSS var on :root). See D3 ADR of
584
584
  // faang-density-tightening plan. Default `comfortable` density makes
585
585
  // this 36px (--theo-control-h: 2.25rem). sm and lg stay hardcoded.
586
- md: "h-[var(--theo-control-h,2.25rem)] px-[var(--theo-control-px,0.875rem)] text-body-sm",
586
+ md: "h-[var(--theo-control-h,2rem)] px-[var(--theo-control-px,0.75rem)] text-body-sm",
587
587
  lg: "h-11 px-4 text-body-md",
588
- icon: "h-[var(--theo-control-h,2.25rem)] w-[var(--theo-control-h,2.25rem)] p-0"
588
+ icon: "h-[var(--theo-control-h,2rem)] w-[var(--theo-control-h,2rem)] p-0"
589
589
  }
590
590
  },
591
591
  defaultVariants: {
@@ -1102,7 +1102,7 @@ var JsonViewer = forwardRef(
1102
1102
  JsonViewer.displayName = "JsonViewer";
1103
1103
  var inputVariants = cva(
1104
1104
  [
1105
- "flex w-full rounded-md border border-input bg-card",
1105
+ "flex w-full rounded-lg border border-input bg-card",
1106
1106
  "text-foreground placeholder:text-muted-foreground",
1107
1107
  "transition-[box-shadow,border-color] duration-base ease-out-soft",
1108
1108
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
@@ -1115,7 +1115,7 @@ var inputVariants = cva(
1115
1115
  size: {
1116
1116
  sm: "h-8 px-2.5 py-1 text-body-sm",
1117
1117
  // md: density-tunable via CSS var. Comfortable (default) = 36px.
1118
- md: "h-[var(--theo-control-h,2.25rem)] px-[var(--theo-control-px,0.875rem)] py-1.5 text-body-sm",
1118
+ md: "h-[var(--theo-control-h,2rem)] px-[var(--theo-control-px,0.75rem)] py-1.5 text-body-sm",
1119
1119
  lg: "h-11 px-4 py-2.5 text-body-md"
1120
1120
  }
1121
1121
  },
@@ -2368,7 +2368,7 @@ var EmptyState = forwardRef(
2368
2368
  EmptyState.displayName = "EmptyState";
2369
2369
  var selectTriggerVariants = cva(
2370
2370
  [
2371
- "flex w-full items-center justify-between gap-2 rounded-md border border-input bg-card",
2371
+ "flex w-full items-center justify-between gap-2 rounded-lg border border-input bg-card",
2372
2372
  "text-foreground placeholder:text-muted-foreground",
2373
2373
  "transition-[border-color,box-shadow] duration-base ease-out-soft",
2374
2374
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
@@ -2381,7 +2381,7 @@ var selectTriggerVariants = cva(
2381
2381
  variants: {
2382
2382
  size: {
2383
2383
  sm: "h-8 px-2.5 py-1 text-body-sm",
2384
- md: "h-[var(--theo-control-h,2.25rem)] px-[var(--theo-control-px,0.875rem)] py-1.5 text-body-sm",
2384
+ md: "h-[var(--theo-control-h,2rem)] px-[var(--theo-control-px,0.75rem)] py-1.5 text-body-sm",
2385
2385
  lg: "h-11 px-4 py-2.5 text-body-md"
2386
2386
  }
2387
2387
  },
@@ -2632,7 +2632,7 @@ var Switch = forwardRef(
2632
2632
  Switch.displayName = "Switch";
2633
2633
  var textareaVariants = cva(
2634
2634
  [
2635
- "flex w-full resize-y rounded-md border border-input bg-card",
2635
+ "flex w-full resize-y rounded-lg border border-input bg-card",
2636
2636
  "text-foreground placeholder:text-muted-foreground",
2637
2637
  "transition-[box-shadow,border-color] duration-base ease-out-soft",
2638
2638
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
@@ -2645,7 +2645,7 @@ var textareaVariants = cva(
2645
2645
  sm: "min-h-[64px] px-2.5 py-1.5 text-body-sm",
2646
2646
  // md: text scale + padding tighten to body-sm per FAANG density.
2647
2647
  // min-h stays 96px because multiline has its own height rationale.
2648
- md: "min-h-[6rem] px-[var(--theo-control-px,0.875rem)] py-1.5 text-body-sm",
2648
+ md: "min-h-[6rem] px-[var(--theo-control-px,0.75rem)] py-1.5 text-body-sm",
2649
2649
  lg: "min-h-[128px] px-4 py-2.5 text-body-md"
2650
2650
  }
2651
2651
  },
@@ -7793,6 +7793,222 @@ function computeHistogram(values, binCount) {
7793
7793
  }
7794
7794
  return bins;
7795
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
+ }
7796
8012
  var VIEW_W2 = 600;
7797
8013
  var PAD3 = { top: 8, right: 8, bottom: 4, left: 40 };
7798
8014
  var BAR_GAP = 2;
@@ -8148,4 +8364,707 @@ function isFreeformConfig(c) {
8148
8364
  return c.type === "freeform";
8149
8365
  }
8150
8366
 
8151
- export { AccountMenu, ActionBar, Alert, AnnotationInput, 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, Histogram, IOCards, Input, JsonViewer, KIND_LABEL, Label, LoginSplit, MessageCard, MessageItem, MetricCard, MetricsPanel, PageShell, Pagination, PercentileChart, PinInput, PlanBadge, PreviewEnvCard, Progress, ProjectCard, RadioGroup, RollbackUI, ScrollArea, Select, SessionSummary, SessionTimeline, 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, aggregateSession, alignSpanTrees, asChat, avatarVariants, badgeVariants, buildLayeredGraph, buttonVariants, cn, computeBarLayout, computeHistogram, computePageRange, computeTraceBounds, contentText, deriveSpanKind, deriveSteps, durationMs, flattenAll, flattenVisible, groupByNamespace, isCategoricalConfig, isContinuousConfig, isFreeformConfig, isNearBottom, isRedactedThinking, isSpanError, linScale, matchesAccept, niceAxisTicks, niceMax, packRows, prettyValue, seriesPath, sessionBounds, sheetVariants, spanCostUsd, toNs, toTranscriptRows, traceMetrics, useStickToBottom, useToast, validateFiles };
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,2rem)] w-full rounded-lg border border-input bg-card px-[var(--theo-control-px,0.75rem)] 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 };