@page-speed/agent-everywhere 0.8.0 → 1.0.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.
- package/dist/index.cjs +2381 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -9
- package/dist/index.d.ts +397 -9
- package/dist/index.js +2370 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7696,17 +7696,2281 @@ function MessageActions({
|
|
|
7696
7696
|
children
|
|
7697
7697
|
] }) });
|
|
7698
7698
|
}
|
|
7699
|
-
function
|
|
7700
|
-
|
|
7701
|
-
|
|
7699
|
+
function CardShell({ className, children }) {
|
|
7700
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("rounded-2xl border bg-card text-card-foreground shadow-sm", className), children });
|
|
7701
|
+
}
|
|
7702
|
+
var DELTA_GLYPH = {
|
|
7703
|
+
up: "\u25B2",
|
|
7704
|
+
down: "\u25BC",
|
|
7705
|
+
flat: "\u2014"
|
|
7706
|
+
};
|
|
7707
|
+
var DELTA_DIRECTION_CLASSES = {
|
|
7708
|
+
up: "bg-emerald-50 text-emerald-600",
|
|
7709
|
+
down: "bg-red-50 text-red-600",
|
|
7710
|
+
flat: "bg-slate-100 text-slate-500"
|
|
7711
|
+
};
|
|
7712
|
+
var DELTA_TONE_CLASSES = {
|
|
7713
|
+
positive: "bg-emerald-50 text-emerald-600",
|
|
7714
|
+
negative: "bg-red-50 text-red-600",
|
|
7715
|
+
warning: "bg-amber-50 text-amber-600",
|
|
7716
|
+
info: "bg-blue-50 text-blue-600",
|
|
7717
|
+
neutral: "bg-slate-100 text-slate-500",
|
|
7718
|
+
muted: "bg-slate-100 text-slate-400",
|
|
7719
|
+
default: "bg-slate-100 text-slate-500"
|
|
7720
|
+
};
|
|
7721
|
+
function DeltaPill({
|
|
7722
|
+
value,
|
|
7723
|
+
text,
|
|
7724
|
+
direction,
|
|
7725
|
+
tone,
|
|
7726
|
+
hideArrow,
|
|
7727
|
+
className
|
|
7728
|
+
}) {
|
|
7729
|
+
const label = value ?? text ?? "";
|
|
7730
|
+
const colorClass = (tone && DELTA_TONE_CLASSES[tone]) ?? DELTA_DIRECTION_CLASSES[direction];
|
|
7731
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7732
|
+
"span",
|
|
7733
|
+
{
|
|
7734
|
+
className: cn(
|
|
7735
|
+
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 font-medium text-xs tabular-nums",
|
|
7736
|
+
colorClass,
|
|
7737
|
+
className
|
|
7738
|
+
),
|
|
7739
|
+
children: [
|
|
7740
|
+
!hideArrow && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-[0.625rem] leading-none", children: DELTA_GLYPH[direction] }),
|
|
7741
|
+
label
|
|
7742
|
+
]
|
|
7743
|
+
}
|
|
7744
|
+
);
|
|
7745
|
+
}
|
|
7746
|
+
var STATUS_DOT_COLORS = {
|
|
7747
|
+
healthy: "#10B981",
|
|
7748
|
+
warning: "#F59E0B",
|
|
7749
|
+
critical: "#EF4444"
|
|
7750
|
+
};
|
|
7751
|
+
function StatusDot({
|
|
7752
|
+
status = "healthy",
|
|
7753
|
+
color,
|
|
7754
|
+
label,
|
|
7755
|
+
size = 12,
|
|
7756
|
+
className
|
|
7757
|
+
}) {
|
|
7758
|
+
const fill = color ?? STATUS_DOT_COLORS[status];
|
|
7759
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7760
|
+
"span",
|
|
7761
|
+
{
|
|
7762
|
+
"data-status-dot": "",
|
|
7763
|
+
role: label ? "img" : void 0,
|
|
7764
|
+
"aria-label": label,
|
|
7765
|
+
title: label,
|
|
7766
|
+
className: cn("inline-block shrink-0 rounded-full", className),
|
|
7767
|
+
style: { width: size, height: size, backgroundColor: fill }
|
|
7768
|
+
}
|
|
7769
|
+
);
|
|
7770
|
+
}
|
|
7771
|
+
var TONE_TEXT_CLASSES = {
|
|
7772
|
+
default: "text-slate-800",
|
|
7773
|
+
positive: "text-emerald-600",
|
|
7774
|
+
negative: "text-red-600",
|
|
7775
|
+
neutral: "text-slate-500",
|
|
7776
|
+
warning: "text-amber-600",
|
|
7777
|
+
info: "text-blue-600",
|
|
7778
|
+
muted: "text-slate-400"
|
|
7779
|
+
};
|
|
7780
|
+
function toneTextColor(tone) {
|
|
7781
|
+
if (!tone) return TONE_TEXT_CLASSES.default;
|
|
7782
|
+
return TONE_TEXT_CLASSES[tone] ?? TONE_TEXT_CLASSES.default;
|
|
7783
|
+
}
|
|
7784
|
+
function toAlpha(n, upper) {
|
|
7785
|
+
let out = "";
|
|
7786
|
+
let value = n;
|
|
7787
|
+
while (value > 0) {
|
|
7788
|
+
const rem = (value - 1) % 26;
|
|
7789
|
+
out = String.fromCharCode(97 + rem) + out;
|
|
7790
|
+
value = Math.floor((value - 1) / 26);
|
|
7791
|
+
}
|
|
7792
|
+
return upper ? out.toUpperCase() : out;
|
|
7793
|
+
}
|
|
7794
|
+
var ROMAN_NUMERALS = [
|
|
7795
|
+
[1e3, "m"],
|
|
7796
|
+
[900, "cm"],
|
|
7797
|
+
[500, "d"],
|
|
7798
|
+
[400, "cd"],
|
|
7799
|
+
[100, "c"],
|
|
7800
|
+
[90, "xc"],
|
|
7801
|
+
[50, "l"],
|
|
7802
|
+
[40, "xl"],
|
|
7803
|
+
[10, "x"],
|
|
7804
|
+
[9, "ix"],
|
|
7805
|
+
[5, "v"],
|
|
7806
|
+
[4, "iv"],
|
|
7807
|
+
[1, "i"]
|
|
7808
|
+
];
|
|
7809
|
+
function toRoman(n) {
|
|
7810
|
+
if (n <= 0) return String(n);
|
|
7811
|
+
let value = n;
|
|
7812
|
+
let out = "";
|
|
7813
|
+
for (const [num, sym] of ROMAN_NUMERALS) {
|
|
7814
|
+
while (value >= num) {
|
|
7815
|
+
out += sym;
|
|
7816
|
+
value -= num;
|
|
7817
|
+
}
|
|
7818
|
+
}
|
|
7819
|
+
return out;
|
|
7820
|
+
}
|
|
7821
|
+
function formatMarker(index, style, start = 1) {
|
|
7822
|
+
const n = index + start;
|
|
7823
|
+
switch (style) {
|
|
7824
|
+
case "lower-alpha":
|
|
7825
|
+
return toAlpha(n, false);
|
|
7826
|
+
case "upper-alpha":
|
|
7827
|
+
return toAlpha(n, true);
|
|
7828
|
+
case "lower-roman":
|
|
7829
|
+
return toRoman(n);
|
|
7830
|
+
default:
|
|
7831
|
+
return String(n);
|
|
7832
|
+
}
|
|
7833
|
+
}
|
|
7834
|
+
var DEFAULT_COLOR = "#10B981";
|
|
7835
|
+
var DEFAULT_WIDTH = 100;
|
|
7836
|
+
var DEFAULT_HEIGHT = 32;
|
|
7837
|
+
function normalizeSeries(data, w, h, pad = 2) {
|
|
7838
|
+
const n = data.length;
|
|
7839
|
+
if (n === 0) return [];
|
|
7840
|
+
const min = Math.min(...data);
|
|
7841
|
+
const max = Math.max(...data);
|
|
7842
|
+
const flat = max === min;
|
|
7843
|
+
const span = flat ? 1 : max - min;
|
|
7844
|
+
const inner = h - pad * 2;
|
|
7845
|
+
return data.map((v, i) => {
|
|
7846
|
+
const x = n === 1 ? w / 2 : i / (n - 1) * w;
|
|
7847
|
+
const y = flat ? h / 2 : h - pad - (v - min) / span * inner;
|
|
7848
|
+
return { x, y };
|
|
7849
|
+
});
|
|
7850
|
+
}
|
|
7851
|
+
function buildLinePath(pts, smooth = false) {
|
|
7852
|
+
if (pts.length < 2) return "";
|
|
7853
|
+
if (!smooth) {
|
|
7854
|
+
const [first, ...rest] = pts;
|
|
7855
|
+
return `M ${fmt(first.x)} ${fmt(first.y)}` + rest.map((p) => ` L ${fmt(p.x)} ${fmt(p.y)}`).join("");
|
|
7856
|
+
}
|
|
7857
|
+
let d = `M ${fmt(pts[0].x)} ${fmt(pts[0].y)}`;
|
|
7858
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
7859
|
+
const p0 = pts[i - 1] ?? pts[i];
|
|
7860
|
+
const p1 = pts[i];
|
|
7861
|
+
const p2 = pts[i + 1];
|
|
7862
|
+
const p3 = pts[i + 2] ?? p2;
|
|
7863
|
+
const c1x = p1.x + (p2.x - p0.x) / 6;
|
|
7864
|
+
const c1y = p1.y + (p2.y - p0.y) / 6;
|
|
7865
|
+
const c2x = p2.x - (p3.x - p1.x) / 6;
|
|
7866
|
+
const c2y = p2.y - (p3.y - p1.y) / 6;
|
|
7867
|
+
d += ` C ${fmt(c1x)} ${fmt(c1y)}, ${fmt(c2x)} ${fmt(c2y)}, ${fmt(p2.x)} ${fmt(p2.y)}`;
|
|
7868
|
+
}
|
|
7869
|
+
return d;
|
|
7870
|
+
}
|
|
7871
|
+
function buildAreaPath(pts, h, smooth = false) {
|
|
7872
|
+
if (pts.length < 2) return "";
|
|
7873
|
+
const line = buildLinePath(pts, smooth);
|
|
7874
|
+
if (!line) return "";
|
|
7875
|
+
const last = pts[pts.length - 1];
|
|
7876
|
+
const first = pts[0];
|
|
7877
|
+
return `${line} L ${fmt(last.x)} ${fmt(h)} L ${fmt(first.x)} ${fmt(h)} Z`;
|
|
7878
|
+
}
|
|
7879
|
+
function fmt(n) {
|
|
7880
|
+
return Number.isInteger(n) ? String(n) : String(Math.round(n * 1e3) / 1e3);
|
|
7881
|
+
}
|
|
7882
|
+
function Sparkline({
|
|
7883
|
+
data,
|
|
7884
|
+
color = DEFAULT_COLOR,
|
|
7885
|
+
showArea = false,
|
|
7886
|
+
smooth = false,
|
|
7887
|
+
width = DEFAULT_WIDTH,
|
|
7888
|
+
height = DEFAULT_HEIGHT,
|
|
7889
|
+
pad = 2,
|
|
7890
|
+
strokeWidth = 2,
|
|
7891
|
+
className
|
|
7892
|
+
}) {
|
|
7893
|
+
const gradientId = React4.useId();
|
|
7894
|
+
if (data.length < 2) return null;
|
|
7895
|
+
const points = normalizeSeries(data, width, height, pad);
|
|
7896
|
+
const linePath = buildLinePath(points, smooth);
|
|
7897
|
+
const areaPath = showArea ? buildAreaPath(points, height, smooth) : "";
|
|
7898
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7899
|
+
"svg",
|
|
7900
|
+
{
|
|
7901
|
+
className: cn("block", className),
|
|
7902
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
7903
|
+
preserveAspectRatio: "none",
|
|
7904
|
+
"aria-hidden": "true",
|
|
7905
|
+
focusable: "false",
|
|
7906
|
+
children: [
|
|
7907
|
+
showArea && areaPath && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7908
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
7909
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: color, stopOpacity: 0.18 }),
|
|
7910
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: color, stopOpacity: 0 })
|
|
7911
|
+
] }) }),
|
|
7912
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: `url(#${gradientId})`, stroke: "none" })
|
|
7913
|
+
] }),
|
|
7914
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7915
|
+
"path",
|
|
7916
|
+
{
|
|
7917
|
+
d: linePath,
|
|
7918
|
+
fill: "none",
|
|
7919
|
+
stroke: color,
|
|
7920
|
+
strokeWidth,
|
|
7921
|
+
strokeLinecap: "round",
|
|
7922
|
+
strokeLinejoin: "round",
|
|
7923
|
+
style: { vectorEffect: "non-scaling-stroke" }
|
|
7924
|
+
}
|
|
7925
|
+
)
|
|
7926
|
+
]
|
|
7927
|
+
}
|
|
7928
|
+
);
|
|
7929
|
+
}
|
|
7930
|
+
var DEFAULT_SERIES_COLOR = "#3B82F6";
|
|
7931
|
+
var DEFAULT_PILL_COLOR = "#3B6FE0";
|
|
7932
|
+
var BASELINE_COLOR = "#EEF1F5";
|
|
7933
|
+
var CHART_WIDTH = 900;
|
|
7934
|
+
var CHART_HEIGHT = 240;
|
|
7935
|
+
function hexToRgba(hex, alpha) {
|
|
7936
|
+
const match = /^#?([\da-f]{6})$/i.exec(hex.trim());
|
|
7937
|
+
if (!match) return hex;
|
|
7938
|
+
const int = parseInt(match[1], 16);
|
|
7939
|
+
const r = int >> 16 & 255;
|
|
7940
|
+
const g = int >> 8 & 255;
|
|
7941
|
+
const b = int & 255;
|
|
7942
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
7943
|
+
}
|
|
7944
|
+
function splitTickLabel(label) {
|
|
7945
|
+
const match = /^(.*?)[\s\u00a0]+(\S+)$/.exec(label.trim());
|
|
7946
|
+
if (!match) return [label];
|
|
7947
|
+
return [match[1], match[2]];
|
|
7948
|
+
}
|
|
7949
|
+
function KpiCardWithChart({
|
|
7950
|
+
title,
|
|
7951
|
+
rangeLabel,
|
|
7952
|
+
data,
|
|
7953
|
+
xAxisLabels,
|
|
7954
|
+
value,
|
|
7955
|
+
delta,
|
|
7956
|
+
seriesColor = DEFAULT_SERIES_COLOR,
|
|
7957
|
+
pillColor = DEFAULT_PILL_COLOR,
|
|
7958
|
+
showBaseline = true,
|
|
7959
|
+
valuePrefix,
|
|
7960
|
+
valueSuffix,
|
|
7961
|
+
className
|
|
7962
|
+
}) {
|
|
7963
|
+
const series = data?.map((point) => point.value) ?? [];
|
|
7964
|
+
const hasChart = series.length >= 2;
|
|
7965
|
+
const hasData = series.length > 0;
|
|
7966
|
+
const formattedValue = value === void 0 || value === null ? null : `${valuePrefix ?? ""}${value}${valueSuffix ?? ""}`;
|
|
7967
|
+
const sparklineProps = {
|
|
7968
|
+
data: series,
|
|
7969
|
+
color: seriesColor,
|
|
7970
|
+
showArea: true,
|
|
7971
|
+
smooth: true,
|
|
7972
|
+
width: CHART_WIDTH,
|
|
7973
|
+
height: CHART_HEIGHT,
|
|
7974
|
+
pad: 12,
|
|
7975
|
+
strokeWidth: 2.5
|
|
7976
|
+
};
|
|
7977
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: cn("w-full overflow-hidden bg-white p-7 sm:p-8", className), children: [
|
|
7978
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
7979
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
7980
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "truncate font-bold text-base text-slate-900 leading-tight tracking-tight", children: title }),
|
|
7981
|
+
formattedValue !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
|
|
7982
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-2xl text-slate-900 tabular-nums", children: formattedValue }),
|
|
7983
|
+
delta && /* @__PURE__ */ jsxRuntime.jsx(DeltaPill, { value: delta.value, direction: delta.direction })
|
|
7984
|
+
] })
|
|
7985
|
+
] }),
|
|
7986
|
+
rangeLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7987
|
+
"span",
|
|
7988
|
+
{
|
|
7989
|
+
className: "shrink-0 rounded-full px-4 py-1.5 font-semibold text-xs",
|
|
7990
|
+
style: {
|
|
7991
|
+
color: pillColor,
|
|
7992
|
+
backgroundColor: hexToRgba(pillColor, 0.12)
|
|
7993
|
+
},
|
|
7994
|
+
children: rangeLabel
|
|
7995
|
+
}
|
|
7996
|
+
)
|
|
7997
|
+
] }),
|
|
7998
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6", children: hasChart ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-[180px] w-full", children: [
|
|
7999
|
+
showBaseline && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8000
|
+
"div",
|
|
8001
|
+
{
|
|
8002
|
+
"aria-hidden": "true",
|
|
8003
|
+
className: "absolute inset-x-0 top-1/2 h-px",
|
|
8004
|
+
style: { backgroundColor: BASELINE_COLOR }
|
|
8005
|
+
}
|
|
8006
|
+
),
|
|
8007
|
+
/* @__PURE__ */ jsxRuntime.jsx(Sparkline, { ...sparklineProps, className: "h-full w-full" })
|
|
8008
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[180px] w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-400 text-sm", children: hasData ? "Not enough data" : "No data" }) }) }),
|
|
8009
|
+
hasChart && xAxisLabels && xAxisLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex items-start justify-between", children: xAxisLabels.map((label, index) => {
|
|
8010
|
+
const [lead, trailing] = splitTickLabel(label);
|
|
8011
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8012
|
+
"span",
|
|
8013
|
+
{
|
|
8014
|
+
className: "flex flex-col text-slate-400 text-xs leading-tight",
|
|
8015
|
+
children: [
|
|
8016
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: lead }),
|
|
8017
|
+
trailing !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { children: trailing })
|
|
8018
|
+
]
|
|
8019
|
+
},
|
|
8020
|
+
`${label}-${index}`
|
|
8021
|
+
);
|
|
8022
|
+
}) })
|
|
8023
|
+
] });
|
|
8024
|
+
}
|
|
8025
|
+
|
|
8026
|
+
// src/components/data-display/primitives/arc.tsx
|
|
8027
|
+
function clamp(n, min, max) {
|
|
8028
|
+
return Math.min(Math.max(n, min), max);
|
|
8029
|
+
}
|
|
8030
|
+
function arcLength(r) {
|
|
8031
|
+
return Math.PI * r;
|
|
8032
|
+
}
|
|
8033
|
+
function halfCircleArcPath(cx, cy, r) {
|
|
8034
|
+
const startX = cx - r;
|
|
8035
|
+
const endX = cx + r;
|
|
8036
|
+
return `M ${startX} ${cy} A ${r} ${r} 0 0 1 ${endX} ${cy}`;
|
|
8037
|
+
}
|
|
8038
|
+
function dashOffsetForFraction(arcLen, fraction) {
|
|
8039
|
+
return arcLen * (1 - clamp(fraction, 0, 1));
|
|
8040
|
+
}
|
|
8041
|
+
function segmentDashArray(value, total, circumference, gap = 0) {
|
|
8042
|
+
const safeTotal = total > 0 ? total : 0;
|
|
8043
|
+
const fraction = safeTotal > 0 ? value / safeTotal : 0;
|
|
8044
|
+
const rawLength = fraction * circumference;
|
|
8045
|
+
const visible = Math.max(0, rawLength - gap);
|
|
8046
|
+
const remainder = Math.max(0, circumference - visible);
|
|
8047
|
+
return {
|
|
8048
|
+
dasharray: `${visible} ${remainder}`,
|
|
8049
|
+
dashoffset: 0
|
|
8050
|
+
};
|
|
8051
|
+
}
|
|
8052
|
+
var DEFAULT_PALETTE = [
|
|
8053
|
+
"#1f2d4d",
|
|
8054
|
+
// navy
|
|
8055
|
+
"#3b82f6",
|
|
8056
|
+
// blue
|
|
8057
|
+
"#f59e0b",
|
|
8058
|
+
// amber
|
|
8059
|
+
"#10b981",
|
|
8060
|
+
// emerald
|
|
8061
|
+
"#8b5cf6",
|
|
8062
|
+
// violet
|
|
8063
|
+
"#ec4899",
|
|
8064
|
+
// pink
|
|
8065
|
+
"#14b8a6",
|
|
8066
|
+
// teal
|
|
8067
|
+
"#f97316"
|
|
8068
|
+
// orange
|
|
8069
|
+
];
|
|
8070
|
+
var EMPTY_RING_COLOR = "#e5e7eb";
|
|
8071
|
+
var VIEWBOX = 100;
|
|
8072
|
+
var CENTER = VIEWBOX / 2;
|
|
8073
|
+
var RADIUS = 38;
|
|
8074
|
+
var STROKE_WIDTH = 18;
|
|
8075
|
+
var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
|
8076
|
+
var SEGMENT_GAP = 1.5;
|
|
8077
|
+
function segmentColor(segment, index) {
|
|
8078
|
+
return segment.color ?? DEFAULT_PALETTE[index % DEFAULT_PALETTE.length];
|
|
8079
|
+
}
|
|
8080
|
+
function legendValue(segment, total, valueFormat) {
|
|
8081
|
+
if (segment.displayValue != null) return segment.displayValue;
|
|
8082
|
+
if (valueFormat === "none") return null;
|
|
8083
|
+
if (valueFormat === "value") return String(segment.value);
|
|
8084
|
+
if (total <= 0) return "0%";
|
|
8085
|
+
const pct = segment.value / total * 100;
|
|
8086
|
+
const rounded = Math.round(pct * 10) / 10;
|
|
8087
|
+
return `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}%`;
|
|
8088
|
+
}
|
|
8089
|
+
function PieChartArtifact({
|
|
8090
|
+
title,
|
|
8091
|
+
centerLabel,
|
|
8092
|
+
centerSublabel,
|
|
8093
|
+
segments,
|
|
8094
|
+
valueFormat = "percent",
|
|
8095
|
+
className
|
|
8096
|
+
}) {
|
|
8097
|
+
const safeSegments = segments ?? [];
|
|
8098
|
+
const total = safeSegments.reduce((sum, s) => sum + (s.value > 0 ? s.value : 0), 0);
|
|
8099
|
+
const isEmpty = safeSegments.length === 0 || total <= 0;
|
|
8100
|
+
let runningOffset = 0;
|
|
8101
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: cn("w-full p-6", className), children: [
|
|
8102
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold text-base text-slate-900 leading-tight", children: title }) : null,
|
|
8103
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
8104
|
+
"div",
|
|
8105
|
+
{
|
|
8106
|
+
className: cn(
|
|
8107
|
+
"flex flex-col items-center gap-6 sm:flex-row sm:items-center sm:gap-8",
|
|
8108
|
+
title && "mt-5"
|
|
8109
|
+
),
|
|
8110
|
+
children: [
|
|
8111
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative shrink-0", style: { width: 150, height: 150 }, children: [
|
|
8112
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8113
|
+
"svg",
|
|
8114
|
+
{
|
|
8115
|
+
viewBox: `0 0 ${VIEWBOX} ${VIEWBOX}`,
|
|
8116
|
+
width: "100%",
|
|
8117
|
+
height: "100%",
|
|
8118
|
+
role: "img",
|
|
8119
|
+
"aria-label": title ?? "Donut chart",
|
|
8120
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `rotate(-90 ${CENTER} ${CENTER})`, fill: "none", children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8121
|
+
"circle",
|
|
8122
|
+
{
|
|
8123
|
+
cx: CENTER,
|
|
8124
|
+
cy: CENTER,
|
|
8125
|
+
r: RADIUS,
|
|
8126
|
+
stroke: EMPTY_RING_COLOR,
|
|
8127
|
+
strokeWidth: STROKE_WIDTH
|
|
8128
|
+
}
|
|
8129
|
+
) : safeSegments.map((segment, index) => {
|
|
8130
|
+
const value = segment.value > 0 ? segment.value : 0;
|
|
8131
|
+
const { dasharray } = segmentDashArray(
|
|
8132
|
+
value,
|
|
8133
|
+
total,
|
|
8134
|
+
CIRCUMFERENCE,
|
|
8135
|
+
SEGMENT_GAP
|
|
8136
|
+
);
|
|
8137
|
+
const dashoffset = -runningOffset;
|
|
8138
|
+
runningOffset += value / total * CIRCUMFERENCE;
|
|
8139
|
+
if (value <= 0) return null;
|
|
8140
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8141
|
+
"circle",
|
|
8142
|
+
{
|
|
8143
|
+
cx: CENTER,
|
|
8144
|
+
cy: CENTER,
|
|
8145
|
+
r: RADIUS,
|
|
8146
|
+
stroke: segmentColor(segment, index),
|
|
8147
|
+
strokeWidth: STROKE_WIDTH,
|
|
8148
|
+
strokeDasharray: dasharray,
|
|
8149
|
+
strokeDashoffset: dashoffset
|
|
8150
|
+
},
|
|
8151
|
+
segment.label + index
|
|
8152
|
+
);
|
|
8153
|
+
}) })
|
|
8154
|
+
}
|
|
8155
|
+
),
|
|
8156
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center", children: isEmpty ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-400 text-sm", children: "No data" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8157
|
+
centerLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-base text-slate-900 leading-none", children: centerLabel }) : null,
|
|
8158
|
+
centerSublabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-slate-500 text-xs leading-none", children: centerSublabel }) : null
|
|
8159
|
+
] }) })
|
|
8160
|
+
] }),
|
|
8161
|
+
!isEmpty ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex w-full min-w-0 flex-col gap-3.5", children: safeSegments.map((segment, index) => {
|
|
8162
|
+
const value = legendValue(segment, total, valueFormat);
|
|
8163
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8164
|
+
"li",
|
|
8165
|
+
{
|
|
8166
|
+
className: "flex items-center justify-between gap-4",
|
|
8167
|
+
children: [
|
|
8168
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
8169
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8170
|
+
"span",
|
|
8171
|
+
{
|
|
8172
|
+
"aria-hidden": "true",
|
|
8173
|
+
className: "inline-block size-3 shrink-0 rounded-sm",
|
|
8174
|
+
style: { backgroundColor: segmentColor(segment, index) }
|
|
8175
|
+
}
|
|
8176
|
+
),
|
|
8177
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-slate-600 text-sm", children: segment.label })
|
|
8178
|
+
] }),
|
|
8179
|
+
value != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-bold text-slate-900 text-sm tabular-nums", children: value }) : null
|
|
8180
|
+
]
|
|
8181
|
+
},
|
|
8182
|
+
segment.label + index
|
|
8183
|
+
);
|
|
8184
|
+
}) }) : null
|
|
8185
|
+
]
|
|
8186
|
+
}
|
|
8187
|
+
)
|
|
8188
|
+
] });
|
|
8189
|
+
}
|
|
8190
|
+
var TREND_COLORS = {
|
|
8191
|
+
up: "#22C55E",
|
|
8192
|
+
down: "#3B82F6",
|
|
8193
|
+
flat: "#9CA3AF"
|
|
8194
|
+
};
|
|
8195
|
+
var PALETTE = ["#22C55E", "#3B82F6", "#F59E0B", "#8B5CF6", "#EF4444"];
|
|
8196
|
+
function resolveColor(row, index) {
|
|
8197
|
+
if (row.color) return row.color;
|
|
8198
|
+
if (row.trend) return TREND_COLORS[row.trend];
|
|
8199
|
+
return PALETTE[index % PALETTE.length];
|
|
8200
|
+
}
|
|
8201
|
+
function StackedSparklines({ rows, className }) {
|
|
8202
|
+
const safeRows = Array.isArray(rows) ? rows : [];
|
|
8203
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6", children: safeRows.map((row, index) => {
|
|
8204
|
+
const color = resolveColor(row, index);
|
|
8205
|
+
const hasData = Array.isArray(row.data) && row.data.length >= 2;
|
|
8206
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8207
|
+
"div",
|
|
8208
|
+
{
|
|
8209
|
+
className: cn(
|
|
8210
|
+
"flex items-center justify-between gap-6 py-5",
|
|
8211
|
+
// Hairline divider on every row except the first.
|
|
8212
|
+
index > 0 && "border-slate-200 border-t"
|
|
8213
|
+
),
|
|
8214
|
+
children: [
|
|
8215
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
|
|
8216
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-slate-500 leading-tight", children: row.label }),
|
|
8217
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-2xl text-slate-900 leading-tight tracking-tight tabular-nums", children: row.value })
|
|
8218
|
+
] }),
|
|
8219
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-9 w-[28%] min-w-[88px] shrink-0", children: hasData ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8220
|
+
Sparkline,
|
|
8221
|
+
{
|
|
8222
|
+
data: row.data,
|
|
8223
|
+
color,
|
|
8224
|
+
width: 110,
|
|
8225
|
+
height: 36,
|
|
8226
|
+
pad: 4,
|
|
8227
|
+
strokeWidth: 2.5,
|
|
8228
|
+
className: "h-full w-full"
|
|
8229
|
+
}
|
|
8230
|
+
) : (
|
|
8231
|
+
// Empty/edge state: data length < 2 → blank area, no crash.
|
|
8232
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "h-full w-full" })
|
|
8233
|
+
) })
|
|
8234
|
+
]
|
|
8235
|
+
},
|
|
8236
|
+
`${row.label}-${index}`
|
|
8237
|
+
);
|
|
8238
|
+
}) }) });
|
|
8239
|
+
}
|
|
8240
|
+
var DEFAULT_MIN = 0;
|
|
8241
|
+
var DEFAULT_MAX = 100;
|
|
8242
|
+
var DEFAULT_FILL_COLOR = "#22c55e";
|
|
8243
|
+
var DEFAULT_TRACK_COLOR = "#eef0f2";
|
|
8244
|
+
var DEFAULT_VALUE_COLOR = "#0f172a";
|
|
8245
|
+
var DEFAULT_MIN_LABEL_COLOR = "#94a3b8";
|
|
8246
|
+
var DEFAULT_TITLE_COLOR = "#0f172a";
|
|
8247
|
+
var VIEW_W = 240;
|
|
8248
|
+
var VIEW_H = 130;
|
|
8249
|
+
var CX = 120;
|
|
8250
|
+
var CY = 110;
|
|
8251
|
+
var RADIUS2 = 100;
|
|
8252
|
+
var STROKE_WIDTH2 = 18;
|
|
8253
|
+
function clamp01(n) {
|
|
8254
|
+
if (Number.isNaN(n)) return 0;
|
|
8255
|
+
return Math.min(Math.max(n, 0), 1);
|
|
8256
|
+
}
|
|
8257
|
+
function StatCardHalfCircle({
|
|
8258
|
+
title,
|
|
8259
|
+
value,
|
|
8260
|
+
displayValue,
|
|
8261
|
+
min = DEFAULT_MIN,
|
|
8262
|
+
max = DEFAULT_MAX,
|
|
8263
|
+
minLabel,
|
|
8264
|
+
maxLabel,
|
|
8265
|
+
fillColor = DEFAULT_FILL_COLOR,
|
|
8266
|
+
trackColor = DEFAULT_TRACK_COLOR,
|
|
8267
|
+
valueColor = DEFAULT_VALUE_COLOR,
|
|
8268
|
+
minLabelColor = DEFAULT_MIN_LABEL_COLOR,
|
|
8269
|
+
// maxLabel color defaults to the fill color so a positive reading reads as one accent.
|
|
8270
|
+
maxLabelColor,
|
|
8271
|
+
titleColor = DEFAULT_TITLE_COLOR,
|
|
8272
|
+
className
|
|
8273
|
+
}) {
|
|
8274
|
+
const span = max - min;
|
|
8275
|
+
const fraction = span > 0 ? clamp01((value - min) / span) : 0;
|
|
8276
|
+
const arcPath = halfCircleArcPath(CX, CY, RADIUS2);
|
|
8277
|
+
const len = arcLength(RADIUS2);
|
|
8278
|
+
const fillDashOffset = dashOffsetForFraction(len, fraction);
|
|
8279
|
+
const readout = displayValue ?? String(value);
|
|
8280
|
+
const resolvedMaxLabelColor = maxLabelColor ?? fillColor;
|
|
8281
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: cn("w-full max-w-[440px] p-5 sm:p-6", className), children: [
|
|
8282
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8283
|
+
"h3",
|
|
8284
|
+
{
|
|
8285
|
+
className: "font-semibold text-base leading-tight",
|
|
8286
|
+
style: { color: titleColor },
|
|
8287
|
+
children: title
|
|
8288
|
+
}
|
|
8289
|
+
),
|
|
8290
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mx-auto mt-4 w-full max-w-[280px]", children: [
|
|
8291
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
8292
|
+
"svg",
|
|
8293
|
+
{
|
|
8294
|
+
viewBox: `0 0 ${VIEW_W} ${VIEW_H}`,
|
|
8295
|
+
className: "block w-full",
|
|
8296
|
+
role: "img",
|
|
8297
|
+
"aria-label": `${title}: ${readout}`,
|
|
8298
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
8299
|
+
children: [
|
|
8300
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8301
|
+
"path",
|
|
8302
|
+
{
|
|
8303
|
+
d: arcPath,
|
|
8304
|
+
fill: "none",
|
|
8305
|
+
stroke: trackColor,
|
|
8306
|
+
strokeWidth: STROKE_WIDTH2,
|
|
8307
|
+
strokeLinecap: "round"
|
|
8308
|
+
}
|
|
8309
|
+
),
|
|
8310
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8311
|
+
"path",
|
|
8312
|
+
{
|
|
8313
|
+
d: arcPath,
|
|
8314
|
+
fill: "none",
|
|
8315
|
+
stroke: fillColor,
|
|
8316
|
+
strokeWidth: STROKE_WIDTH2,
|
|
8317
|
+
strokeLinecap: "round",
|
|
8318
|
+
strokeDasharray: len,
|
|
8319
|
+
strokeDashoffset: fillDashOffset
|
|
8320
|
+
}
|
|
8321
|
+
)
|
|
8322
|
+
]
|
|
8323
|
+
}
|
|
8324
|
+
),
|
|
8325
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8326
|
+
"div",
|
|
8327
|
+
{
|
|
8328
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 flex items-end justify-center pb-1",
|
|
8329
|
+
"aria-hidden": "true",
|
|
8330
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8331
|
+
"span",
|
|
8332
|
+
{
|
|
8333
|
+
className: "font-bold text-2xl leading-none tabular-nums",
|
|
8334
|
+
style: { color: valueColor },
|
|
8335
|
+
children: readout
|
|
8336
|
+
}
|
|
8337
|
+
)
|
|
8338
|
+
}
|
|
8339
|
+
)
|
|
8340
|
+
] }),
|
|
8341
|
+
(minLabel || maxLabel) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto mt-3 flex w-full max-w-[280px] items-baseline justify-between", children: [
|
|
8342
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-xs", style: { color: minLabelColor }, children: minLabel }),
|
|
8343
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8344
|
+
"span",
|
|
8345
|
+
{
|
|
8346
|
+
className: "font-semibold text-xs",
|
|
8347
|
+
style: { color: resolvedMaxLabelColor },
|
|
8348
|
+
children: maxLabel
|
|
8349
|
+
}
|
|
8350
|
+
)
|
|
8351
|
+
] })
|
|
8352
|
+
] });
|
|
8353
|
+
}
|
|
8354
|
+
var TONE_TEXT_COLOR = {
|
|
8355
|
+
default: "",
|
|
8356
|
+
neutral: "",
|
|
8357
|
+
positive: "text-emerald-600",
|
|
8358
|
+
negative: "text-rose-600",
|
|
8359
|
+
warning: "text-amber-600",
|
|
8360
|
+
info: "text-blue-600",
|
|
8361
|
+
muted: "text-slate-400"
|
|
8362
|
+
};
|
|
8363
|
+
function toneTextColor2(tone, toneMap = {}) {
|
|
8364
|
+
if (!tone) return "";
|
|
8365
|
+
const override = toneMap[tone];
|
|
8366
|
+
if (override !== void 0) return override;
|
|
8367
|
+
return TONE_TEXT_COLOR[tone] ?? "";
|
|
8368
|
+
}
|
|
8369
|
+
function parseBoldString(s) {
|
|
8370
|
+
const segments = [];
|
|
8371
|
+
if (!s) return segments;
|
|
8372
|
+
const regex = /\*\*([\s\S]+?)\*\*/g;
|
|
8373
|
+
let lastIndex = 0;
|
|
8374
|
+
let match;
|
|
8375
|
+
while ((match = regex.exec(s)) !== null) {
|
|
8376
|
+
if (match.index > lastIndex) {
|
|
8377
|
+
segments.push({ text: s.slice(lastIndex, match.index) });
|
|
8378
|
+
}
|
|
8379
|
+
segments.push({ text: match[1], bold: true });
|
|
8380
|
+
lastIndex = regex.lastIndex;
|
|
8381
|
+
}
|
|
8382
|
+
if (lastIndex < s.length) {
|
|
8383
|
+
segments.push({ text: s.slice(lastIndex) });
|
|
8384
|
+
}
|
|
8385
|
+
return segments;
|
|
8386
|
+
}
|
|
8387
|
+
function normalizeRich(content) {
|
|
8388
|
+
return typeof content === "string" ? parseBoldString(content) : content;
|
|
8389
|
+
}
|
|
8390
|
+
function renderRich(content, toneMap = {}) {
|
|
8391
|
+
const segments = normalizeRich(content);
|
|
8392
|
+
return segments.map((segment, index) => {
|
|
8393
|
+
const colorClass = toneTextColor2(segment.tone, toneMap);
|
|
8394
|
+
if (segment.code) {
|
|
8395
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8396
|
+
"code",
|
|
8397
|
+
{
|
|
8398
|
+
className: cn(
|
|
8399
|
+
"rounded bg-slate-100 px-1 py-0.5 font-mono text-[0.85em] text-slate-700",
|
|
8400
|
+
colorClass
|
|
8401
|
+
),
|
|
8402
|
+
children: segment.text
|
|
8403
|
+
},
|
|
8404
|
+
index
|
|
8405
|
+
);
|
|
8406
|
+
}
|
|
8407
|
+
if (segment.bold) {
|
|
8408
|
+
return /* @__PURE__ */ jsxRuntime.jsx("strong", { className: cn("font-bold", colorClass), children: segment.text }, index);
|
|
8409
|
+
}
|
|
8410
|
+
if (colorClass) {
|
|
8411
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: colorClass, children: segment.text }, index);
|
|
8412
|
+
}
|
|
8413
|
+
return /* @__PURE__ */ jsxRuntime.jsx(React4.Fragment, { children: segment.text }, index);
|
|
8414
|
+
});
|
|
8415
|
+
}
|
|
8416
|
+
var VIEW_W2 = 800;
|
|
8417
|
+
var VIEW_H2 = 300;
|
|
8418
|
+
var PAD_LEFT = 8;
|
|
8419
|
+
var PAD_RIGHT = 8;
|
|
8420
|
+
var PAD_TOP = 16;
|
|
8421
|
+
var PAD_BOTTOM = 16;
|
|
8422
|
+
var PLOT_W = VIEW_W2 - PAD_LEFT - PAD_RIGHT;
|
|
8423
|
+
var PLOT_H = VIEW_H2 - PAD_TOP - PAD_BOTTOM;
|
|
8424
|
+
var GRID_COLOR = "#e2e8f0";
|
|
8425
|
+
var AXIS_LABEL_COLOR = "#94a3b8";
|
|
8426
|
+
var BAND_FILL = "#94a3b8";
|
|
8427
|
+
var TABLE_HEADER_BG = "#f8fafc";
|
|
8428
|
+
var TABLE_HEADER_TEXT = "#64748b";
|
|
8429
|
+
var TABLE_ROW_BORDER = "#f1f5f9";
|
|
8430
|
+
var FIRST_COL_TEXT = "#0f172a";
|
|
8431
|
+
var SUMMARY_TEXT_COLOR = "#475569";
|
|
8432
|
+
function niceMax(value) {
|
|
8433
|
+
if (value <= 0) return 1;
|
|
8434
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(value)));
|
|
8435
|
+
const normalized = value / magnitude;
|
|
8436
|
+
let nice;
|
|
8437
|
+
if (normalized <= 1) nice = 1;
|
|
8438
|
+
else if (normalized <= 2) nice = 2;
|
|
8439
|
+
else if (normalized <= 5) nice = 5;
|
|
8440
|
+
else nice = 10;
|
|
8441
|
+
return nice * magnitude;
|
|
8442
|
+
}
|
|
8443
|
+
function yFor(value, yMax) {
|
|
8444
|
+
const span = yMax || 1;
|
|
8445
|
+
return PAD_TOP + PLOT_H - value / span * PLOT_H;
|
|
8446
|
+
}
|
|
8447
|
+
function xFor(i, n) {
|
|
8448
|
+
if (n <= 1) return PAD_LEFT + PLOT_W / 2;
|
|
8449
|
+
return PAD_LEFT + i / (n - 1) * PLOT_W;
|
|
8450
|
+
}
|
|
8451
|
+
var ALIGN_CLASS = {
|
|
8452
|
+
left: "text-left",
|
|
8453
|
+
right: "text-right",
|
|
8454
|
+
center: "text-center"
|
|
8455
|
+
};
|
|
8456
|
+
function deltaDirection(d) {
|
|
8457
|
+
return d ?? "flat";
|
|
8458
|
+
}
|
|
8459
|
+
function TableListArtifact({
|
|
8460
|
+
chartTitle,
|
|
8461
|
+
series,
|
|
8462
|
+
xAxisLabels,
|
|
8463
|
+
yAxisTicks,
|
|
8464
|
+
chartType = "area",
|
|
8465
|
+
tableColumns,
|
|
8466
|
+
tableRows,
|
|
8467
|
+
summaryText,
|
|
8468
|
+
className
|
|
8469
|
+
}) {
|
|
8470
|
+
const gradientId = React4.useId();
|
|
8471
|
+
const hasSeries = Array.isArray(series) && series.length > 0 && series.some((s) => Array.isArray(s.data) && s.data.length > 0);
|
|
8472
|
+
const dataMax = hasSeries ? Math.max(
|
|
8473
|
+
0,
|
|
8474
|
+
...series.flatMap((s) => Array.isArray(s.data) ? s.data : [])
|
|
8475
|
+
) : 0;
|
|
8476
|
+
const ticks = yAxisTicks && yAxisTicks.length > 0 ? [...yAxisTicks].sort((a, b) => b.value - a.value) : null;
|
|
8477
|
+
const yMax = ticks ? Math.max(...ticks.map((t) => t.value), dataMax) : niceMax(dataMax);
|
|
8478
|
+
const seriesPoints = hasSeries ? series.map((s) => {
|
|
8479
|
+
const data = Array.isArray(s.data) ? s.data : [];
|
|
8480
|
+
const n = data.length;
|
|
8481
|
+
const pts = data.map((v, i) => ({ x: xFor(i, n), y: yFor(v, yMax) }));
|
|
8482
|
+
return { color: s.color, pts };
|
|
8483
|
+
}) : [];
|
|
8484
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-4", className), children: [
|
|
8485
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: "border-[#e5e7eb] p-6", children: [
|
|
8486
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
8487
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold text-base text-slate-800 leading-snug", children: chartTitle }),
|
|
8488
|
+
hasSeries && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap items-center gap-x-4 gap-y-1", children: series.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8489
|
+
"li",
|
|
8490
|
+
{
|
|
8491
|
+
className: "flex items-center gap-2 text-slate-600 text-sm",
|
|
8492
|
+
children: [
|
|
8493
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8494
|
+
"span",
|
|
8495
|
+
{
|
|
8496
|
+
"aria-hidden": "true",
|
|
8497
|
+
className: "inline-block size-2 shrink-0 rounded-full",
|
|
8498
|
+
style: { backgroundColor: s.color }
|
|
8499
|
+
}
|
|
8500
|
+
),
|
|
8501
|
+
s.name
|
|
8502
|
+
]
|
|
8503
|
+
},
|
|
8504
|
+
s.name
|
|
8505
|
+
)) })
|
|
8506
|
+
] }),
|
|
8507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5", children: [
|
|
8508
|
+
hasSeries ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8509
|
+
"svg",
|
|
8510
|
+
{
|
|
8511
|
+
className: "block h-[300px] w-full",
|
|
8512
|
+
viewBox: `0 0 ${VIEW_W2} ${VIEW_H2}`,
|
|
8513
|
+
preserveAspectRatio: "none",
|
|
8514
|
+
role: "img",
|
|
8515
|
+
"aria-label": chartTitle,
|
|
8516
|
+
children: [
|
|
8517
|
+
ticks?.map((tick) => {
|
|
8518
|
+
const y = yFor(tick.value, yMax);
|
|
8519
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
|
|
8520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8521
|
+
"line",
|
|
8522
|
+
{
|
|
8523
|
+
x1: PAD_LEFT,
|
|
8524
|
+
y1: y,
|
|
8525
|
+
x2: VIEW_W2 - PAD_RIGHT,
|
|
8526
|
+
y2: y,
|
|
8527
|
+
stroke: GRID_COLOR,
|
|
8528
|
+
strokeWidth: 1,
|
|
8529
|
+
style: { vectorEffect: "non-scaling-stroke" }
|
|
8530
|
+
}
|
|
8531
|
+
),
|
|
8532
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8533
|
+
"text",
|
|
8534
|
+
{
|
|
8535
|
+
x: PAD_LEFT,
|
|
8536
|
+
y: y - 6,
|
|
8537
|
+
fill: AXIS_LABEL_COLOR,
|
|
8538
|
+
fontSize: 13,
|
|
8539
|
+
textAnchor: "start",
|
|
8540
|
+
children: tick.label
|
|
8541
|
+
}
|
|
8542
|
+
)
|
|
8543
|
+
] }, tick.label);
|
|
8544
|
+
}),
|
|
8545
|
+
chartType === "area" && seriesPoints.length >= 2 && seriesPoints[0].pts.length > 1 && seriesPoints[1].pts.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8546
|
+
"path",
|
|
8547
|
+
{
|
|
8548
|
+
d: bandPath(seriesPoints[0].pts, seriesPoints[1].pts),
|
|
8549
|
+
fill: BAND_FILL,
|
|
8550
|
+
fillOpacity: 0.12,
|
|
8551
|
+
stroke: "none"
|
|
8552
|
+
}
|
|
8553
|
+
),
|
|
8554
|
+
chartType === "area" && seriesPoints.length > 0 && (() => {
|
|
8555
|
+
const bottom = seriesPoints[seriesPoints.length - 1];
|
|
8556
|
+
const area = buildAreaPath(bottom.pts, VIEW_H2 - PAD_BOTTOM);
|
|
8557
|
+
if (!area) return null;
|
|
8558
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8559
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8560
|
+
"linearGradient",
|
|
8561
|
+
{
|
|
8562
|
+
id: gradientId,
|
|
8563
|
+
x1: "0",
|
|
8564
|
+
y1: "0",
|
|
8565
|
+
x2: "0",
|
|
8566
|
+
y2: "1",
|
|
8567
|
+
children: [
|
|
8568
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8569
|
+
"stop",
|
|
8570
|
+
{
|
|
8571
|
+
offset: "0%",
|
|
8572
|
+
stopColor: bottom.color,
|
|
8573
|
+
stopOpacity: 0.14
|
|
8574
|
+
}
|
|
8575
|
+
),
|
|
8576
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8577
|
+
"stop",
|
|
8578
|
+
{
|
|
8579
|
+
offset: "100%",
|
|
8580
|
+
stopColor: bottom.color,
|
|
8581
|
+
stopOpacity: 0
|
|
8582
|
+
}
|
|
8583
|
+
)
|
|
8584
|
+
]
|
|
8585
|
+
}
|
|
8586
|
+
) }),
|
|
8587
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8588
|
+
"path",
|
|
8589
|
+
{
|
|
8590
|
+
d: area,
|
|
8591
|
+
fill: `url(#${gradientId})`,
|
|
8592
|
+
stroke: "none"
|
|
8593
|
+
}
|
|
8594
|
+
)
|
|
8595
|
+
] });
|
|
8596
|
+
})(),
|
|
8597
|
+
seriesPoints.map((sp, idx) => {
|
|
8598
|
+
const d = buildLinePath(sp.pts, true);
|
|
8599
|
+
if (!d) return null;
|
|
8600
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8601
|
+
"path",
|
|
8602
|
+
{
|
|
8603
|
+
d,
|
|
8604
|
+
fill: "none",
|
|
8605
|
+
stroke: sp.color,
|
|
8606
|
+
strokeWidth: 3,
|
|
8607
|
+
strokeLinecap: "round",
|
|
8608
|
+
strokeLinejoin: "round",
|
|
8609
|
+
style: { vectorEffect: "non-scaling-stroke" }
|
|
8610
|
+
},
|
|
8611
|
+
series[idx].name
|
|
8612
|
+
);
|
|
8613
|
+
})
|
|
8614
|
+
]
|
|
8615
|
+
}
|
|
8616
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[160px] items-center justify-center text-slate-400 text-sm", children: "No data" }),
|
|
8617
|
+
hasSeries && xAxisLabels && xAxisLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex justify-between px-1", children: xAxisLabels.map((label, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8618
|
+
"span",
|
|
8619
|
+
{
|
|
8620
|
+
className: "text-slate-400 text-xs",
|
|
8621
|
+
style: { color: AXIS_LABEL_COLOR },
|
|
8622
|
+
children: label
|
|
8623
|
+
},
|
|
8624
|
+
`${label}-${i}`
|
|
8625
|
+
)) })
|
|
8626
|
+
] })
|
|
8627
|
+
] }),
|
|
8628
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: "overflow-hidden border-[#e5e7eb] p-0", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse", children: [
|
|
8629
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { style: { backgroundColor: TABLE_HEADER_BG }, children: tableColumns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8630
|
+
"th",
|
|
8631
|
+
{
|
|
8632
|
+
scope: "col",
|
|
8633
|
+
className: cn(
|
|
8634
|
+
"px-6 py-4 font-semibold text-xs uppercase tracking-wider",
|
|
8635
|
+
ALIGN_CLASS[col.align ?? "left"]
|
|
8636
|
+
),
|
|
8637
|
+
style: { color: TABLE_HEADER_TEXT },
|
|
8638
|
+
children: col.label
|
|
8639
|
+
},
|
|
8640
|
+
col.key
|
|
8641
|
+
)) }) }),
|
|
8642
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: tableRows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8643
|
+
"tr",
|
|
8644
|
+
{
|
|
8645
|
+
style: {
|
|
8646
|
+
borderTop: rowIndex === 0 ? void 0 : `1px solid ${TABLE_ROW_BORDER}`
|
|
8647
|
+
},
|
|
8648
|
+
children: tableColumns.map((col, colIndex) => {
|
|
8649
|
+
const align = col.align ?? (colIndex === 0 ? "left" : "right");
|
|
8650
|
+
if (colIndex === 0) {
|
|
8651
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8652
|
+
"td",
|
|
8653
|
+
{
|
|
8654
|
+
className: cn(
|
|
8655
|
+
"px-6 py-5 font-bold text-sm",
|
|
8656
|
+
ALIGN_CLASS[align]
|
|
8657
|
+
),
|
|
8658
|
+
style: { color: FIRST_COL_TEXT },
|
|
8659
|
+
children: row.label
|
|
8660
|
+
},
|
|
8661
|
+
col.key
|
|
8662
|
+
);
|
|
8663
|
+
}
|
|
8664
|
+
if (colIndex === 1) {
|
|
8665
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8666
|
+
"td",
|
|
8667
|
+
{
|
|
8668
|
+
className: cn(
|
|
8669
|
+
"px-6 py-5 text-sm text-slate-700 tabular-nums",
|
|
8670
|
+
ALIGN_CLASS[align]
|
|
8671
|
+
),
|
|
8672
|
+
children: row.value
|
|
8673
|
+
},
|
|
8674
|
+
col.key
|
|
8675
|
+
);
|
|
8676
|
+
}
|
|
8677
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8678
|
+
"td",
|
|
8679
|
+
{
|
|
8680
|
+
className: cn("px-6 py-5", ALIGN_CLASS[align]),
|
|
8681
|
+
children: row.delta ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8682
|
+
DeltaPill,
|
|
8683
|
+
{
|
|
8684
|
+
value: row.delta.text,
|
|
8685
|
+
direction: deltaDirection(row.delta.direction),
|
|
8686
|
+
hideArrow: true,
|
|
8687
|
+
className: "bg-transparent px-0 py-0 font-semibold text-xs"
|
|
8688
|
+
}
|
|
8689
|
+
) : null
|
|
8690
|
+
},
|
|
8691
|
+
col.key
|
|
8692
|
+
);
|
|
8693
|
+
})
|
|
8694
|
+
},
|
|
8695
|
+
`${row.label}-${rowIndex}`
|
|
8696
|
+
)) })
|
|
8697
|
+
] }) }),
|
|
8698
|
+
summaryText && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8699
|
+
"p",
|
|
8700
|
+
{
|
|
8701
|
+
className: "text-sm leading-relaxed",
|
|
8702
|
+
style: { color: SUMMARY_TEXT_COLOR },
|
|
8703
|
+
children: renderRich(summaryText)
|
|
8704
|
+
}
|
|
8705
|
+
)
|
|
8706
|
+
] });
|
|
8707
|
+
}
|
|
8708
|
+
function bandPath(top, bottom) {
|
|
8709
|
+
if (top.length === 0 || bottom.length === 0) return "";
|
|
8710
|
+
const fmt2 = (n) => Number.isInteger(n) ? String(n) : String(Math.round(n * 1e3) / 1e3);
|
|
8711
|
+
const forward = top.map((p, i) => `${i === 0 ? "M" : "L"} ${fmt2(p.x)} ${fmt2(p.y)}`).join(" ");
|
|
8712
|
+
const back = [...bottom].reverse().map((p) => `L ${fmt2(p.x)} ${fmt2(p.y)}`).join(" ");
|
|
8713
|
+
return `${forward} ${back} Z`;
|
|
8714
|
+
}
|
|
8715
|
+
var DEFAULT_ICON_CHIP_BG = "#F1F5F9";
|
|
8716
|
+
var DEFAULT_ICON_CHIP_FG = "#334155";
|
|
8717
|
+
var DEFAULT_VALUE_COLOR2 = "#0F172A";
|
|
8718
|
+
var DEFAULT_SPARKLINE_COLOR = "#10B981";
|
|
8719
|
+
function KpiCardWithSparklines({
|
|
8720
|
+
label,
|
|
8721
|
+
value,
|
|
8722
|
+
delta,
|
|
8723
|
+
caption,
|
|
8724
|
+
icon,
|
|
8725
|
+
iconChipBg,
|
|
8726
|
+
iconChipFg,
|
|
8727
|
+
sparkline,
|
|
8728
|
+
accentColor,
|
|
8729
|
+
valueColor,
|
|
8730
|
+
className,
|
|
8731
|
+
onClick
|
|
8732
|
+
}) {
|
|
8733
|
+
const sparklineColor = sparkline?.color ?? accentColor ?? DEFAULT_SPARKLINE_COLOR;
|
|
8734
|
+
const sparkData = sparkline?.data ?? [];
|
|
8735
|
+
const hasSparkline = sparkData.length >= 2;
|
|
8736
|
+
const interactive = typeof onClick === "function";
|
|
8737
|
+
const body = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
|
|
8738
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
8739
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-xs text-slate-500 leading-snug", children: label }),
|
|
8740
|
+
icon != null && icon !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8741
|
+
"span",
|
|
8742
|
+
{
|
|
8743
|
+
"aria-hidden": "true",
|
|
8744
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg font-semibold text-base",
|
|
8745
|
+
style: {
|
|
8746
|
+
backgroundColor: iconChipBg ?? DEFAULT_ICON_CHIP_BG,
|
|
8747
|
+
color: iconChipFg ?? DEFAULT_ICON_CHIP_FG
|
|
8748
|
+
},
|
|
8749
|
+
children: icon
|
|
8750
|
+
}
|
|
8751
|
+
)
|
|
8752
|
+
] }),
|
|
8753
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-wrap items-end gap-x-3 gap-y-1", children: [
|
|
8754
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8755
|
+
"span",
|
|
8756
|
+
{
|
|
8757
|
+
className: "font-extrabold text-2xl leading-none tracking-tight tabular-nums",
|
|
8758
|
+
style: { color: valueColor ?? DEFAULT_VALUE_COLOR2 },
|
|
8759
|
+
children: value
|
|
8760
|
+
}
|
|
8761
|
+
),
|
|
8762
|
+
delta && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8763
|
+
DeltaPill,
|
|
8764
|
+
{
|
|
8765
|
+
value: delta.value,
|
|
8766
|
+
direction: delta.direction,
|
|
8767
|
+
tone: delta.tone,
|
|
8768
|
+
className: "mb-1 px-2.5 py-1 text-xs"
|
|
8769
|
+
}
|
|
8770
|
+
)
|
|
8771
|
+
] }),
|
|
8772
|
+
caption && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-slate-400 text-xs", children: caption }),
|
|
8773
|
+
hasSparkline && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-6 -mb-6 mt-5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8774
|
+
Sparkline,
|
|
8775
|
+
{
|
|
8776
|
+
data: sparkData,
|
|
8777
|
+
color: sparklineColor,
|
|
8778
|
+
showArea: sparkline?.showArea ?? true,
|
|
8779
|
+
smooth: sparkline?.smooth ?? false,
|
|
8780
|
+
className: "h-24 w-full"
|
|
8781
|
+
}
|
|
8782
|
+
) })
|
|
8783
|
+
] });
|
|
8784
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8785
|
+
CardShell,
|
|
8786
|
+
{
|
|
8787
|
+
className: cn(
|
|
8788
|
+
"overflow-hidden",
|
|
8789
|
+
interactive && "cursor-pointer transition-shadow hover:shadow-md",
|
|
8790
|
+
className
|
|
8791
|
+
),
|
|
8792
|
+
children: interactive ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8793
|
+
"div",
|
|
8794
|
+
{
|
|
8795
|
+
role: "button",
|
|
8796
|
+
tabIndex: 0,
|
|
8797
|
+
onClick,
|
|
8798
|
+
onKeyDown: (event) => {
|
|
8799
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
8800
|
+
event.preventDefault();
|
|
8801
|
+
onClick?.();
|
|
8802
|
+
}
|
|
8803
|
+
},
|
|
8804
|
+
className: "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
8805
|
+
children: body
|
|
8806
|
+
}
|
|
8807
|
+
) : body
|
|
8808
|
+
}
|
|
8809
|
+
);
|
|
8810
|
+
}
|
|
8811
|
+
var DEFAULT_LOCATION_LABEL = "Location";
|
|
8812
|
+
var DEFAULT_REVENUE_LABEL = "Rev";
|
|
8813
|
+
var DEFAULT_STATUS_LABEL = "Status";
|
|
8814
|
+
var GRID_TEMPLATE = "grid grid-cols-[1fr_auto_3rem] items-center gap-x-4 px-6";
|
|
8815
|
+
function LocationTableRow({ row }) {
|
|
8816
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(GRID_TEMPLATE, "py-5"), role: "row", children: [
|
|
8817
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8818
|
+
"span",
|
|
8819
|
+
{
|
|
8820
|
+
role: "cell",
|
|
8821
|
+
className: "truncate font-semibold text-sm text-slate-800",
|
|
8822
|
+
children: row.name
|
|
8823
|
+
}
|
|
8824
|
+
),
|
|
8825
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8826
|
+
"span",
|
|
8827
|
+
{
|
|
8828
|
+
role: "cell",
|
|
8829
|
+
className: "text-right text-sm text-slate-700 tabular-nums",
|
|
8830
|
+
children: row.revenue
|
|
8831
|
+
}
|
|
8832
|
+
),
|
|
8833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "cell", className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8834
|
+
StatusDot,
|
|
8835
|
+
{
|
|
8836
|
+
status: row.status,
|
|
8837
|
+
color: row.statusColor,
|
|
8838
|
+
label: row.statusLabel,
|
|
8839
|
+
size: 13
|
|
8840
|
+
}
|
|
8841
|
+
) })
|
|
8842
|
+
] });
|
|
8843
|
+
}
|
|
8844
|
+
function LocationsRevenueCard({
|
|
8845
|
+
locationColumnLabel = DEFAULT_LOCATION_LABEL,
|
|
8846
|
+
revenueColumnLabel = DEFAULT_REVENUE_LABEL,
|
|
8847
|
+
statusColumnLabel = DEFAULT_STATUS_LABEL,
|
|
8848
|
+
rows,
|
|
8849
|
+
className
|
|
8850
|
+
}) {
|
|
8851
|
+
const hasRows = Array.isArray(rows) && rows.length > 0;
|
|
8852
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "table", "aria-label": locationColumnLabel, children: [
|
|
8853
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
8854
|
+
"div",
|
|
8855
|
+
{
|
|
8856
|
+
role: "row",
|
|
8857
|
+
className: cn(
|
|
8858
|
+
GRID_TEMPLATE,
|
|
8859
|
+
"border-b bg-slate-50 py-4 font-semibold text-xs text-slate-400 uppercase tracking-wider"
|
|
8860
|
+
),
|
|
8861
|
+
children: [
|
|
8862
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "columnheader", className: "text-left", children: locationColumnLabel }),
|
|
8863
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "columnheader", className: "text-right", children: revenueColumnLabel }),
|
|
8864
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "columnheader", className: "text-right", children: statusColumnLabel })
|
|
8865
|
+
]
|
|
8866
|
+
}
|
|
8867
|
+
),
|
|
8868
|
+
hasRows ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: rows.map((row, index) => /* @__PURE__ */ jsxRuntime.jsx(LocationTableRow, { row }, row.id ?? `${row.name}-${index}`)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-8 text-center text-slate-400 text-sm", children: "No locations" })
|
|
8869
|
+
] }) });
|
|
8870
|
+
}
|
|
8871
|
+
var DENSITY_CELL_PADDING = {
|
|
8872
|
+
comfortable: "px-6 py-4",
|
|
8873
|
+
compact: "px-6 py-3"
|
|
8874
|
+
};
|
|
8875
|
+
function RowBasedDataList({
|
|
8876
|
+
rows,
|
|
8877
|
+
title,
|
|
8878
|
+
labelColumnWidth = "30%",
|
|
8879
|
+
density = "comfortable",
|
|
8880
|
+
className
|
|
8881
|
+
}) {
|
|
8882
|
+
const cellPadding = DENSITY_CELL_PADDING[density] ?? DENSITY_CELL_PADDING.comfortable;
|
|
8883
|
+
const hasRows = Array.isArray(rows) && rows.length > 0;
|
|
8884
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full", className), children: [
|
|
8885
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 font-medium text-slate-700 text-sm", children: title }),
|
|
8886
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardShell, { className: "overflow-hidden rounded-xl border-slate-200 shadow-none", children: hasRows ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8887
|
+
"dl",
|
|
8888
|
+
{
|
|
8889
|
+
className: "grid",
|
|
8890
|
+
style: { gridTemplateColumns: `${labelColumnWidth} 1fr` },
|
|
8891
|
+
children: rows.map((row, index) => {
|
|
8892
|
+
const isLast = index === rows.length - 1;
|
|
8893
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8894
|
+
RowPair,
|
|
8895
|
+
{
|
|
8896
|
+
row,
|
|
8897
|
+
isLast,
|
|
8898
|
+
cellPadding
|
|
8899
|
+
},
|
|
8900
|
+
`${row.label}-${index}`
|
|
8901
|
+
);
|
|
8902
|
+
})
|
|
8903
|
+
}
|
|
8904
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-6 py-5 text-slate-400 text-sm", children: "No details available" }) })
|
|
8905
|
+
] });
|
|
8906
|
+
}
|
|
8907
|
+
function RowPair({ row, isLast, cellPadding }) {
|
|
8908
|
+
const valueColorClass = toneTextColor(row.valueColor);
|
|
8909
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8910
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8911
|
+
"dt",
|
|
8912
|
+
{
|
|
8913
|
+
className: cn(
|
|
8914
|
+
"min-w-0 break-words bg-slate-50/70 font-normal text-slate-500 text-sm leading-snug",
|
|
8915
|
+
cellPadding,
|
|
8916
|
+
!isLast && "border-slate-100 border-b"
|
|
8917
|
+
),
|
|
8918
|
+
children: row.label
|
|
8919
|
+
}
|
|
8920
|
+
),
|
|
8921
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8922
|
+
"dd",
|
|
8923
|
+
{
|
|
8924
|
+
className: cn(
|
|
8925
|
+
"min-w-0 break-words border-slate-100 border-l font-medium text-sm leading-snug",
|
|
8926
|
+
valueColorClass,
|
|
8927
|
+
cellPadding,
|
|
8928
|
+
!isLast && "border-b"
|
|
8929
|
+
),
|
|
8930
|
+
children: row.href ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8931
|
+
"a",
|
|
8932
|
+
{
|
|
8933
|
+
href: row.href,
|
|
8934
|
+
target: "_blank",
|
|
8935
|
+
rel: "noreferrer noopener",
|
|
8936
|
+
className: "underline decoration-slate-300 underline-offset-2 hover:decoration-current",
|
|
8937
|
+
children: row.value
|
|
8938
|
+
}
|
|
8939
|
+
) : row.value
|
|
8940
|
+
}
|
|
8941
|
+
)
|
|
8942
|
+
] });
|
|
8943
|
+
}
|
|
8944
|
+
var TONE_STYLES = {
|
|
8945
|
+
green: { bg: "#E7F6EC", text: "#16A34A" },
|
|
8946
|
+
blue: { bg: "#E8EDFB", text: "#3B5BDB" },
|
|
8947
|
+
amber: { bg: "#FBEFD3", text: "#B45309" },
|
|
8948
|
+
red: { bg: "#FCE4E7", text: "#DC2626" },
|
|
8949
|
+
gray: { bg: "#F4F5F7", text: "#374151" },
|
|
8950
|
+
neutral: { bg: "#F4F5F7", text: "#374151" }
|
|
8951
|
+
};
|
|
8952
|
+
var TAG_BG = "#F4F5F7";
|
|
8953
|
+
var TAG_BORDER = "#E5E7EB";
|
|
8954
|
+
var TAG_TEXT = "#374151";
|
|
8955
|
+
var DEFAULT_TONE = "neutral";
|
|
8956
|
+
function tintFromColor(color) {
|
|
8957
|
+
const hex = /^#([0-9a-fA-F]{6})$/.exec(color);
|
|
8958
|
+
if (!hex) return color;
|
|
8959
|
+
return `#${hex[1]}24`;
|
|
8960
|
+
}
|
|
8961
|
+
function resolveSolidStyle(badge) {
|
|
8962
|
+
if (badge.color) {
|
|
8963
|
+
return { bg: tintFromColor(badge.color), text: badge.color };
|
|
8964
|
+
}
|
|
8965
|
+
const tone = badge.tone ?? DEFAULT_TONE;
|
|
8966
|
+
return TONE_STYLES[tone] ?? TONE_STYLES[DEFAULT_TONE];
|
|
8967
|
+
}
|
|
8968
|
+
function resolveDotColor(badge) {
|
|
8969
|
+
if (badge.dotColor) return badge.dotColor;
|
|
8970
|
+
if (badge.color) return badge.color;
|
|
8971
|
+
const tone = badge.tone ?? DEFAULT_TONE;
|
|
8972
|
+
return TONE_STYLES[tone]?.text ?? TONE_STYLES[DEFAULT_TONE].text;
|
|
8973
|
+
}
|
|
8974
|
+
function BadgePill({ badge, groupStyle }) {
|
|
8975
|
+
const style = badge.style ?? groupStyle;
|
|
8976
|
+
const baseClass = "inline-flex items-center rounded-full px-4 py-1.5 font-medium text-xs whitespace-nowrap";
|
|
8977
|
+
if (style === "outline") {
|
|
8978
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8979
|
+
"span",
|
|
8980
|
+
{
|
|
8981
|
+
className: cn(baseClass, "border"),
|
|
8982
|
+
style: {
|
|
8983
|
+
backgroundColor: TAG_BG,
|
|
8984
|
+
borderColor: TAG_BORDER,
|
|
8985
|
+
color: TAG_TEXT
|
|
8986
|
+
},
|
|
8987
|
+
children: [
|
|
8988
|
+
badge.dot && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8989
|
+
"span",
|
|
8990
|
+
{
|
|
8991
|
+
"aria-hidden": "true",
|
|
8992
|
+
className: "mr-2 inline-block size-2 shrink-0 rounded-full",
|
|
8993
|
+
style: { backgroundColor: resolveDotColor(badge) }
|
|
8994
|
+
}
|
|
8995
|
+
),
|
|
8996
|
+
badge.label
|
|
8997
|
+
]
|
|
8998
|
+
}
|
|
8999
|
+
);
|
|
9000
|
+
}
|
|
9001
|
+
const solid = resolveSolidStyle(badge);
|
|
9002
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9003
|
+
"span",
|
|
9004
|
+
{
|
|
9005
|
+
className: baseClass,
|
|
9006
|
+
style: { backgroundColor: solid.bg, color: solid.text },
|
|
9007
|
+
children: [
|
|
9008
|
+
badge.dot && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9009
|
+
"span",
|
|
9010
|
+
{
|
|
9011
|
+
"aria-hidden": "true",
|
|
9012
|
+
className: "mr-2 inline-block size-2 shrink-0 rounded-full",
|
|
9013
|
+
style: { backgroundColor: resolveDotColor(badge) }
|
|
9014
|
+
}
|
|
9015
|
+
),
|
|
9016
|
+
badge.label
|
|
9017
|
+
]
|
|
9018
|
+
}
|
|
9019
|
+
);
|
|
9020
|
+
}
|
|
9021
|
+
function BadgeGroupBlock({ group }) {
|
|
9022
|
+
const badges = group.badges ?? [];
|
|
9023
|
+
if (badges.length === 0) return null;
|
|
9024
|
+
const groupStyle = group.variant === "tag" ? "outline" : "solid";
|
|
9025
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
9026
|
+
group.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-2 block font-medium text-[11px] text-gray-400 uppercase tracking-wider", children: group.label }),
|
|
9027
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-3", children: badges.map((badge, i) => {
|
|
9028
|
+
if (!badge?.label) return null;
|
|
9029
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9030
|
+
BadgePill,
|
|
9031
|
+
{
|
|
9032
|
+
badge,
|
|
9033
|
+
groupStyle
|
|
9034
|
+
},
|
|
9035
|
+
`${badge.label}-${i}`
|
|
9036
|
+
);
|
|
9037
|
+
}) })
|
|
9038
|
+
] });
|
|
9039
|
+
}
|
|
9040
|
+
function BadgesArtifact({ groups, className }) {
|
|
9041
|
+
const safeGroups = groups ?? [];
|
|
9042
|
+
if (safeGroups.length === 0) return null;
|
|
9043
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-6", className), children: safeGroups.map((group, i) => /* @__PURE__ */ jsxRuntime.jsx(BadgeGroupBlock, { group }, group.label ?? `group-${i}`)) });
|
|
9044
|
+
}
|
|
9045
|
+
var BADGE_TONE_CLASSES = {
|
|
9046
|
+
default: "bg-slate-100 text-slate-600",
|
|
9047
|
+
positive: "bg-emerald-50 text-emerald-700",
|
|
9048
|
+
negative: "bg-rose-50 text-rose-700",
|
|
9049
|
+
muted: "bg-slate-50 text-slate-400",
|
|
9050
|
+
info: "bg-blue-50 text-blue-700"
|
|
9051
|
+
};
|
|
9052
|
+
function badgeToneClass(tone) {
|
|
9053
|
+
if (!tone) return BADGE_TONE_CLASSES.default;
|
|
9054
|
+
return BADGE_TONE_CLASSES[tone] ?? BADGE_TONE_CLASSES.default;
|
|
9055
|
+
}
|
|
9056
|
+
function OrderedListArtifact({
|
|
9057
|
+
title,
|
|
9058
|
+
intro,
|
|
9059
|
+
items,
|
|
9060
|
+
startIndex = 1,
|
|
9061
|
+
markerStyle = "number",
|
|
9062
|
+
className
|
|
9063
|
+
}) {
|
|
9064
|
+
const safeItems = Array.isArray(items) ? items : [];
|
|
9065
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: cn("text-left", className), children: [
|
|
9066
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-bold text-slate-900 text-base leading-snug", children: title }) : null,
|
|
9067
|
+
intro ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9068
|
+
"p",
|
|
9069
|
+
{
|
|
9070
|
+
className: cn(
|
|
9071
|
+
"text-slate-500 text-sm leading-relaxed",
|
|
9072
|
+
title && "mt-4"
|
|
9073
|
+
),
|
|
9074
|
+
children: renderRich(intro)
|
|
9075
|
+
}
|
|
9076
|
+
) : null,
|
|
9077
|
+
safeItems.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9078
|
+
"ol",
|
|
9079
|
+
{
|
|
9080
|
+
className: cn(
|
|
9081
|
+
"flex list-none flex-col gap-3.5",
|
|
9082
|
+
(title || intro) && "mt-5"
|
|
9083
|
+
),
|
|
9084
|
+
children: safeItems.map((item, index) => {
|
|
9085
|
+
const marker = item.marker ?? formatMarker(index, markerStyle, startIndex);
|
|
9086
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9087
|
+
"li",
|
|
9088
|
+
{
|
|
9089
|
+
className: "flex items-start gap-3.5",
|
|
9090
|
+
children: [
|
|
9091
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9092
|
+
"span",
|
|
9093
|
+
{
|
|
9094
|
+
className: cn(
|
|
9095
|
+
"flex h-7 w-7 shrink-0 items-center justify-center rounded-md font-medium text-sm tabular-nums",
|
|
9096
|
+
badgeToneClass(item.tone)
|
|
9097
|
+
),
|
|
9098
|
+
children: marker
|
|
9099
|
+
}
|
|
9100
|
+
),
|
|
9101
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 pt-0.5 text-slate-900 text-sm leading-relaxed", children: renderRich(item.content) })
|
|
9102
|
+
]
|
|
9103
|
+
},
|
|
9104
|
+
index
|
|
9105
|
+
);
|
|
9106
|
+
})
|
|
9107
|
+
}
|
|
9108
|
+
) : null
|
|
9109
|
+
] });
|
|
9110
|
+
}
|
|
9111
|
+
var COMPLETE_BG = "#E6F4EC";
|
|
9112
|
+
var COMPLETE_ACCENT = "#16A34A";
|
|
9113
|
+
var ACTIVE_COLOR = "#F59E0B";
|
|
9114
|
+
var PENDING_STROKE = "#CBD5E1";
|
|
9115
|
+
var ERROR_BG = "#FEE2E2";
|
|
9116
|
+
var ERROR_COLOR = "#DC2626";
|
|
9117
|
+
var SPIN_CLASS = "ae-spin";
|
|
9118
|
+
var SPIN_STYLE_ID = "ae-spin-keyframes";
|
|
9119
|
+
function ensureSpinKeyframes() {
|
|
9120
|
+
if (typeof document === "undefined") return;
|
|
9121
|
+
if (document.getElementById(SPIN_STYLE_ID)) return;
|
|
9122
|
+
const style = document.createElement("style");
|
|
9123
|
+
style.id = SPIN_STYLE_ID;
|
|
9124
|
+
style.textContent = `@keyframes ${SPIN_CLASS}{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: no-preference){.${SPIN_CLASS}{animation:${SPIN_CLASS} 0.8s linear infinite;transform-origin:center}}`;
|
|
9125
|
+
document.head.appendChild(style);
|
|
9126
|
+
}
|
|
9127
|
+
function resolveSize(size) {
|
|
9128
|
+
if (typeof size === "number") return size;
|
|
9129
|
+
switch (size) {
|
|
9130
|
+
case "sm":
|
|
9131
|
+
return 24;
|
|
9132
|
+
case "lg":
|
|
9133
|
+
return 48;
|
|
9134
|
+
case "md":
|
|
9135
|
+
default:
|
|
9136
|
+
return 34;
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
function normalizeStatus(status) {
|
|
9140
|
+
switch (status) {
|
|
9141
|
+
case "completed":
|
|
9142
|
+
return "complete";
|
|
9143
|
+
case "in_progress":
|
|
9144
|
+
return "active";
|
|
9145
|
+
case "active":
|
|
9146
|
+
case "pending":
|
|
9147
|
+
case "error":
|
|
9148
|
+
case "complete":
|
|
9149
|
+
return status;
|
|
9150
|
+
default:
|
|
9151
|
+
return "complete";
|
|
9152
|
+
}
|
|
9153
|
+
}
|
|
9154
|
+
function CheckIcon8({
|
|
9155
|
+
color = COMPLETE_ACCENT,
|
|
9156
|
+
className,
|
|
9157
|
+
style,
|
|
9158
|
+
strokeWidth = 2.25
|
|
9159
|
+
}) {
|
|
9160
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9161
|
+
"svg",
|
|
9162
|
+
{
|
|
9163
|
+
viewBox: "0 0 24 24",
|
|
9164
|
+
fill: "none",
|
|
9165
|
+
stroke: color,
|
|
9166
|
+
strokeWidth,
|
|
9167
|
+
strokeLinecap: "round",
|
|
9168
|
+
strokeLinejoin: "round",
|
|
9169
|
+
"aria-hidden": "true",
|
|
9170
|
+
className,
|
|
9171
|
+
style,
|
|
9172
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12l4 4 8-9" })
|
|
9173
|
+
}
|
|
9174
|
+
);
|
|
9175
|
+
}
|
|
9176
|
+
function ClockIcon2({
|
|
9177
|
+
color = "#64748B",
|
|
9178
|
+
className,
|
|
9179
|
+
strokeWidth = 1.75
|
|
9180
|
+
}) {
|
|
9181
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9182
|
+
"svg",
|
|
9183
|
+
{
|
|
9184
|
+
viewBox: "0 0 24 24",
|
|
9185
|
+
fill: "none",
|
|
9186
|
+
stroke: color,
|
|
9187
|
+
strokeWidth,
|
|
9188
|
+
strokeLinecap: "round",
|
|
9189
|
+
strokeLinejoin: "round",
|
|
9190
|
+
"aria-hidden": "true",
|
|
9191
|
+
className,
|
|
9192
|
+
children: [
|
|
9193
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
9194
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
|
|
9195
|
+
]
|
|
9196
|
+
}
|
|
9197
|
+
);
|
|
9198
|
+
}
|
|
9199
|
+
function XIcon7({
|
|
9200
|
+
color = ERROR_COLOR,
|
|
9201
|
+
className,
|
|
9202
|
+
style
|
|
9203
|
+
}) {
|
|
9204
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9205
|
+
"svg",
|
|
9206
|
+
{
|
|
9207
|
+
viewBox: "0 0 24 24",
|
|
9208
|
+
fill: "none",
|
|
9209
|
+
stroke: color,
|
|
9210
|
+
strokeWidth: 2.25,
|
|
9211
|
+
strokeLinecap: "round",
|
|
9212
|
+
strokeLinejoin: "round",
|
|
9213
|
+
"aria-hidden": "true",
|
|
9214
|
+
className,
|
|
9215
|
+
style,
|
|
9216
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 7l10 10M17 7L7 17" })
|
|
9217
|
+
}
|
|
9218
|
+
);
|
|
9219
|
+
}
|
|
9220
|
+
function StatusNode({ status, size, accentColor, className }) {
|
|
9221
|
+
const resolved = normalizeStatus(status);
|
|
9222
|
+
const px = resolveSize(size);
|
|
9223
|
+
const accent = accentColor ?? COMPLETE_ACCENT;
|
|
9224
|
+
if (resolved === "active") ensureSpinKeyframes();
|
|
9225
|
+
const base = cn(
|
|
9226
|
+
"inline-flex shrink-0 items-center justify-center rounded-full",
|
|
9227
|
+
className
|
|
9228
|
+
);
|
|
9229
|
+
const dimensions = { width: px, height: px };
|
|
9230
|
+
const glyphPx = Math.round(px * 0.5);
|
|
9231
|
+
if (resolved === "complete") {
|
|
9232
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9233
|
+
"span",
|
|
9234
|
+
{
|
|
9235
|
+
className: base,
|
|
9236
|
+
style: { ...dimensions, backgroundColor: COMPLETE_BG },
|
|
9237
|
+
"data-status": "complete",
|
|
9238
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon8, { color: accent, className: "block", style: { width: glyphPx, height: glyphPx } })
|
|
9239
|
+
}
|
|
9240
|
+
);
|
|
9241
|
+
}
|
|
9242
|
+
if (resolved === "error") {
|
|
9243
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9244
|
+
"span",
|
|
9245
|
+
{
|
|
9246
|
+
className: base,
|
|
9247
|
+
style: { ...dimensions, backgroundColor: ERROR_BG },
|
|
9248
|
+
"data-status": "error",
|
|
9249
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(XIcon7, { color: ERROR_COLOR, className: "block", style: { width: glyphPx, height: glyphPx } })
|
|
9250
|
+
}
|
|
9251
|
+
);
|
|
9252
|
+
}
|
|
9253
|
+
if (resolved === "active") {
|
|
9254
|
+
const ringColor = accentColor ?? ACTIVE_COLOR;
|
|
9255
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9256
|
+
"span",
|
|
9257
|
+
{
|
|
9258
|
+
className: base,
|
|
9259
|
+
style: dimensions,
|
|
9260
|
+
"data-status": "active",
|
|
9261
|
+
role: "status",
|
|
9262
|
+
"aria-label": "In progress",
|
|
9263
|
+
"aria-live": "polite",
|
|
9264
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(SPIN_CLASS, "block"), style: { width: px, height: px }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", className: "block size-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9265
|
+
"circle",
|
|
9266
|
+
{
|
|
9267
|
+
cx: "12",
|
|
9268
|
+
cy: "12",
|
|
9269
|
+
r: "10",
|
|
9270
|
+
fill: "none",
|
|
9271
|
+
stroke: ringColor,
|
|
9272
|
+
strokeWidth: "2.5",
|
|
9273
|
+
strokeLinecap: "round",
|
|
9274
|
+
strokeDasharray: "47 16"
|
|
9275
|
+
}
|
|
9276
|
+
) }) })
|
|
9277
|
+
}
|
|
9278
|
+
);
|
|
9279
|
+
}
|
|
9280
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: base, style: dimensions, "data-status": "pending", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", className: "block size-full", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", fill: "none", stroke: PENDING_STROKE, strokeWidth: "1.75" }) }) });
|
|
9281
|
+
}
|
|
9282
|
+
var DEFAULT_TASK_LABEL = "RESEARCH TASK";
|
|
9283
|
+
var DEFAULT_FINDINGS_LABEL = "EMERGING FINDINGS";
|
|
9284
|
+
function stepLabelClass(status) {
|
|
9285
|
+
switch (status) {
|
|
9286
|
+
case "completed":
|
|
9287
|
+
return "font-normal text-slate-700";
|
|
9288
|
+
case "in_progress":
|
|
9289
|
+
return "font-bold text-slate-900";
|
|
9290
|
+
case "pending":
|
|
9291
|
+
default:
|
|
9292
|
+
return "font-normal text-slate-400";
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
function hasRichContent(findings) {
|
|
9296
|
+
if (findings == null) return false;
|
|
9297
|
+
if (typeof findings === "string") return findings.trim().length > 0;
|
|
9298
|
+
return findings.some((segment) => segment.text.trim().length > 0);
|
|
9299
|
+
}
|
|
9300
|
+
function DeepResearchProgress({
|
|
9301
|
+
taskLabel = DEFAULT_TASK_LABEL,
|
|
9302
|
+
taskDescription,
|
|
9303
|
+
steps,
|
|
9304
|
+
findingsLabel = DEFAULT_FINDINGS_LABEL,
|
|
9305
|
+
findings,
|
|
9306
|
+
showFindings,
|
|
9307
|
+
className
|
|
9308
|
+
}) {
|
|
9309
|
+
const safeSteps = Array.isArray(steps) ? steps : [];
|
|
9310
|
+
const findingsHasContent = hasRichContent(findings);
|
|
9311
|
+
const findingsVisible = findingsHasContent && showFindings !== false;
|
|
9312
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex w-full flex-col gap-6 text-left", className), children: [
|
|
9313
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: "rounded-xl border-slate-200/80 bg-slate-50/70 px-5 py-4 shadow-none", children: [
|
|
9314
|
+
taskLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-[11px] text-slate-400 uppercase tracking-wider", children: taskLabel }) : null,
|
|
9315
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-slate-700 leading-relaxed", children: taskDescription })
|
|
9316
|
+
] }),
|
|
9317
|
+
safeSteps.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "flex list-none flex-col gap-5", children: safeSteps.map((step, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9318
|
+
"li",
|
|
9319
|
+
{
|
|
9320
|
+
className: "flex items-center gap-4",
|
|
9321
|
+
"data-status": step.status,
|
|
9322
|
+
children: [
|
|
9323
|
+
/* @__PURE__ */ jsxRuntime.jsx(StatusNode, { status: step.status, size: "md" }),
|
|
9324
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9325
|
+
"span",
|
|
9326
|
+
{
|
|
9327
|
+
className: cn(
|
|
9328
|
+
"text-sm leading-snug",
|
|
9329
|
+
stepLabelClass(step.status)
|
|
9330
|
+
),
|
|
9331
|
+
children: step.label
|
|
9332
|
+
}
|
|
9333
|
+
)
|
|
9334
|
+
]
|
|
9335
|
+
},
|
|
9336
|
+
step.id ?? index
|
|
9337
|
+
)) }) : null,
|
|
9338
|
+
findingsVisible ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
9339
|
+
/* @__PURE__ */ jsxRuntime.jsx("hr", { className: "border-slate-200 border-t" }),
|
|
9340
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
9341
|
+
findingsLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-[11px] text-slate-400 uppercase tracking-wider", children: findingsLabel }) : null,
|
|
9342
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm text-slate-700 leading-relaxed [&_strong]:text-slate-900", children: renderRich(findings) })
|
|
9343
|
+
] })
|
|
9344
|
+
] }) : null
|
|
9345
|
+
] });
|
|
9346
|
+
}
|
|
9347
|
+
var NODE_SIZE = 48;
|
|
9348
|
+
var NODE_COLUMN_WIDTH = NODE_SIZE;
|
|
9349
|
+
var DEFAULT_ACCENT = "#16A34A";
|
|
9350
|
+
var FOOTER_SEPARATOR = " \xB7 ";
|
|
9351
|
+
function resolveFooterItems(footer) {
|
|
9352
|
+
if (!footer) return [];
|
|
9353
|
+
if (Array.isArray(footer.items) && footer.items.length > 0) {
|
|
9354
|
+
return footer.items.filter((item) => item != null && item !== "");
|
|
7702
9355
|
}
|
|
7703
|
-
|
|
7704
|
-
|
|
9356
|
+
const parts = [];
|
|
9357
|
+
if (footer.thinkingLabel) parts.push(footer.thinkingLabel);
|
|
9358
|
+
if (footer.sourcesLabel) parts.push(footer.sourcesLabel);
|
|
9359
|
+
return parts;
|
|
9360
|
+
}
|
|
9361
|
+
function TimelineStep({
|
|
9362
|
+
step,
|
|
9363
|
+
isLast,
|
|
9364
|
+
accentColor
|
|
9365
|
+
}) {
|
|
9366
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "relative flex gap-4", "data-status": step.status ?? "complete", children: [
|
|
9367
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9368
|
+
"div",
|
|
9369
|
+
{
|
|
9370
|
+
className: "relative flex shrink-0 justify-center",
|
|
9371
|
+
style: { width: NODE_COLUMN_WIDTH },
|
|
9372
|
+
children: [
|
|
9373
|
+
!isLast && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9374
|
+
"span",
|
|
9375
|
+
{
|
|
9376
|
+
"aria-hidden": "true",
|
|
9377
|
+
className: "absolute top-1/2 bottom-0 w-0.5 bg-slate-200",
|
|
9378
|
+
style: { top: NODE_SIZE / 2, height: `calc(100% - ${NODE_SIZE / 2}px)` }
|
|
9379
|
+
}
|
|
9380
|
+
),
|
|
9381
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9382
|
+
StatusNode,
|
|
9383
|
+
{
|
|
9384
|
+
status: step.status ?? "complete",
|
|
9385
|
+
size: NODE_SIZE,
|
|
9386
|
+
accentColor,
|
|
9387
|
+
className: "relative z-10"
|
|
9388
|
+
}
|
|
9389
|
+
)
|
|
9390
|
+
]
|
|
9391
|
+
}
|
|
9392
|
+
),
|
|
9393
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-1.5 pt-1.5 pb-8", children: [
|
|
9394
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-sm text-slate-900 leading-snug", children: step.title }),
|
|
9395
|
+
step.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500 leading-snug", children: step.description }) : null
|
|
9396
|
+
] })
|
|
9397
|
+
] });
|
|
9398
|
+
}
|
|
9399
|
+
function Tracker({
|
|
9400
|
+
steps,
|
|
9401
|
+
footer,
|
|
9402
|
+
showFooterIcon = true,
|
|
9403
|
+
accentColor = DEFAULT_ACCENT,
|
|
9404
|
+
className
|
|
9405
|
+
}) {
|
|
9406
|
+
const hasSteps = Array.isArray(steps) && steps.length > 0;
|
|
9407
|
+
const footerItems = resolveFooterItems(footer);
|
|
9408
|
+
const hasFooter = footerItems.length > 0;
|
|
9409
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full bg-card pl-2", className), children: [
|
|
9410
|
+
hasSteps ? /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "flex flex-col", children: steps.map((step, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9411
|
+
TimelineStep,
|
|
9412
|
+
{
|
|
9413
|
+
step,
|
|
9414
|
+
isLast: index === steps.length - 1,
|
|
9415
|
+
accentColor
|
|
9416
|
+
},
|
|
9417
|
+
step.id ?? `${step.title}-${index}`
|
|
9418
|
+
)) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-6 text-slate-400 text-sm", children: "No steps" }),
|
|
9419
|
+
hasFooter ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 border-slate-200 border-t pt-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500", children: [
|
|
9420
|
+
showFooterIcon ? /* @__PURE__ */ jsxRuntime.jsx(ClockIcon2, { className: "size-4 shrink-0" }) : null,
|
|
9421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: footerItems.join(FOOTER_SEPARATOR) })
|
|
9422
|
+
] }) }) : null
|
|
9423
|
+
] });
|
|
9424
|
+
}
|
|
9425
|
+
var DEFAULTS = {
|
|
9426
|
+
/** Selected row background — emerald-50. */
|
|
9427
|
+
selectedBg: "#ECFDF5",
|
|
9428
|
+
/** Selected row border — emerald-200. */
|
|
9429
|
+
selectedBorder: "#A7F3D0",
|
|
9430
|
+
/** Selected radio circle fill — emerald-500. */
|
|
9431
|
+
selectedCircle: "#10B981",
|
|
9432
|
+
/** Selected label text — slate-900. */
|
|
9433
|
+
selectedLabel: "#0F172A",
|
|
9434
|
+
/** Unselected row border — slate-200. */
|
|
9435
|
+
unselectedBorder: "#E2E8F0",
|
|
9436
|
+
/** Unselected radio ring — slate-300. */
|
|
9437
|
+
unselectedRing: "#CBD5E1",
|
|
9438
|
+
/** Unselected label text — slate-500. */
|
|
9439
|
+
unselectedLabel: "#64748B",
|
|
9440
|
+
/** Progress pill background — indigo-50. */
|
|
9441
|
+
pillBg: "#EEF2FF",
|
|
9442
|
+
/** Progress pill text — indigo-600. */
|
|
9443
|
+
pillText: "#4F46E5",
|
|
9444
|
+
/** Category label text — slate-400. */
|
|
9445
|
+
categoryText: "#94A3B8",
|
|
9446
|
+
/** Question title text — slate-900. */
|
|
9447
|
+
titleColor: "#0F172A"
|
|
9448
|
+
};
|
|
9449
|
+
function resolveProgressLabel(progressLabel, questionIndex, questionTotal) {
|
|
9450
|
+
if (progressLabel) return progressLabel;
|
|
9451
|
+
if (typeof questionIndex === "number" && typeof questionTotal === "number" && questionTotal > 0) {
|
|
9452
|
+
return `Question ${questionIndex} of ${questionTotal}`;
|
|
7705
9453
|
}
|
|
7706
|
-
|
|
7707
|
-
|
|
9454
|
+
return void 0;
|
|
9455
|
+
}
|
|
9456
|
+
function CheckIcon9() {
|
|
9457
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9458
|
+
"svg",
|
|
9459
|
+
{
|
|
9460
|
+
"aria-hidden": "true",
|
|
9461
|
+
viewBox: "0 0 24 24",
|
|
9462
|
+
width: "14",
|
|
9463
|
+
height: "14",
|
|
9464
|
+
fill: "none",
|
|
9465
|
+
stroke: "#FFFFFF",
|
|
9466
|
+
strokeWidth: "3",
|
|
9467
|
+
strokeLinecap: "round",
|
|
9468
|
+
strokeLinejoin: "round",
|
|
9469
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" })
|
|
9470
|
+
}
|
|
9471
|
+
);
|
|
9472
|
+
}
|
|
9473
|
+
function OptionRow({
|
|
9474
|
+
option,
|
|
9475
|
+
selected,
|
|
9476
|
+
submitted,
|
|
9477
|
+
isTabStop,
|
|
9478
|
+
colors,
|
|
9479
|
+
customAnswer,
|
|
9480
|
+
customAnswerPlaceholder,
|
|
9481
|
+
onSelect,
|
|
9482
|
+
onMove,
|
|
9483
|
+
onCustomChange,
|
|
9484
|
+
onCustomCommit
|
|
9485
|
+
}) {
|
|
9486
|
+
const isOtherExpanded = !!option.isOther && selected;
|
|
9487
|
+
const handleKeyDown = (e) => {
|
|
9488
|
+
if (submitted) return;
|
|
9489
|
+
switch (e.key) {
|
|
9490
|
+
case " ":
|
|
9491
|
+
case "Enter":
|
|
9492
|
+
e.preventDefault();
|
|
9493
|
+
onSelect();
|
|
9494
|
+
break;
|
|
9495
|
+
// WAI-ARIA radiogroup pattern: arrow keys move focus to the adjacent
|
|
9496
|
+
// radio and check it. Down/Right advance, Up/Left retreat.
|
|
9497
|
+
case "ArrowDown":
|
|
9498
|
+
case "ArrowRight":
|
|
9499
|
+
e.preventDefault();
|
|
9500
|
+
onMove(1);
|
|
9501
|
+
break;
|
|
9502
|
+
case "ArrowUp":
|
|
9503
|
+
case "ArrowLeft":
|
|
9504
|
+
e.preventDefault();
|
|
9505
|
+
onMove(-1);
|
|
9506
|
+
break;
|
|
9507
|
+
}
|
|
9508
|
+
};
|
|
9509
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9510
|
+
"div",
|
|
9511
|
+
{
|
|
9512
|
+
role: "radio",
|
|
9513
|
+
"aria-checked": selected,
|
|
9514
|
+
tabIndex: submitted ? -1 : isTabStop ? 0 : -1,
|
|
9515
|
+
"data-option-id": option.id,
|
|
9516
|
+
onClick: submitted ? void 0 : onSelect,
|
|
9517
|
+
onKeyDown: handleKeyDown,
|
|
9518
|
+
className: cn(
|
|
9519
|
+
"flex items-center gap-3 rounded-xl border px-4 py-4 text-left transition-colors",
|
|
9520
|
+
!submitted && "cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-400/60",
|
|
9521
|
+
selected ? "font-medium" : "font-normal"
|
|
9522
|
+
),
|
|
9523
|
+
style: {
|
|
9524
|
+
backgroundColor: selected ? colors.selectedBg : "transparent",
|
|
9525
|
+
borderColor: selected ? colors.selectedBorder : colors.unselectedBorder
|
|
9526
|
+
},
|
|
9527
|
+
children: [
|
|
9528
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9529
|
+
"span",
|
|
9530
|
+
{
|
|
9531
|
+
"aria-hidden": "true",
|
|
9532
|
+
className: "mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full",
|
|
9533
|
+
style: selected ? { backgroundColor: colors.selectedCircle } : {
|
|
9534
|
+
backgroundColor: "transparent",
|
|
9535
|
+
border: `2px solid ${colors.unselectedRing}`
|
|
9536
|
+
},
|
|
9537
|
+
children: selected && /* @__PURE__ */ jsxRuntime.jsx(CheckIcon9, {})
|
|
9538
|
+
}
|
|
9539
|
+
),
|
|
9540
|
+
isOtherExpanded ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9541
|
+
"input",
|
|
9542
|
+
{
|
|
9543
|
+
type: "text",
|
|
9544
|
+
value: customAnswer,
|
|
9545
|
+
placeholder: customAnswerPlaceholder,
|
|
9546
|
+
disabled: submitted,
|
|
9547
|
+
autoComplete: "off",
|
|
9548
|
+
onClick: (e) => e.stopPropagation(),
|
|
9549
|
+
onChange: (e) => onCustomChange(e.target.value),
|
|
9550
|
+
onBlur: onCustomCommit,
|
|
9551
|
+
onKeyDown: (e) => {
|
|
9552
|
+
e.stopPropagation();
|
|
9553
|
+
if (e.key === "Enter") {
|
|
9554
|
+
e.preventDefault();
|
|
9555
|
+
onCustomCommit();
|
|
9556
|
+
}
|
|
9557
|
+
},
|
|
9558
|
+
className: "flex-1 border-0 bg-transparent p-0 text-sm leading-snug outline-none placeholder:text-slate-400",
|
|
9559
|
+
style: { color: colors.selectedLabel }
|
|
9560
|
+
}
|
|
9561
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
9562
|
+
"span",
|
|
9563
|
+
{
|
|
9564
|
+
className: "flex-1 text-sm leading-snug",
|
|
9565
|
+
style: { color: selected ? colors.selectedLabel : colors.unselectedLabel },
|
|
9566
|
+
children: option.label
|
|
9567
|
+
}
|
|
9568
|
+
)
|
|
9569
|
+
]
|
|
9570
|
+
}
|
|
9571
|
+
);
|
|
9572
|
+
}
|
|
9573
|
+
function BuiltInQuestions(props) {
|
|
9574
|
+
const {
|
|
9575
|
+
question,
|
|
9576
|
+
options,
|
|
9577
|
+
selectedOptionId,
|
|
9578
|
+
customAnswer,
|
|
9579
|
+
customAnswerPlaceholder,
|
|
9580
|
+
progressLabel,
|
|
9581
|
+
categoryLabel,
|
|
9582
|
+
questionIndex,
|
|
9583
|
+
questionTotal,
|
|
9584
|
+
submitted = false,
|
|
9585
|
+
onSelect,
|
|
9586
|
+
onCustomAnswerChange,
|
|
9587
|
+
onAnswer,
|
|
9588
|
+
colors: colorOverrides,
|
|
9589
|
+
className
|
|
9590
|
+
} = props;
|
|
9591
|
+
const colors = {
|
|
9592
|
+
...DEFAULTS,
|
|
9593
|
+
...colorOverrides
|
|
9594
|
+
};
|
|
9595
|
+
const safeOptions = (options ?? []).filter(
|
|
9596
|
+
(option) => !!option?.id
|
|
9597
|
+
);
|
|
9598
|
+
const radioGroupRef = React4.useRef(null);
|
|
9599
|
+
const isControlled = typeof onSelect === "function";
|
|
9600
|
+
const [internalSelected, setInternalSelected] = React4.useState(
|
|
9601
|
+
selectedOptionId ?? null
|
|
9602
|
+
);
|
|
9603
|
+
const [internalCustom, setInternalCustom] = React4.useState(customAnswer ?? "");
|
|
9604
|
+
const activeSelected = isControlled ? selectedOptionId ?? null : internalSelected;
|
|
9605
|
+
const activeCustom = isControlled ? customAnswer ?? "" : internalCustom;
|
|
9606
|
+
const resolvedProgress = resolveProgressLabel(
|
|
9607
|
+
progressLabel,
|
|
9608
|
+
questionIndex,
|
|
9609
|
+
questionTotal
|
|
9610
|
+
);
|
|
9611
|
+
const hasHeader = !!resolvedProgress || !!categoryLabel;
|
|
9612
|
+
const handleSelect = (option) => {
|
|
9613
|
+
if (submitted) return;
|
|
9614
|
+
if (isControlled) {
|
|
9615
|
+
onSelect?.(option.id);
|
|
9616
|
+
} else {
|
|
9617
|
+
setInternalSelected(option.id);
|
|
9618
|
+
}
|
|
9619
|
+
if (!option.isOther) {
|
|
9620
|
+
onAnswer?.({ optionId: option.id });
|
|
9621
|
+
}
|
|
9622
|
+
};
|
|
9623
|
+
const handleMove = (fromIndex, delta) => {
|
|
9624
|
+
if (submitted) return;
|
|
9625
|
+
const count = safeOptions.length;
|
|
9626
|
+
if (count === 0) return;
|
|
9627
|
+
const nextIndex = (fromIndex + delta + count) % count;
|
|
9628
|
+
const nextOption = safeOptions[nextIndex];
|
|
9629
|
+
if (!nextOption) return;
|
|
9630
|
+
handleSelect(nextOption);
|
|
9631
|
+
const group = radioGroupRef.current;
|
|
9632
|
+
if (group) {
|
|
9633
|
+
const target = group.querySelector(
|
|
9634
|
+
`[data-option-id="${CSS.escape(nextOption.id)}"]`
|
|
9635
|
+
);
|
|
9636
|
+
target?.focus();
|
|
9637
|
+
}
|
|
9638
|
+
};
|
|
9639
|
+
const handleCustomChange = (value) => {
|
|
9640
|
+
if (isControlled) {
|
|
9641
|
+
onCustomAnswerChange?.(value);
|
|
9642
|
+
} else {
|
|
9643
|
+
setInternalCustom(value);
|
|
9644
|
+
}
|
|
9645
|
+
};
|
|
9646
|
+
const handleCustomCommit = (optionId) => {
|
|
9647
|
+
onAnswer?.({ optionId, customAnswer: activeCustom });
|
|
9648
|
+
};
|
|
9649
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { className: cn("p-6 sm:p-7", className), children: [
|
|
9650
|
+
hasHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-5 flex items-center justify-between gap-3", children: [
|
|
9651
|
+
resolvedProgress ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
9652
|
+
"span",
|
|
9653
|
+
{
|
|
9654
|
+
className: "inline-flex items-center rounded-lg px-3 py-1 font-semibold text-xs",
|
|
9655
|
+
style: { backgroundColor: colors.pillBg, color: colors.pillText },
|
|
9656
|
+
children: resolvedProgress
|
|
9657
|
+
}
|
|
9658
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" }),
|
|
9659
|
+
categoryLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { color: colors.categoryText }, children: categoryLabel })
|
|
9660
|
+
] }),
|
|
9661
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9662
|
+
"h3",
|
|
9663
|
+
{
|
|
9664
|
+
className: "font-bold text-base leading-snug",
|
|
9665
|
+
style: { color: colors.titleColor },
|
|
9666
|
+
children: question
|
|
9667
|
+
}
|
|
9668
|
+
),
|
|
9669
|
+
safeOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9670
|
+
"div",
|
|
9671
|
+
{
|
|
9672
|
+
ref: radioGroupRef,
|
|
9673
|
+
role: "radiogroup",
|
|
9674
|
+
"aria-label": question,
|
|
9675
|
+
className: "mt-7 flex flex-col gap-3",
|
|
9676
|
+
children: (() => {
|
|
9677
|
+
const selectedIndex = safeOptions.findIndex(
|
|
9678
|
+
(option) => option.id === activeSelected
|
|
9679
|
+
);
|
|
9680
|
+
const tabStopIndex = selectedIndex === -1 ? 0 : selectedIndex;
|
|
9681
|
+
return safeOptions.map((option, i) => {
|
|
9682
|
+
const selected = activeSelected === option.id;
|
|
9683
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9684
|
+
OptionRow,
|
|
9685
|
+
{
|
|
9686
|
+
option,
|
|
9687
|
+
selected,
|
|
9688
|
+
submitted,
|
|
9689
|
+
isTabStop: i === tabStopIndex,
|
|
9690
|
+
colors,
|
|
9691
|
+
customAnswer: activeCustom,
|
|
9692
|
+
customAnswerPlaceholder,
|
|
9693
|
+
onSelect: () => handleSelect(option),
|
|
9694
|
+
onMove: (delta) => handleMove(i, delta),
|
|
9695
|
+
onCustomChange: handleCustomChange,
|
|
9696
|
+
onCustomCommit: () => handleCustomCommit(option.id)
|
|
9697
|
+
},
|
|
9698
|
+
option.id
|
|
9699
|
+
);
|
|
9700
|
+
});
|
|
9701
|
+
})()
|
|
9702
|
+
}
|
|
9703
|
+
)
|
|
9704
|
+
] });
|
|
9705
|
+
}
|
|
9706
|
+
var DEFAULT_ACCENT_COLOR = "#1e2a44";
|
|
9707
|
+
var DEFAULT_INACTIVE_COLOR = "#dfe3ea";
|
|
9708
|
+
var DEFAULT_ACTIVE_CHAPTER_BG = "#f4f6fa";
|
|
9709
|
+
var DEFAULT_MUTED_TEXT_COLOR = "#94a3b8";
|
|
9710
|
+
var DEFAULT_WAVEFORM = [
|
|
9711
|
+
0.4,
|
|
9712
|
+
0.45,
|
|
9713
|
+
0.95,
|
|
9714
|
+
0.6,
|
|
9715
|
+
1,
|
|
9716
|
+
0.85,
|
|
9717
|
+
0.4,
|
|
9718
|
+
0.55,
|
|
9719
|
+
0.7,
|
|
9720
|
+
0.45,
|
|
9721
|
+
0.6,
|
|
9722
|
+
0.45,
|
|
9723
|
+
0.4,
|
|
9724
|
+
0.7,
|
|
9725
|
+
0.5,
|
|
9726
|
+
0.45,
|
|
9727
|
+
0.55,
|
|
9728
|
+
0.4
|
|
9729
|
+
];
|
|
9730
|
+
function formatTime2(totalSeconds) {
|
|
9731
|
+
const safe = Number.isFinite(totalSeconds) && totalSeconds > 0 ? totalSeconds : 0;
|
|
9732
|
+
const whole = Math.floor(safe);
|
|
9733
|
+
const minutes = Math.floor(whole / 60);
|
|
9734
|
+
const seconds = whole % 60;
|
|
9735
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
9736
|
+
}
|
|
9737
|
+
function AudioPlayer({
|
|
9738
|
+
title,
|
|
9739
|
+
src,
|
|
9740
|
+
durationSeconds = 0,
|
|
9741
|
+
currentTimeSeconds = 0,
|
|
9742
|
+
isPlaying = false,
|
|
9743
|
+
waveform,
|
|
9744
|
+
chapters,
|
|
9745
|
+
accentColor,
|
|
9746
|
+
inactiveColor,
|
|
9747
|
+
activeChapterBg,
|
|
9748
|
+
mutedTextColor,
|
|
9749
|
+
className,
|
|
9750
|
+
onTogglePlay,
|
|
9751
|
+
onSeek
|
|
9752
|
+
}) {
|
|
9753
|
+
const accent = accentColor ?? DEFAULT_ACCENT_COLOR;
|
|
9754
|
+
const inactive = inactiveColor ?? DEFAULT_INACTIVE_COLOR;
|
|
9755
|
+
const activeBg = activeChapterBg ?? DEFAULT_ACTIVE_CHAPTER_BG;
|
|
9756
|
+
const mutedText = mutedTextColor ?? DEFAULT_MUTED_TEXT_COLOR;
|
|
9757
|
+
const bars = waveform && waveform.length > 0 ? waveform : DEFAULT_WAVEFORM;
|
|
9758
|
+
const chapterList = chapters ?? [];
|
|
9759
|
+
const hasSrc = typeof src === "string" && src.length > 0;
|
|
9760
|
+
const audioRef = React4.useRef(null);
|
|
9761
|
+
const [currentTime, setCurrentTime] = React4.useState(currentTimeSeconds);
|
|
9762
|
+
const [duration, setDuration] = React4.useState(durationSeconds);
|
|
9763
|
+
const [playing, setPlaying] = React4.useState(isPlaying);
|
|
9764
|
+
React4.useEffect(() => {
|
|
9765
|
+
setCurrentTime(currentTimeSeconds);
|
|
9766
|
+
}, [currentTimeSeconds]);
|
|
9767
|
+
React4.useEffect(() => {
|
|
9768
|
+
setDuration(durationSeconds);
|
|
9769
|
+
}, [durationSeconds]);
|
|
9770
|
+
React4.useEffect(() => {
|
|
9771
|
+
setPlaying(isPlaying);
|
|
9772
|
+
}, [isPlaying]);
|
|
9773
|
+
const lastChapterStart = chapterList.reduce(
|
|
9774
|
+
(max, c) => c.startSeconds > max ? c.startSeconds : max,
|
|
9775
|
+
0
|
|
9776
|
+
);
|
|
9777
|
+
const effectiveDuration = duration > 0 ? duration : durationSeconds > 0 ? durationSeconds : lastChapterStart;
|
|
9778
|
+
const progress = effectiveDuration > 0 ? Math.min(Math.max(currentTime / effectiveDuration, 0), 1) : 0;
|
|
9779
|
+
const handleLoadedMetadata = React4.useCallback(() => {
|
|
9780
|
+
const el = audioRef.current;
|
|
9781
|
+
if (el && Number.isFinite(el.duration)) setDuration(el.duration);
|
|
9782
|
+
}, []);
|
|
9783
|
+
const handleTimeUpdate = React4.useCallback(() => {
|
|
9784
|
+
const el = audioRef.current;
|
|
9785
|
+
if (el) setCurrentTime(el.currentTime);
|
|
9786
|
+
}, []);
|
|
9787
|
+
const handleEnded = React4.useCallback(() => {
|
|
9788
|
+
setPlaying(false);
|
|
9789
|
+
onTogglePlay?.(false);
|
|
9790
|
+
}, [onTogglePlay]);
|
|
9791
|
+
const seekTo = React4.useCallback(
|
|
9792
|
+
(seconds) => {
|
|
9793
|
+
const clamped = effectiveDuration > 0 ? Math.min(Math.max(seconds, 0), effectiveDuration) : Math.max(seconds, 0);
|
|
9794
|
+
setCurrentTime(clamped);
|
|
9795
|
+
const el = audioRef.current;
|
|
9796
|
+
if (el) {
|
|
9797
|
+
try {
|
|
9798
|
+
el.currentTime = clamped;
|
|
9799
|
+
} catch {
|
|
9800
|
+
}
|
|
9801
|
+
}
|
|
9802
|
+
onSeek?.(clamped);
|
|
9803
|
+
},
|
|
9804
|
+
[effectiveDuration, onSeek]
|
|
9805
|
+
);
|
|
9806
|
+
const togglePlay = React4.useCallback(() => {
|
|
9807
|
+
const next = !playing;
|
|
9808
|
+
setPlaying(next);
|
|
9809
|
+
const el = audioRef.current;
|
|
9810
|
+
if (el) {
|
|
9811
|
+
try {
|
|
9812
|
+
if (next) {
|
|
9813
|
+
const maybePromise = el.play();
|
|
9814
|
+
if (maybePromise && typeof maybePromise.catch === "function") {
|
|
9815
|
+
maybePromise.catch(() => {
|
|
9816
|
+
});
|
|
9817
|
+
}
|
|
9818
|
+
} else {
|
|
9819
|
+
el.pause();
|
|
9820
|
+
}
|
|
9821
|
+
} catch {
|
|
9822
|
+
}
|
|
9823
|
+
}
|
|
9824
|
+
onTogglePlay?.(next);
|
|
9825
|
+
}, [playing, onTogglePlay]);
|
|
9826
|
+
const handleWaveformSeek = React4.useCallback(
|
|
9827
|
+
(event) => {
|
|
9828
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
9829
|
+
const ratio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0;
|
|
9830
|
+
const clampedRatio = Math.min(Math.max(ratio, 0), 1);
|
|
9831
|
+
seekTo(clampedRatio * effectiveDuration);
|
|
9832
|
+
},
|
|
9833
|
+
[effectiveDuration, seekTo]
|
|
9834
|
+
);
|
|
9835
|
+
let activeChapterIndex = -1;
|
|
9836
|
+
for (let i = 0; i < chapterList.length; i += 1) {
|
|
9837
|
+
if (chapterList[i].startSeconds <= currentTime) activeChapterIndex = i;
|
|
7708
9838
|
}
|
|
7709
|
-
return
|
|
9839
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full", className), children: [
|
|
9840
|
+
hasSrc && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9841
|
+
"audio",
|
|
9842
|
+
{
|
|
9843
|
+
ref: audioRef,
|
|
9844
|
+
src,
|
|
9845
|
+
preload: "metadata",
|
|
9846
|
+
className: "hidden",
|
|
9847
|
+
"aria-label": title,
|
|
9848
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
9849
|
+
onTimeUpdate: handleTimeUpdate,
|
|
9850
|
+
onEnded: handleEnded,
|
|
9851
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("track", { kind: "captions" })
|
|
9852
|
+
}
|
|
9853
|
+
),
|
|
9854
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-5", children: [
|
|
9855
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9856
|
+
"button",
|
|
9857
|
+
{
|
|
9858
|
+
type: "button",
|
|
9859
|
+
onClick: togglePlay,
|
|
9860
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
9861
|
+
"aria-pressed": playing,
|
|
9862
|
+
className: "flex h-24 w-24 shrink-0 items-center justify-center rounded-full text-white transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
9863
|
+
style: { backgroundColor: accent },
|
|
9864
|
+
children: playing ? (
|
|
9865
|
+
// Pause glyph: two rounded bars.
|
|
9866
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "34", height: "34", viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
9867
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1.5", fill: "currentColor" }),
|
|
9868
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1.5", fill: "currentColor" })
|
|
9869
|
+
] })
|
|
9870
|
+
) : (
|
|
9871
|
+
// Play glyph: a single triangle nudged right for optical centering.
|
|
9872
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { width: "34", height: "34", viewBox: "0 0 24 24", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5.5l11 6.5-11 6.5z", fill: "currentColor" }) })
|
|
9873
|
+
)
|
|
9874
|
+
}
|
|
9875
|
+
),
|
|
9876
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9877
|
+
"button",
|
|
9878
|
+
{
|
|
9879
|
+
type: "button",
|
|
9880
|
+
onClick: handleWaveformSeek,
|
|
9881
|
+
"aria-label": "Seek",
|
|
9882
|
+
className: "flex h-[88px] flex-1 cursor-pointer items-center gap-[6px] rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
9883
|
+
children: bars.map((rawHeight, index) => {
|
|
9884
|
+
const height = Math.min(Math.max(rawHeight, 0), 1);
|
|
9885
|
+
const barRatio = bars.length > 1 ? index / (bars.length - 1) : 0;
|
|
9886
|
+
const played = barRatio <= progress;
|
|
9887
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9888
|
+
"span",
|
|
9889
|
+
{
|
|
9890
|
+
"aria-hidden": "true",
|
|
9891
|
+
className: "flex-1 rounded-[3px]",
|
|
9892
|
+
style: {
|
|
9893
|
+
height: `${Math.max(height * 100, 8)}%`,
|
|
9894
|
+
backgroundColor: played ? accent : inactive
|
|
9895
|
+
}
|
|
9896
|
+
},
|
|
9897
|
+
index
|
|
9898
|
+
);
|
|
9899
|
+
})
|
|
9900
|
+
}
|
|
9901
|
+
),
|
|
9902
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9903
|
+
"span",
|
|
9904
|
+
{
|
|
9905
|
+
className: "shrink-0 font-mono text-xs tabular-nums",
|
|
9906
|
+
style: { color: mutedText },
|
|
9907
|
+
children: [
|
|
9908
|
+
formatTime2(currentTime),
|
|
9909
|
+
" / ",
|
|
9910
|
+
formatTime2(effectiveDuration)
|
|
9911
|
+
]
|
|
9912
|
+
}
|
|
9913
|
+
)
|
|
9914
|
+
] }),
|
|
9915
|
+
chapterList.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-6 flex flex-col gap-1", children: chapterList.map((chapter, index) => {
|
|
9916
|
+
const isActive = index === activeChapterIndex;
|
|
9917
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9918
|
+
"button",
|
|
9919
|
+
{
|
|
9920
|
+
type: "button",
|
|
9921
|
+
onClick: () => seekTo(chapter.startSeconds),
|
|
9922
|
+
"aria-current": isActive ? "true" : void 0,
|
|
9923
|
+
className: cn(
|
|
9924
|
+
"flex w-full items-center gap-5 rounded-xl px-3 py-2.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
9925
|
+
),
|
|
9926
|
+
style: isActive ? { backgroundColor: activeBg } : void 0,
|
|
9927
|
+
children: [
|
|
9928
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9929
|
+
"span",
|
|
9930
|
+
{
|
|
9931
|
+
className: "shrink-0 font-mono text-xs tabular-nums",
|
|
9932
|
+
style: { color: mutedText },
|
|
9933
|
+
children: formatTime2(chapter.startSeconds)
|
|
9934
|
+
}
|
|
9935
|
+
),
|
|
9936
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9937
|
+
"span",
|
|
9938
|
+
{
|
|
9939
|
+
className: cn(
|
|
9940
|
+
"text-sm",
|
|
9941
|
+
isActive ? "font-semibold text-slate-900" : "font-normal text-slate-500"
|
|
9942
|
+
),
|
|
9943
|
+
children: chapter.label
|
|
9944
|
+
}
|
|
9945
|
+
)
|
|
9946
|
+
]
|
|
9947
|
+
}
|
|
9948
|
+
) }, chapter.startSeconds + "-" + chapter.label);
|
|
9949
|
+
}) })
|
|
9950
|
+
] });
|
|
9951
|
+
}
|
|
9952
|
+
var ARTIFACT_REGISTRY = {
|
|
9953
|
+
chart: ({ payload, className }) => payload.chart ? /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { data: payload.chart, className }) : null,
|
|
9954
|
+
metrics: ({ payload, className }) => payload.metrics ? /* @__PURE__ */ jsxRuntime.jsx(MetricsGrid, { metrics: payload.metrics, className }) : null,
|
|
9955
|
+
table: ({ payload, className }) => payload.table ? /* @__PURE__ */ jsxRuntime.jsx(DataTable, { data: payload.table, className }) : null,
|
|
9956
|
+
"kpi-card-with-chart": ({ payload, className }) => payload.kpiCardWithChart ? /* @__PURE__ */ jsxRuntime.jsx(KpiCardWithChart, { ...payload.kpiCardWithChart, className }) : null,
|
|
9957
|
+
"pie-chart": ({ payload, className }) => payload.pieChart ? /* @__PURE__ */ jsxRuntime.jsx(PieChartArtifact, { ...payload.pieChart, className }) : null,
|
|
9958
|
+
"stacked-sparklines": ({ payload, className }) => payload.stackedSparklines ? /* @__PURE__ */ jsxRuntime.jsx(StackedSparklines, { ...payload.stackedSparklines, className }) : null,
|
|
9959
|
+
"stat-card-half-circle": ({ payload, className }) => payload.statCardHalfCircle ? /* @__PURE__ */ jsxRuntime.jsx(StatCardHalfCircle, { ...payload.statCardHalfCircle, className }) : null,
|
|
9960
|
+
"table-list": ({ payload, className }) => payload.tableList ? /* @__PURE__ */ jsxRuntime.jsx(TableListArtifact, { ...payload.tableList, className }) : null,
|
|
9961
|
+
"kpi-card-with-sparklines": ({ payload, className }) => payload.kpiCardWithSparklines ? /* @__PURE__ */ jsxRuntime.jsx(KpiCardWithSparklines, { ...payload.kpiCardWithSparklines, className }) : null,
|
|
9962
|
+
"locations-revenue-card": ({ payload, className }) => payload.locationsRevenue ? /* @__PURE__ */ jsxRuntime.jsx(LocationsRevenueCard, { ...payload.locationsRevenue, className }) : null,
|
|
9963
|
+
"row-based-data-list": ({ payload, className }) => payload.rowBasedDataList ? /* @__PURE__ */ jsxRuntime.jsx(RowBasedDataList, { ...payload.rowBasedDataList, className }) : null,
|
|
9964
|
+
badges: ({ payload, className }) => payload.badges ? /* @__PURE__ */ jsxRuntime.jsx(BadgesArtifact, { ...payload.badges, className }) : null,
|
|
9965
|
+
"ordered-list": ({ payload, className }) => payload.orderedList ? /* @__PURE__ */ jsxRuntime.jsx(OrderedListArtifact, { ...payload.orderedList, className }) : null,
|
|
9966
|
+
"deep-research-progress": ({ payload, className }) => payload.deepResearchProgress ? /* @__PURE__ */ jsxRuntime.jsx(DeepResearchProgress, { ...payload.deepResearchProgress, className }) : null,
|
|
9967
|
+
tracker: ({ payload, className }) => payload.tracker ? /* @__PURE__ */ jsxRuntime.jsx(Tracker, { ...payload.tracker, className }) : null,
|
|
9968
|
+
"built-in-questions": ({ payload, className }) => payload.builtInQuestions ? /* @__PURE__ */ jsxRuntime.jsx(BuiltInQuestions, { ...payload.builtInQuestions, className }) : null,
|
|
9969
|
+
"audio-player": ({ payload, className }) => payload.audioPlayer ? /* @__PURE__ */ jsxRuntime.jsx(AudioPlayer, { ...payload.audioPlayer, className }) : null
|
|
9970
|
+
};
|
|
9971
|
+
function DataPayloadView({ payload, className }) {
|
|
9972
|
+
const render = ARTIFACT_REGISTRY[payload.type];
|
|
9973
|
+
return render ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render({ payload, className }) }) : null;
|
|
7710
9974
|
}
|
|
7711
9975
|
function ReportView({ report, className }) {
|
|
7712
9976
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-6", className), children: [
|
|
@@ -7797,25 +10061,34 @@ function MessageList({
|
|
|
7797
10061
|
const hasSteps = message.role === "assistant" && !!message.steps && message.steps.length > 0;
|
|
7798
10062
|
const hasAttachments = !!message.attachments && message.attachments.length > 0;
|
|
7799
10063
|
return /* @__PURE__ */ jsxRuntime.jsxs(MessageContainer, { role: message.role, children: [
|
|
7800
|
-
showAvatars && /* @__PURE__ */ jsxRuntime.jsx(AgentAvatar, { role: message.role, size: "
|
|
7801
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
10064
|
+
showAvatars && /* @__PURE__ */ jsxRuntime.jsx(AgentAvatar, { role: message.role, size: "md" }),
|
|
10065
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10066
|
+
"div",
|
|
10067
|
+
{
|
|
10068
|
+
className: cn(
|
|
10069
|
+
"flex min-w-0 flex-col gap-2",
|
|
10070
|
+
message.role === "user" && "items-end"
|
|
10071
|
+
),
|
|
10072
|
+
children: [
|
|
10073
|
+
hasReasoning && /* @__PURE__ */ jsxRuntime.jsx(MessageWithReasoning, { steps: message.reasoning }),
|
|
10074
|
+
hasSteps && /* @__PURE__ */ jsxRuntime.jsx(MessageWithSteps, { steps: message.steps }),
|
|
10075
|
+
message.content && /* @__PURE__ */ jsxRuntime.jsx(MessageBubble, { role: message.role, children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: message.content }) }),
|
|
10076
|
+
hasAttachments && /* @__PURE__ */ jsxRuntime.jsx(MessageWithAttachments, { attachments: message.attachments }),
|
|
10077
|
+
message.data && /* @__PURE__ */ jsxRuntime.jsx(DataPayloadView, { payload: message.data }),
|
|
10078
|
+
message.confirmation && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10079
|
+
ConfirmationPanel,
|
|
10080
|
+
{
|
|
10081
|
+
title: message.confirmation.title,
|
|
10082
|
+
summary: message.confirmation.summary,
|
|
10083
|
+
body: message.confirmation.body,
|
|
10084
|
+
markdown: message.confirmation.markdown,
|
|
10085
|
+
actions: message.confirmation.actions,
|
|
10086
|
+
onAction: (actionId) => onConfirmAction?.(message.id, actionId)
|
|
10087
|
+
}
|
|
10088
|
+
)
|
|
10089
|
+
]
|
|
10090
|
+
}
|
|
10091
|
+
)
|
|
7819
10092
|
] }, message.id);
|
|
7820
10093
|
}) });
|
|
7821
10094
|
}
|
|
@@ -8741,13 +11014,31 @@ function useOpenState(controlled, defaultOpen, onChange) {
|
|
|
8741
11014
|
);
|
|
8742
11015
|
return [open, setOpen];
|
|
8743
11016
|
}
|
|
11017
|
+
function useIsDesktop() {
|
|
11018
|
+
const read = () => typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(min-width: 1024px)").matches : true;
|
|
11019
|
+
const [isDesktop, setIsDesktop] = React4.useState(read);
|
|
11020
|
+
React4.useEffect(() => {
|
|
11021
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
11022
|
+
return;
|
|
11023
|
+
const mql = window.matchMedia("(min-width: 1024px)");
|
|
11024
|
+
const update = () => setIsDesktop(mql.matches);
|
|
11025
|
+
update();
|
|
11026
|
+
if (mql.addEventListener) {
|
|
11027
|
+
mql.addEventListener("change", update);
|
|
11028
|
+
return () => mql.removeEventListener("change", update);
|
|
11029
|
+
}
|
|
11030
|
+
mql.addListener(update);
|
|
11031
|
+
return () => mql.removeListener(update);
|
|
11032
|
+
}, []);
|
|
11033
|
+
return isDesktop;
|
|
11034
|
+
}
|
|
8744
11035
|
function AgentWorkspace({
|
|
8745
11036
|
children,
|
|
8746
11037
|
leftPanel,
|
|
8747
11038
|
rightPanel,
|
|
8748
11039
|
leftPanelWidth = 320,
|
|
8749
11040
|
rightPanelWidth = 460,
|
|
8750
|
-
defaultLeftPanelOpen
|
|
11041
|
+
defaultLeftPanelOpen,
|
|
8751
11042
|
defaultRightPanelOpen = true,
|
|
8752
11043
|
leftPanelOpen: leftPanelOpenProp,
|
|
8753
11044
|
rightPanelOpen: rightPanelOpenProp,
|
|
@@ -8757,6 +11048,7 @@ function AgentWorkspace({
|
|
|
8757
11048
|
showHeader,
|
|
8758
11049
|
showConversationHeader,
|
|
8759
11050
|
headerActions,
|
|
11051
|
+
headerControlsPlacement = "before",
|
|
8760
11052
|
headerStatus,
|
|
8761
11053
|
conversation,
|
|
8762
11054
|
conversationOpen: conversationOpenProp,
|
|
@@ -8776,9 +11068,11 @@ function AgentWorkspace({
|
|
|
8776
11068
|
const agent = useNativeAgentOptional();
|
|
8777
11069
|
const hasLeftPanel = leftPanel !== void 0 && leftPanel !== null;
|
|
8778
11070
|
const hasRightPanel = rightPanel !== void 0 && rightPanel !== null;
|
|
11071
|
+
const isDesktop = useIsDesktop();
|
|
11072
|
+
const resolvedLeftDefault = defaultLeftPanelOpen ?? (leftPanelOpenProp === void 0 ? isDesktop : true);
|
|
8779
11073
|
const [leftOpen, setLeftOpen] = useOpenState(
|
|
8780
11074
|
leftPanelOpenProp,
|
|
8781
|
-
|
|
11075
|
+
resolvedLeftDefault,
|
|
8782
11076
|
onLeftPanelOpenChange
|
|
8783
11077
|
);
|
|
8784
11078
|
const [rightOpen, setRightOpen] = useOpenState(
|
|
@@ -8835,6 +11129,39 @@ function AgentWorkspace({
|
|
|
8835
11129
|
) : composer;
|
|
8836
11130
|
const headerVisible = showHeader ?? showConversationHeader ?? true;
|
|
8837
11131
|
const statusLine = headerStatus ?? (agent ? agent.error ?? agent.statusLabel : null);
|
|
11132
|
+
const builtInControls = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11133
|
+
agent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11134
|
+
"button",
|
|
11135
|
+
{
|
|
11136
|
+
type: "button",
|
|
11137
|
+
onClick: () => agent.reset(),
|
|
11138
|
+
"aria-label": "Start a new conversation",
|
|
11139
|
+
title: "New conversation",
|
|
11140
|
+
className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
11141
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "size-4" })
|
|
11142
|
+
}
|
|
11143
|
+
),
|
|
11144
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11145
|
+
"button",
|
|
11146
|
+
{
|
|
11147
|
+
type: "button",
|
|
11148
|
+
onClick: () => setConversationOpen(!conversationOpen),
|
|
11149
|
+
"aria-label": conversationOpen ? "Hide conversation" : "Show conversation",
|
|
11150
|
+
title: conversationOpen ? "Hide conversation" : "Show conversation",
|
|
11151
|
+
className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
11152
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11153
|
+
lucideReact.ChevronDown,
|
|
11154
|
+
{
|
|
11155
|
+
className: cn(
|
|
11156
|
+
"size-4 transition-transform duration-200",
|
|
11157
|
+
!conversationOpen && "rotate-180"
|
|
11158
|
+
)
|
|
11159
|
+
}
|
|
11160
|
+
)
|
|
11161
|
+
}
|
|
11162
|
+
),
|
|
11163
|
+
hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx(AgentWorkspacePanelToggle, { side: "right", className: "hidden md:flex" })
|
|
11164
|
+
] });
|
|
8838
11165
|
return /* @__PURE__ */ jsxRuntime.jsx(AgentWorkspaceContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8839
11166
|
"div",
|
|
8840
11167
|
{
|
|
@@ -8850,7 +11177,7 @@ function AgentWorkspace({
|
|
|
8850
11177
|
"data-state": leftOpen ? "open" : "collapsed",
|
|
8851
11178
|
"aria-hidden": !leftOpen || void 0,
|
|
8852
11179
|
className: cn(
|
|
8853
|
-
"relative hidden h-full shrink-0 overflow-hidden border-r border-border transition-[width] duration-300 ease-in-out
|
|
11180
|
+
"relative hidden h-full shrink-0 overflow-hidden border-r border-border transition-[width] duration-300 ease-in-out md:block",
|
|
8854
11181
|
!leftOpen && "border-r-0",
|
|
8855
11182
|
leftPanelClassName
|
|
8856
11183
|
),
|
|
@@ -8859,14 +11186,14 @@ function AgentWorkspace({
|
|
|
8859
11186
|
}
|
|
8860
11187
|
),
|
|
8861
11188
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-full min-w-0 grow flex-col", children: [
|
|
8862
|
-
showPanelToggles && hasLeftPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-3 top-3 z-30 hidden
|
|
11189
|
+
showPanelToggles && hasLeftPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-3 top-3 z-30 hidden md:block", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8863
11190
|
AgentWorkspacePanelToggle,
|
|
8864
11191
|
{
|
|
8865
11192
|
side: "left",
|
|
8866
11193
|
className: "border border-border/60 bg-background/80 shadow-sm backdrop-blur"
|
|
8867
11194
|
}
|
|
8868
11195
|
) }),
|
|
8869
|
-
showPanelToggles && hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-3 z-30 hidden
|
|
11196
|
+
showPanelToggles && hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-3 z-30 hidden md:block", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8870
11197
|
AgentWorkspacePanelToggle,
|
|
8871
11198
|
{
|
|
8872
11199
|
side: "right",
|
|
@@ -8884,7 +11211,7 @@ function AgentWorkspace({
|
|
|
8884
11211
|
AgentWorkspacePanelToggle,
|
|
8885
11212
|
{
|
|
8886
11213
|
side: "left",
|
|
8887
|
-
className: "hidden
|
|
11214
|
+
className: "hidden md:flex"
|
|
8888
11215
|
}
|
|
8889
11216
|
),
|
|
8890
11217
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "size-3.5" }) }),
|
|
@@ -8893,46 +11220,13 @@ function AgentWorkspace({
|
|
|
8893
11220
|
statusLine && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-muted-foreground", children: statusLine })
|
|
8894
11221
|
] })
|
|
8895
11222
|
] }),
|
|
8896
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11223
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-1", children: headerControlsPlacement === "before" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11224
|
+
builtInControls,
|
|
11225
|
+
headerActions
|
|
11226
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8897
11227
|
headerActions,
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
{
|
|
8901
|
-
type: "button",
|
|
8902
|
-
onClick: () => agent.reset(),
|
|
8903
|
-
"aria-label": "Start a new conversation",
|
|
8904
|
-
title: "New conversation",
|
|
8905
|
-
className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
8906
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "size-4" })
|
|
8907
|
-
}
|
|
8908
|
-
),
|
|
8909
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8910
|
-
"button",
|
|
8911
|
-
{
|
|
8912
|
-
type: "button",
|
|
8913
|
-
onClick: () => setConversationOpen(!conversationOpen),
|
|
8914
|
-
"aria-label": conversationOpen ? "Hide conversation" : "Show conversation",
|
|
8915
|
-
title: conversationOpen ? "Hide conversation" : "Show conversation",
|
|
8916
|
-
className: "flex size-8 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
8917
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8918
|
-
lucideReact.ChevronDown,
|
|
8919
|
-
{
|
|
8920
|
-
className: cn(
|
|
8921
|
-
"size-4 transition-transform duration-200",
|
|
8922
|
-
!conversationOpen && "rotate-180"
|
|
8923
|
-
)
|
|
8924
|
-
}
|
|
8925
|
-
)
|
|
8926
|
-
}
|
|
8927
|
-
),
|
|
8928
|
-
hasRightPanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8929
|
-
AgentWorkspacePanelToggle,
|
|
8930
|
-
{
|
|
8931
|
-
side: "right",
|
|
8932
|
-
className: "hidden lg:flex"
|
|
8933
|
-
}
|
|
8934
|
-
)
|
|
8935
|
-
] })
|
|
11228
|
+
builtInControls
|
|
11229
|
+
] }) })
|
|
8936
11230
|
]
|
|
8937
11231
|
}
|
|
8938
11232
|
),
|
|
@@ -8999,7 +11293,7 @@ function AgentWorkspace({
|
|
|
8999
11293
|
"data-state": rightOpen ? "open" : "collapsed",
|
|
9000
11294
|
"aria-hidden": !rightOpen || void 0,
|
|
9001
11295
|
className: cn(
|
|
9002
|
-
"relative hidden h-full shrink-0 overflow-hidden border-l border-border transition-[width] duration-300 ease-in-out
|
|
11296
|
+
"relative hidden h-full shrink-0 overflow-hidden border-l border-border transition-[width] duration-300 ease-in-out md:block",
|
|
9003
11297
|
!rightOpen && "border-l-0",
|
|
9004
11298
|
rightPanelClassName
|
|
9005
11299
|
),
|
|
@@ -9114,10 +11408,13 @@ exports.AgentWorkspacePanelToggle = AgentWorkspacePanelToggle;
|
|
|
9114
11408
|
exports.AgentWorkspaceSkeleton = AgentWorkspaceSkeleton;
|
|
9115
11409
|
exports.AllocationBreakdown = AllocationBreakdown;
|
|
9116
11410
|
exports.AnalyticsDashboard = AnalyticsDashboard;
|
|
11411
|
+
exports.AudioPlayer = AudioPlayer;
|
|
9117
11412
|
exports.Avatar = Avatar;
|
|
9118
11413
|
exports.AvatarFallback = AvatarFallback;
|
|
9119
11414
|
exports.AvatarImage = AvatarImage;
|
|
9120
11415
|
exports.Badge = Badge;
|
|
11416
|
+
exports.BadgesArtifact = BadgesArtifact;
|
|
11417
|
+
exports.BuiltInQuestions = BuiltInQuestions;
|
|
9121
11418
|
exports.Button = Button;
|
|
9122
11419
|
exports.ChartContainer = ChartContainer;
|
|
9123
11420
|
exports.ChatPanel = ChatPanel;
|
|
@@ -9130,6 +11427,7 @@ exports.ConversationAnalytics = ConversationAnalytics;
|
|
|
9130
11427
|
exports.ConversationArtifact = ConversationArtifact;
|
|
9131
11428
|
exports.DataPayloadView = DataPayloadView;
|
|
9132
11429
|
exports.DataTable = DataTable;
|
|
11430
|
+
exports.DeepResearchProgress = DeepResearchProgress;
|
|
9133
11431
|
exports.DynamicRenderer = DynamicRenderer;
|
|
9134
11432
|
exports.EntityCard = EntityCard;
|
|
9135
11433
|
exports.FileDropZone = FileDropZone;
|
|
@@ -9140,7 +11438,10 @@ exports.GuidedLessonFlow = GuidedLessonFlow;
|
|
|
9140
11438
|
exports.ImageGenerator = ImageGenerator;
|
|
9141
11439
|
exports.InlineSuggestionsInput = InlineSuggestionsInput;
|
|
9142
11440
|
exports.Input = Input;
|
|
11441
|
+
exports.KpiCardWithChart = KpiCardWithChart;
|
|
11442
|
+
exports.KpiCardWithSparklines = KpiCardWithSparklines;
|
|
9143
11443
|
exports.ListingFeed = ListingFeed;
|
|
11444
|
+
exports.LocationsRevenueCard = LocationsRevenueCard;
|
|
9144
11445
|
exports.MediaEditorCanvas = MediaEditorCanvas;
|
|
9145
11446
|
exports.MediaGallery = MediaGallery;
|
|
9146
11447
|
exports.MessageActions = MessageActions;
|
|
@@ -9159,9 +11460,11 @@ exports.NativeAgentProvider = NativeAgentProvider;
|
|
|
9159
11460
|
exports.NativeSurface = NativeSurface;
|
|
9160
11461
|
exports.OnboardingWizard = OnboardingWizard;
|
|
9161
11462
|
exports.OptionCards = OptionCards;
|
|
11463
|
+
exports.OrderedListArtifact = OrderedListArtifact;
|
|
9162
11464
|
exports.OverlayModal = OverlayModal;
|
|
9163
11465
|
exports.PerformanceMetrics = PerformanceMetrics;
|
|
9164
11466
|
exports.PersonaSelector = PersonaSelector;
|
|
11467
|
+
exports.PieChartArtifact = PieChartArtifact;
|
|
9165
11468
|
exports.Progress = Progress;
|
|
9166
11469
|
exports.ProgressTracker = ProgressTracker;
|
|
9167
11470
|
exports.PromptInput = PromptInput;
|
|
@@ -9170,6 +11473,7 @@ exports.QuickReplies = QuickReplies;
|
|
|
9170
11473
|
exports.QuizCard = QuizCard;
|
|
9171
11474
|
exports.RecommendationCards = RecommendationCards;
|
|
9172
11475
|
exports.ReportView = ReportView;
|
|
11476
|
+
exports.RowBasedDataList = RowBasedDataList;
|
|
9173
11477
|
exports.ScheduleTimeline = ScheduleTimeline;
|
|
9174
11478
|
exports.ScrollArea = ScrollArea;
|
|
9175
11479
|
exports.ScrollBar = ScrollBar;
|
|
@@ -9178,8 +11482,11 @@ exports.SentimentDisplay = SentimentDisplay;
|
|
|
9178
11482
|
exports.SettingsPanel = SettingsPanel;
|
|
9179
11483
|
exports.SlotRenderer = SlotRenderer;
|
|
9180
11484
|
exports.SplitView = SplitView;
|
|
11485
|
+
exports.StackedSparklines = StackedSparklines;
|
|
11486
|
+
exports.StatCardHalfCircle = StatCardHalfCircle;
|
|
9181
11487
|
exports.StatusBadge = StatusBadge;
|
|
9182
11488
|
exports.SystemMessage = SystemMessage;
|
|
11489
|
+
exports.TableListArtifact = TableListArtifact;
|
|
9183
11490
|
exports.TemplateSelector = TemplateSelector;
|
|
9184
11491
|
exports.Textarea = Textarea;
|
|
9185
11492
|
exports.Timestamp = Timestamp;
|
|
@@ -9187,6 +11494,7 @@ exports.Tooltip = Tooltip4;
|
|
|
9187
11494
|
exports.TooltipContent = TooltipContent;
|
|
9188
11495
|
exports.TooltipProvider = TooltipProvider;
|
|
9189
11496
|
exports.TooltipTrigger = TooltipTrigger;
|
|
11497
|
+
exports.Tracker = Tracker;
|
|
9190
11498
|
exports.TypingIndicator = TypingIndicator;
|
|
9191
11499
|
exports.WritingAssistant = WritingAssistant;
|
|
9192
11500
|
exports.badgeVariants = badgeVariants;
|