@flanksource/clicky-ui 0.3.4 → 0.3.6
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/ai.cjs +15 -0
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +15 -0
- package/dist/ai.js.map +1 -1
- package/dist/data/ProgressBars.cjs +194 -0
- package/dist/data/ProgressBars.cjs.map +1 -0
- package/dist/data/ProgressBars.d.ts +85 -0
- package/dist/data/ProgressBars.d.ts.map +1 -0
- package/dist/data/ProgressBars.js +194 -0
- package/dist/data/ProgressBars.js.map +1 -0
- package/dist/data/ProgressBars.model.cjs +27 -0
- package/dist/data/ProgressBars.model.cjs.map +1 -0
- package/dist/data/ProgressBars.model.d.ts +20 -0
- package/dist/data/ProgressBars.model.d.ts.map +1 -0
- package/dist/data/ProgressBars.model.js +27 -0
- package/dist/data/ProgressBars.model.js.map +1 -0
- package/dist/data/TimeseriesCoreBars.cjs +19 -173
- package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
- package/dist/data/TimeseriesCoreBars.d.ts +9 -26
- package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
- package/dist/data/TimeseriesCoreBars.js +20 -174
- package/dist/data/TimeseriesCoreBars.js.map +1 -1
- package/dist/data/WorkloadCard.model.cjs.map +1 -1
- package/dist/data/WorkloadCard.model.d.ts +2 -2
- package/dist/data/WorkloadCard.model.d.ts.map +1 -1
- package/dist/data/WorkloadCard.model.js.map +1 -1
- package/dist/data/ai/SessionViewer.cjs +275 -0
- package/dist/data/ai/SessionViewer.cjs.map +1 -0
- package/dist/data/ai/SessionViewer.d.ts +33 -0
- package/dist/data/ai/SessionViewer.d.ts.map +1 -0
- package/dist/data/ai/SessionViewer.fixtures.d.ts +4 -0
- package/dist/data/ai/SessionViewer.fixtures.d.ts.map +1 -0
- package/dist/data/ai/SessionViewer.js +275 -0
- package/dist/data/ai/SessionViewer.js.map +1 -0
- package/dist/data/ai/SessionViewer.model.cjs +275 -0
- package/dist/data/ai/SessionViewer.model.cjs.map +1 -0
- package/dist/data/ai/SessionViewer.model.d.ts +87 -0
- package/dist/data/ai/SessionViewer.model.d.ts.map +1 -0
- package/dist/data/ai/SessionViewer.model.js +275 -0
- package/dist/data/ai/SessionViewer.model.js.map +1 -0
- package/dist/data/ai/SessionViewerMenu.cjs +190 -0
- package/dist/data/ai/SessionViewerMenu.cjs.map +1 -0
- package/dist/data/ai/SessionViewerMenu.d.ts +24 -0
- package/dist/data/ai/SessionViewerMenu.d.ts.map +1 -0
- package/dist/data/ai/SessionViewerMenu.js +190 -0
- package/dist/data/ai/SessionViewerMenu.js.map +1 -0
- package/dist/data/ai/index.d.ts +3 -0
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/ai/session-categories.cjs +219 -0
- package/dist/data/ai/session-categories.cjs.map +1 -0
- package/dist/data/ai/session-categories.d.ts +36 -0
- package/dist/data/ai/session-categories.d.ts.map +1 -0
- package/dist/data/ai/session-categories.js +219 -0
- package/dist/data/ai/session-categories.js.map +1 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.cjs.map +1 -1
- package/dist/data/version-info.js +3 -3
- package/dist/data/version-info.js.map +1 -1
- package/dist/data.cjs +4 -2
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +3 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +4 -2
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/data/TimeseriesCoreBars.model.cjs +0 -27
- package/dist/data/TimeseriesCoreBars.model.cjs.map +0 -1
- package/dist/data/TimeseriesCoreBars.model.d.ts +0 -20
- package/dist/data/TimeseriesCoreBars.model.d.ts.map +0 -1
- package/dist/data/TimeseriesCoreBars.model.js +0 -27
- package/dist/data/TimeseriesCoreBars.model.js.map +0 -1
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { jsx
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useQueries } from "@tanstack/react-query";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import { Icon } from "./Icon.js";
|
|
6
|
-
import { deriveCoreBars } from "./TimeseriesCoreBars.model.js";
|
|
7
|
-
import { GaugeHoverCard } from "./GaugeHoverCard.js";
|
|
4
|
+
import { ProgressBars } from "./ProgressBars.js";
|
|
8
5
|
import { seriesStats } from "./gauge-stats.js";
|
|
9
6
|
const defaultFetcher = async (url) => {
|
|
10
7
|
const res = await fetch(url);
|
|
@@ -17,36 +14,6 @@ function latestValue(resp) {
|
|
|
17
14
|
if (!points || points.length === 0) return void 0;
|
|
18
15
|
return (_a = points[points.length - 1]) == null ? void 0 : _a.value;
|
|
19
16
|
}
|
|
20
|
-
function toneClass(pct, [warn, danger]) {
|
|
21
|
-
if (pct >= danger) return "bg-red-500";
|
|
22
|
-
if (pct >= warn) return "bg-amber-500";
|
|
23
|
-
return "bg-emerald-500";
|
|
24
|
-
}
|
|
25
|
-
function CoreBarsIcon({ icon }) {
|
|
26
|
-
if (typeof icon === "string") {
|
|
27
|
-
return /* @__PURE__ */ jsx(
|
|
28
|
-
Icon,
|
|
29
|
-
{
|
|
30
|
-
name: icon,
|
|
31
|
-
width: 14,
|
|
32
|
-
height: 14,
|
|
33
|
-
className: "text-muted-foreground"
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return /* @__PURE__ */ jsx(
|
|
38
|
-
Icon,
|
|
39
|
-
{
|
|
40
|
-
icon,
|
|
41
|
-
width: 14,
|
|
42
|
-
height: 14,
|
|
43
|
-
className: "text-muted-foreground"
|
|
44
|
-
}
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
function formatCores(cores) {
|
|
48
|
-
return Number.isInteger(cores) ? String(cores) : cores.toFixed(1);
|
|
49
|
-
}
|
|
50
17
|
function TimeseriesCoreBars({
|
|
51
18
|
baseUrl = "",
|
|
52
19
|
value,
|
|
@@ -67,10 +34,6 @@ function TimeseriesCoreBars({
|
|
|
67
34
|
className
|
|
68
35
|
}) {
|
|
69
36
|
var _a, _b, _c, _d;
|
|
70
|
-
const perBar = (unit == null ? void 0 : unit.perBar) ?? 1e3;
|
|
71
|
-
const unitLabel = (unit == null ? void 0 : unit.label) ?? "cores";
|
|
72
|
-
const barLabel = (unit == null ? void 0 : unit.barLabel) ?? "core";
|
|
73
|
-
const formatUnits = (unit == null ? void 0 : unit.format) ?? formatCores;
|
|
74
37
|
const maxIsSeries = typeof max === "object";
|
|
75
38
|
const maxSeries = maxIsSeries ? max : void 0;
|
|
76
39
|
const ids = useMemo(() => {
|
|
@@ -101,144 +64,27 @@ function TimeseriesCoreBars({
|
|
|
101
64
|
const rawMax = latestValue((_b = results[1]) == null ? void 0 : _b.data);
|
|
102
65
|
limit = rawMax === void 0 ? void 0 : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;
|
|
103
66
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const capacityText = model.limitCores !== void 0 ? formatUnits(model.limitCores) : "?";
|
|
108
|
-
const caption = !model.hasUsage ? "—" : showCapacity ? `${usageText} / ${capacityText} ${unitLabel}` : `${usageText} ${unitLabel}`;
|
|
109
|
-
const compactCaption = !model.hasUsage ? "—" : showCapacity ? `${usageText}/${capacityText} ${unitLabel}` : `${usageText} ${unitLabel}`;
|
|
110
|
-
const titleText = `${title}: ${compactCaption}`;
|
|
111
|
-
const isCell = variant === "cell";
|
|
112
|
-
const pctTone = model.limitCores === void 0 ? void 0 : model.pct >= thresholds[1] ? "text-red-500" : model.pct >= thresholds[0] ? "text-amber-500" : "text-emerald-500";
|
|
113
|
-
const stats = seriesStats(
|
|
114
|
-
(_d = (_c = results[0]) == null ? void 0 : _c.data) == null ? void 0 : _d.points,
|
|
115
|
-
(v) => (value.transform ? value.transform(v) : v) / perBar
|
|
116
|
-
);
|
|
117
|
-
const fmt = (n) => `${formatUnits(n)} ${unitLabel}`;
|
|
118
|
-
const hoverRows = model.hasUsage ? [
|
|
119
|
-
{ label: "Current", value: fmt(model.usageCores), ...pctTone ? { tone: pctTone } : {} },
|
|
120
|
-
...stats ? [
|
|
121
|
-
{ label: "Min", value: fmt(stats.min) },
|
|
122
|
-
{ label: "Max", value: fmt(stats.max) },
|
|
123
|
-
{ label: "Avg", value: fmt(stats.avg) }
|
|
124
|
-
] : [],
|
|
125
|
-
...model.limitCores !== void 0 ? [{ label: "Capacity", value: fmt(model.limitCores) }] : []
|
|
126
|
-
] : [];
|
|
127
|
-
const resolvedOrientation = orientation ?? "vertical";
|
|
128
|
-
const isDense = model.coreCount > 4;
|
|
129
|
-
const valueText = isCell ? compactCaption : caption;
|
|
130
|
-
const hiddenAccessibleText = !showLabel || !showValue ? titleText : void 0;
|
|
131
|
-
const bars = /* @__PURE__ */ jsx(
|
|
132
|
-
"span",
|
|
133
|
-
{
|
|
134
|
-
className: cn(
|
|
135
|
-
"flex shrink-0",
|
|
136
|
-
resolvedOrientation === "vertical" ? cn("items-end", isCell ? "h-4 gap-px" : "h-10 gap-0.5") : cn(
|
|
137
|
-
"flex-col justify-center",
|
|
138
|
-
isCell ? "h-4 w-10 gap-px" : "h-10 w-16 gap-0.5"
|
|
139
|
-
)
|
|
140
|
-
),
|
|
141
|
-
"aria-hidden": isCell ? "true" : void 0,
|
|
142
|
-
"data-orientation": resolvedOrientation,
|
|
143
|
-
"data-core-count": model.coreCount,
|
|
144
|
-
"data-core-density": isDense ? "compact" : "default",
|
|
145
|
-
children: model.bars.map((bar, i) => {
|
|
146
|
-
const corePct = Math.round(bar.fill * 100);
|
|
147
|
-
return /* @__PURE__ */ jsx(
|
|
148
|
-
"span",
|
|
149
|
-
{
|
|
150
|
-
className: cn(
|
|
151
|
-
"relative overflow-hidden bg-muted",
|
|
152
|
-
resolvedOrientation === "vertical" ? cn(
|
|
153
|
-
"h-full",
|
|
154
|
-
isCell ? isDense ? "w-[3px] rounded-[2px]" : "w-1.5 rounded-[2px]" : isDense ? "w-1 rounded-sm" : "w-2 rounded-sm"
|
|
155
|
-
) : cn("min-h-px w-full flex-1 rounded-[2px]")
|
|
156
|
-
),
|
|
157
|
-
title: `${barLabel} ${i + 1}: ${corePct}%`,
|
|
158
|
-
"aria-label": !isCell ? `${barLabel} ${i + 1}: ${corePct}%` : void 0,
|
|
159
|
-
"data-fill": bar.fill,
|
|
160
|
-
children: /* @__PURE__ */ jsx(
|
|
161
|
-
"span",
|
|
162
|
-
{
|
|
163
|
-
className: cn(
|
|
164
|
-
"absolute bottom-0 left-0 transition-all duration-300",
|
|
165
|
-
tone
|
|
166
|
-
),
|
|
167
|
-
style: resolvedOrientation === "vertical" ? { height: `${corePct}%`, right: 0 } : { width: `${corePct}%`, top: 0 }
|
|
168
|
-
}
|
|
169
|
-
)
|
|
170
|
-
},
|
|
171
|
-
i
|
|
172
|
-
);
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
);
|
|
176
|
-
const valueNode = showValue ? /* @__PURE__ */ jsx(
|
|
177
|
-
"span",
|
|
178
|
-
{
|
|
179
|
-
className: cn(
|
|
180
|
-
"shrink-0 font-semibold tabular-nums text-foreground",
|
|
181
|
-
isCell ? null : "text-xs"
|
|
182
|
-
),
|
|
183
|
-
children: valueText
|
|
184
|
-
}
|
|
185
|
-
) : null;
|
|
186
|
-
const label = icon || showLabel ? /* @__PURE__ */ jsxs(
|
|
187
|
-
"span",
|
|
188
|
-
{
|
|
189
|
-
className: cn(
|
|
190
|
-
"inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground"
|
|
191
|
-
),
|
|
192
|
-
children: [
|
|
193
|
-
icon ? /* @__PURE__ */ jsx(CoreBarsIcon, { icon }) : null,
|
|
194
|
-
showLabel ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: title }) : null
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
) : null;
|
|
198
|
-
const useHover = hoverCard && hoverRows.length > 0;
|
|
199
|
-
const footer = `over last ${range}`;
|
|
200
|
-
if (isCell) {
|
|
201
|
-
const cell = /* @__PURE__ */ jsxs(
|
|
202
|
-
"span",
|
|
203
|
-
{
|
|
204
|
-
className: cn(
|
|
205
|
-
"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
|
|
206
|
-
className
|
|
207
|
-
),
|
|
208
|
-
...useHover ? {} : { title: titleText },
|
|
209
|
-
"aria-label": useHover ? titleText : hiddenAccessibleText,
|
|
210
|
-
children: [
|
|
211
|
-
label,
|
|
212
|
-
bars,
|
|
213
|
-
valueNode
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
);
|
|
217
|
-
return useHover ? /* @__PURE__ */ jsx(GaugeHoverCard, { title, rows: hoverRows, footer, trigger: cell }) : cell;
|
|
218
|
-
}
|
|
219
|
-
const block = /* @__PURE__ */ jsxs(
|
|
220
|
-
"div",
|
|
221
|
-
{
|
|
222
|
-
className: cn("flex flex-col items-center gap-1", className),
|
|
223
|
-
...useHover ? {} : { title: hiddenAccessibleText },
|
|
224
|
-
"aria-label": useHover ? titleText : hiddenAccessibleText,
|
|
225
|
-
children: [
|
|
226
|
-
bars,
|
|
227
|
-
valueNode,
|
|
228
|
-
label
|
|
229
|
-
]
|
|
230
|
-
}
|
|
231
|
-
);
|
|
232
|
-
return useHover ? /* @__PURE__ */ jsx(
|
|
233
|
-
GaugeHoverCard,
|
|
67
|
+
const stats = seriesStats((_d = (_c = results[0]) == null ? void 0 : _c.data) == null ? void 0 : _d.points, value.transform);
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
ProgressBars,
|
|
234
70
|
{
|
|
71
|
+
usage,
|
|
72
|
+
stats,
|
|
235
73
|
title,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
74
|
+
thresholds,
|
|
75
|
+
variant,
|
|
76
|
+
showLabel,
|
|
77
|
+
showValue,
|
|
78
|
+
showCapacity,
|
|
79
|
+
hoverCard,
|
|
80
|
+
hoverFooter: `over last ${range}`,
|
|
81
|
+
...limit !== void 0 ? { max: limit } : {},
|
|
82
|
+
...unit !== void 0 ? { unit } : {},
|
|
83
|
+
...icon !== void 0 ? { icon } : {},
|
|
84
|
+
...orientation !== void 0 ? { orientation } : {},
|
|
85
|
+
...className !== void 0 ? { className } : {}
|
|
240
86
|
}
|
|
241
|
-
)
|
|
87
|
+
);
|
|
242
88
|
}
|
|
243
89
|
export {
|
|
244
90
|
TimeseriesCoreBars
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeseriesCoreBars.js","sources":["../../src/data/TimeseriesCoreBars.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\nimport type { TimeseriesResponse } from \"./TimeseriesPanel\";\nimport { deriveCoreBars } from \"./TimeseriesCoreBars.model\";\nimport { GaugeHoverCard, type GaugeHoverRow } from \"./GaugeHoverCard\";\nimport { seriesStats } from \"./gauge-stats\";\n\nexport type TimeseriesCoreBarsVariant = \"default\" | \"cell\";\nexport type TimeseriesCoreBarsOrientation = \"horizontal\" | \"vertical\";\n\n/**\n * Describes what one bar represents, letting the same component render CPU cores,\n * memory gigabytes, or any other quantised capacity. Defaults model CPU cores.\n */\nexport interface CoreBarsUnit {\n /** Raw value that fills one bar, in the domain of `value`/`max`. Default 1000 (millicores → 1 core). */\n perBar?: number;\n /** Caption/tooltip suffix naming the unit, plural. Default \"cores\". */\n label?: string;\n /** Per-bar hover tooltip noun, singular. Default \"core\". */\n barLabel?: string;\n /** Format a bar-unit count for the caption. Default: integers as-is, else 1 decimal. */\n format?: (units: number) => string;\n}\n\nexport interface TimeseriesCoreBarsProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value (CPU millicores) fills the bars. */\n value: GaugeSeries;\n /**\n * The capacity. Either a `.limit`-style metric series (its latest value) or a\n * fixed number, in the same raw domain as `value`. Determines the number of\n * bars (one per `unit.perBar`). When omitted or 0, the bar count falls back to\n * the ceiling of the usage so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\n /** What one bar represents. Defaults to CPU cores (perBar 1000, label \"cores\"). */\n unit?: CoreBarsUnit;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Look-back window passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /**\n * Utilisation thresholds (percent of capacity) at which the fill turns amber\n * then red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: TimeseriesCoreBarsVariant;\n /** Show the title text. Icons and values remain visible unless separately hidden. */\n showLabel?: boolean;\n /** Show the value/capacity caption. Icons and bars remain visible when false. */\n showValue?: boolean;\n /**\n * Include the capacity in the caption (\"2.3 / 4 cores\"). Defaults to false — the\n * caption shows only the current value (\"2.3 cores\") since the bars already\n * convey capacity. Set true to append the \"/ <capacity>\".\n */\n showCapacity?: boolean;\n /** Bar direction. Vertical bars fill bottom-up; horizontal bars fill left-to-right. */\n orientation?: TimeseriesCoreBarsOrientation;\n /**\n * Wrap the bars in a hover card summarising the metric (label + current / min /\n * max / avg / capacity over the window). Defaults to true.\n */\n hoverCard?: boolean;\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"bg-red-500\";\n if (pct >= warn) return \"bg-amber-500\";\n return \"bg-emerald-500\";\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return (\n <Icon\n name={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n }\n return (\n <Icon\n icon={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n}\n\nfunction formatCores(cores: number): string {\n return Number.isInteger(cores) ? String(cores) : cores.toFixed(1);\n}\n\n/**\n * TimeseriesCoreBars renders CPU utilisation as a row of vertical bars — one bar\n * per core (the ceiling of the CPU limit) — filled left to right by the latest\n * usage. The bar that straddles the usage boundary is partially filled (\"blocked\n * out\") to show the fractional core in use, and the trailing bars stay empty to\n * show headroom. Both usage and limit are read live from the timeseries store,\n * mirroring TimeseriesGauge's data plumbing; the bar colour crosses warning and\n * danger thresholds. A \"<used> / <cores>\" caption sits below with the icon+title.\n */\nexport function TimeseriesCoreBars({\n baseUrl = \"\",\n value,\n max,\n unit,\n title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\n variant = \"default\",\n showLabel = true,\n showValue = true,\n showCapacity = false,\n orientation,\n hoverCard = true,\n fetcher = defaultFetcher,\n className,\n}: TimeseriesCoreBarsProps) {\n const perBar = unit?.perBar ?? 1000;\n const unitLabel = unit?.label ?? \"cores\";\n const barLabel = unit?.barLabel ?? \"core\";\n const formatUnits = unit?.format ?? formatCores;\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const usage =\n rawValue === undefined\n ? undefined\n : value.transform\n ? value.transform(rawValue)\n : rawValue;\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit =\n rawMax === undefined\n ? undefined\n : maxSeries.transform\n ? maxSeries.transform(rawMax)\n : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit, perBar);\n const tone = toneClass(model.pct, thresholds);\n const usageText = formatUnits(model.usageCores);\n const capacityText =\n model.limitCores !== undefined ? formatUnits(model.limitCores) : \"?\";\n const caption = !model.hasUsage\n ? \"—\"\n : showCapacity\n ? `${usageText} / ${capacityText} ${unitLabel}`\n : `${usageText} ${unitLabel}`;\n const compactCaption = !model.hasUsage\n ? \"—\"\n : showCapacity\n ? `${usageText}/${capacityText} ${unitLabel}`\n : `${usageText} ${unitLabel}`;\n const titleText = `${title}: ${compactCaption}`;\n const isCell = variant === \"cell\";\n\n // Hover card: reduce the fetched points to current/min/max/avg in bar-units\n // (raw value transformed, then divided by perBar) so they read in the same unit\n // as the caption. A bounded reading colours the current row by its threshold.\n const pctTone =\n model.limitCores === undefined\n ? undefined\n : model.pct >= thresholds[1]\n ? \"text-red-500\"\n : model.pct >= thresholds[0]\n ? \"text-amber-500\"\n : \"text-emerald-500\";\n const stats = seriesStats(results[0]?.data?.points, (v) =>\n (value.transform ? value.transform(v) : v) / perBar,\n );\n const fmt = (n: number) => `${formatUnits(n)} ${unitLabel}`;\n const hoverRows: GaugeHoverRow[] = model.hasUsage\n ? [\n { label: \"Current\", value: fmt(model.usageCores), ...(pctTone ? { tone: pctTone } : {}) },\n ...(stats\n ? [\n { label: \"Min\", value: fmt(stats.min) },\n { label: \"Max\", value: fmt(stats.max) },\n { label: \"Avg\", value: fmt(stats.avg) },\n ]\n : []),\n ...(model.limitCores !== undefined\n ? [{ label: \"Capacity\", value: fmt(model.limitCores) }]\n : []),\n ]\n : [];\n\n const resolvedOrientation = orientation ?? \"vertical\";\n const isDense = model.coreCount > 4;\n const valueText = isCell ? compactCaption : caption;\n const hiddenAccessibleText = !showLabel || !showValue ? titleText : undefined;\n const bars = (\n <span\n className={cn(\n \"flex shrink-0\",\n resolvedOrientation === \"vertical\"\n ? cn(\"items-end\", isCell ? \"h-4 gap-px\" : \"h-10 gap-0.5\")\n : cn(\n \"flex-col justify-center\",\n isCell ? \"h-4 w-10 gap-px\" : \"h-10 w-16 gap-0.5\",\n ),\n )}\n aria-hidden={isCell ? \"true\" : undefined}\n data-orientation={resolvedOrientation}\n data-core-count={model.coreCount}\n data-core-density={isDense ? \"compact\" : \"default\"}\n >\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <span\n key={i}\n className={cn(\n \"relative overflow-hidden bg-muted\",\n resolvedOrientation === \"vertical\"\n ? cn(\n \"h-full\",\n isCell\n ? isDense\n ? \"w-[3px] rounded-[2px]\"\n : \"w-1.5 rounded-[2px]\"\n : isDense\n ? \"w-1 rounded-sm\"\n : \"w-2 rounded-sm\",\n )\n : cn(\"min-h-px w-full flex-1 rounded-[2px]\"),\n )}\n title={`${barLabel} ${i + 1}: ${corePct}%`}\n aria-label={!isCell ? `${barLabel} ${i + 1}: ${corePct}%` : undefined}\n data-fill={bar.fill}\n >\n <span\n className={cn(\n \"absolute bottom-0 left-0 transition-all duration-300\",\n tone,\n )}\n style={\n resolvedOrientation === \"vertical\"\n ? { height: `${corePct}%`, right: 0 }\n : { width: `${corePct}%`, top: 0 }\n }\n />\n </span>\n );\n })}\n </span>\n );\n const valueNode = showValue ? (\n <span\n className={cn(\n \"shrink-0 font-semibold tabular-nums text-foreground\",\n isCell ? null : \"text-xs\",\n )}\n >\n {valueText}\n </span>\n ) : null;\n const label =\n icon || showLabel ? (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground\",\n )}\n >\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n {showLabel ? <span className=\"min-w-0 truncate\">{title}</span> : null}\n </span>\n ) : null;\n\n const useHover = hoverCard && hoverRows.length > 0;\n const footer = `over last ${range}`;\n\n if (isCell) {\n const cell = (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs\",\n className,\n )}\n {...(useHover ? {} : { title: titleText })}\n aria-label={useHover ? titleText : hiddenAccessibleText}\n >\n {label}\n {bars}\n {valueNode}\n </span>\n );\n return useHover ? (\n <GaugeHoverCard title={title} rows={hoverRows} footer={footer} trigger={cell} />\n ) : (\n cell\n );\n }\n\n const block = (\n <div\n className={cn(\"flex flex-col items-center gap-1\", className)}\n {...(useHover ? {} : { title: hiddenAccessibleText })}\n aria-label={useHover ? titleText : hiddenAccessibleText}\n >\n {bars}\n {valueNode}\n {label}\n </div>\n );\n return useHover ? (\n <GaugeHoverCard\n title={title}\n rows={hoverRows}\n footer={footer}\n trigger={block}\n className=\"w-full justify-center\"\n />\n ) : (\n block\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeseriesCoreBars.js","sources":["../../src/data/TimeseriesCoreBars.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { type StaticIconComponent } from \"./Icon\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\nimport type { TimeseriesResponse } from \"./TimeseriesPanel\";\nimport {\n ProgressBars,\n type ProgressBarsOrientation,\n type ProgressBarsUnit,\n type ProgressBarsVariant,\n} from \"./ProgressBars\";\nimport { seriesStats } from \"./gauge-stats\";\n\nexport interface TimeseriesCoreBarsProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value (CPU millicores) fills the bars. */\n value: GaugeSeries;\n /**\n * The capacity. Either a `.limit`-style metric series (its latest value) or a\n * fixed number, in the same raw domain as `value`. Determines the number of\n * bars (one per `unit.perBar`). When omitted or 0, the bar count falls back to\n * the ceiling of the usage so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\n /** What one bar represents. Defaults to CPU cores (perBar 1000, label \"cores\"). */\n unit?: ProgressBarsUnit;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Look-back window passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /**\n * Utilisation thresholds (percent of capacity) at which the fill turns amber\n * then red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: ProgressBarsVariant;\n /** Show the title text. Icons and values remain visible unless separately hidden. */\n showLabel?: boolean;\n /** Show the value/capacity caption. Icons and bars remain visible when false. */\n showValue?: boolean;\n /**\n * Include the capacity in the caption (\"2.3 / 4 cores\"). Defaults to false — the\n * caption shows only the current value (\"2.3 cores\") since the bars already\n * convey capacity. Set true to append the \"/ <capacity>\".\n */\n showCapacity?: boolean;\n /** Bar direction. Vertical bars fill bottom-up; horizontal bars fill left-to-right. */\n orientation?: ProgressBarsOrientation;\n /**\n * Wrap the bars in a hover card summarising the metric (label + current / min /\n * max / avg / capacity over the window). Defaults to true.\n */\n hoverCard?: boolean;\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\n/**\n * TimeseriesCoreBars reads usage and limit live from the timeseries store\n * (mirroring TimeseriesGauge's data plumbing) and renders them as quantised\n * utilisation bars via {@link ProgressBars}. Usage and limit flow through each\n * series' optional `transform`; the hover card summarises current/min/max/avg/\n * capacity over the look-back window.\n */\nexport function TimeseriesCoreBars({\n baseUrl = \"\",\n value,\n max,\n unit,\n title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\n variant = \"default\",\n showLabel = true,\n showValue = true,\n showCapacity = false,\n orientation,\n hoverCard = true,\n fetcher = defaultFetcher,\n className,\n}: TimeseriesCoreBarsProps) {\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const usage =\n rawValue === undefined\n ? undefined\n : value.transform\n ? value.transform(rawValue)\n : rawValue;\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit =\n rawMax === undefined\n ? undefined\n : maxSeries.transform\n ? maxSeries.transform(rawMax)\n : rawMax;\n }\n\n const stats = seriesStats(results[0]?.data?.points, value.transform);\n\n return (\n <ProgressBars\n usage={usage}\n stats={stats}\n title={title}\n thresholds={thresholds}\n variant={variant}\n showLabel={showLabel}\n showValue={showValue}\n showCapacity={showCapacity}\n hoverCard={hoverCard}\n hoverFooter={`over last ${range}`}\n {...(limit !== undefined ? { max: limit } : {})}\n {...(unit !== undefined ? { unit } : {})}\n {...(icon !== undefined ? { icon } : {})}\n {...(orientation !== undefined ? { orientation } : {})}\n {...(className !== undefined ? { className } : {})}\n />\n );\n}\n"],"names":[],"mappings":";;;;;AA+DA,MAAM,iBAAiB,OAAO,QAA6C;AACzE,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AACpE,SAAO,IAAI,KAAA;AACb;AAEA,SAAS,YAAY,MAA0D;;AAC7E,QAAM,SAAS,6BAAM;AACrB,MAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO;AAC3C,UAAO,YAAO,OAAO,SAAS,CAAC,MAAxB,mBAA2B;AACpC;AASO,SAAS,mBAAmB;AAAA,EACjC,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa,CAAC,IAAI,EAAE;AAAA,EACpB,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,eAAe;AAAA,EACf;AAAA,EACA,YAAY;AAAA,EACZ,UAAU;AAAA,EACV;AACF,GAA4B;;AAC1B,QAAM,cAAc,OAAO,QAAQ;AACnC,QAAM,YAAY,cAAc,MAAM;AACtC,QAAM,MAAM,QAAQ,MAAM;AACxB,UAAM,OAAO,CAAC,MAAM,EAAE;AACtB,QAAI,UAAW,MAAK,KAAK,UAAU,EAAE;AACrC,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC;AAExB,QAAM,UAAU,WAAW;AAAA,IACzB,SAAS,IAAI,IAAI,CAAC,OAAO;AACvB,YAAM,IAAI,IAAI,IAAI,UAAU,IAAI,OAAO,SAAS,MAAM;AACtD,UAAI,MAAO,GAAE,aAAa,IAAI,SAAS,KAAK;AAC5C,YAAM,aAAa,EAAE,WAAW,EAAE;AAClC,aAAO;AAAA,QACL,UAAU,CAAC,cAAc,UAAU;AAAA,QACnC,SAAS,MAAM,QAAQ,UAAU;AAAA,QACjC,iBAAiB,YAAY,IAAI,YAAY;AAAA,QAC7C,WAAW;AAAA,QACX,OAAO;AAAA,MAAA;AAAA,IAEX,CAAC;AAAA,EAAA,CACF;AAED,QAAM,WAAW,aAAY,aAAQ,CAAC,MAAT,mBAAY,IAAI;AAC7C,QAAM,QACJ,aAAa,SACT,SACA,MAAM,YACJ,MAAM,UAAU,QAAQ,IACxB;AAER,MAAI;AACJ,MAAI,OAAO,QAAQ,UAAU;AAC3B,YAAQ;AAAA,EACV,WAAW,WAAW;AACpB,UAAM,SAAS,aAAY,aAAQ,CAAC,MAAT,mBAAY,IAAI;AAC3C,YACE,WAAW,SACP,SACA,UAAU,YACR,UAAU,UAAU,MAAM,IAC1B;AAAA,EACV;AAEA,QAAM,QAAQ,aAAY,mBAAQ,CAAC,MAAT,mBAAY,SAAZ,mBAAkB,QAAQ,MAAM,SAAS;AAEnE,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,aAAa,KAAK;AAAA,MAC9B,GAAI,UAAU,SAAY,EAAE,KAAK,MAAA,IAAU,CAAA;AAAA,MAC3C,GAAI,SAAS,SAAY,EAAE,KAAA,IAAS,CAAA;AAAA,MACpC,GAAI,SAAS,SAAY,EAAE,KAAA,IAAS,CAAA;AAAA,MACpC,GAAI,gBAAgB,SAAY,EAAE,YAAA,IAAgB,CAAA;AAAA,MAClD,GAAI,cAAc,SAAY,EAAE,cAAc,CAAA;AAAA,IAAC;AAAA,EAAA;AAGtD;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkloadCard.model.cjs","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type {
|
|
1
|
+
{"version":3,"file":"WorkloadCard.model.cjs","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n label?: string;\n code?: string;\n health?: string;\n message?: string;\n tone?: BadgeTone;\n}\n\nexport interface WorkloadCardWorkload {\n kind: WorkloadCardKind;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n const token = [status?.label, status?.code, status?.health]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n if (!token) return \"neutral\";\n if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))\n return \"danger\";\n if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))\n return \"warning\";\n if (/(healthy|ready|running|available|ok|success)/.test(token))\n return \"success\";\n if (/(info|unknown)/.test(token)) return \"info\";\n return \"neutral\";\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":";;AA8DO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAEO,SAAS,mBACd,QACW;AACX,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,QAAM,QAAQ,CAAC,iCAAQ,OAAO,iCAAQ,MAAM,iCAAQ,MAAM,EACvD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA;AACH,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,0DAA0D,KAAK,KAAK;AACtE,WAAO;AACT,MAAI,6DAA6D,KAAK,KAAK;AACzE,WAAO;AACT,MAAI,+CAA+C,KAAK,KAAK;AAC3D,WAAO;AACT,MAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BadgeTone } from './Badge';
|
|
2
2
|
import { StaticIconComponent } from './Icon';
|
|
3
|
-
import {
|
|
3
|
+
import { ProgressBarsOrientation } from './ProgressBars';
|
|
4
4
|
import { GaugeSeries } from './TimeseriesGauge';
|
|
5
5
|
export type WorkloadCardKind = "service" | "ingress" | "deployment" | "statefulset" | "pod";
|
|
6
6
|
export type WorkloadCardIcon = string | StaticIconComponent;
|
|
@@ -38,7 +38,7 @@ export interface WorkloadCardResourceMetric {
|
|
|
38
38
|
centerDisplay?: "value" | "percent";
|
|
39
39
|
}
|
|
40
40
|
export interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {
|
|
41
|
-
orientation?:
|
|
41
|
+
orientation?: ProgressBarsOrientation;
|
|
42
42
|
showValue?: boolean;
|
|
43
43
|
}
|
|
44
44
|
export interface WorkloadCardMetrics {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkloadCard.model.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"WorkloadCard.model.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,SAAS,GACT,YAAY,GACZ,aAAa,GACb,KAAK,CAAC;AAEV,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,mBAAmB,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,qBAAsB,SAAQ,0BAA0B;IACvE,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,qBAAqB,CAAC;IAC5B,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,IAAI,CAAC,EAAE,0BAA0B,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAahE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,SAAS,CAeX;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,GACzC,MAAM,GAAG,SAAS,CAapB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkloadCard.model.js","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type {
|
|
1
|
+
{"version":3,"file":"WorkloadCard.model.js","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n label?: string;\n code?: string;\n health?: string;\n message?: string;\n tone?: BadgeTone;\n}\n\nexport interface WorkloadCardWorkload {\n kind: WorkloadCardKind;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n const token = [status?.label, status?.code, status?.health]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n if (!token) return \"neutral\";\n if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))\n return \"danger\";\n if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))\n return \"warning\";\n if (/(healthy|ready|running|available|ok|success)/.test(token))\n return \"success\";\n if (/(info|unknown)/.test(token)) return \"info\";\n return \"neutral\";\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":"AA8DO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAEO,SAAS,mBACd,QACW;AACX,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,QAAM,QAAQ,CAAC,iCAAQ,OAAO,iCAAQ,MAAM,iCAAQ,MAAM,EACvD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA;AACH,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,0DAA0D,KAAK,KAAK;AACtE,WAAO;AACT,MAAI,6DAA6D,KAAK,KAAK;AACzE,WAAO;AACT,MAAI,+CAA+C,KAAK,KAAK;AAC3D,WAAO;AACT,MAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
const Icon = require("../Icon.cjs");
|
|
7
|
+
const CodeBlock = require("../CodeBlock.cjs");
|
|
8
|
+
const useDensity = require("../../hooks/use-density.cjs");
|
|
9
|
+
const densityProvider = require("../../hooks/density-provider.cjs");
|
|
10
|
+
const SessionViewer_model = require("./SessionViewer.model.cjs");
|
|
11
|
+
const sessionCategories = require("./session-categories.cjs");
|
|
12
|
+
const SessionViewerMenu = require("./SessionViewerMenu.cjs");
|
|
13
|
+
const UiRobotAi = require("../../icons/components/UiRobotAi.cjs");
|
|
14
|
+
const UiUserCircle = require("../../icons/components/UiUserCircle.cjs");
|
|
15
|
+
const UiWarningTriangle = require("../../icons/components/UiWarningTriangle.cjs");
|
|
16
|
+
const UiBrain = require("../../icons/components/UiBrain.cjs");
|
|
17
|
+
const UiSparkles = require("../../icons/components/UiSparkles.cjs");
|
|
18
|
+
const UiChevronDown = require("../../icons/components/UiChevronDown.cjs");
|
|
19
|
+
const DISC_TONE = {
|
|
20
|
+
sky: "bg-sky-100 text-sky-700 [[data-theme=dark]_&]:bg-sky-500/15 [[data-theme=dark]_&]:text-sky-300",
|
|
21
|
+
amber: "bg-amber-100 text-amber-700 [[data-theme=dark]_&]:bg-amber-500/15 [[data-theme=dark]_&]:text-amber-300",
|
|
22
|
+
violet: "bg-violet-100 text-violet-700 [[data-theme=dark]_&]:bg-violet-500/15 [[data-theme=dark]_&]:text-violet-300",
|
|
23
|
+
emerald: "bg-emerald-100 text-emerald-700 [[data-theme=dark]_&]:bg-emerald-500/15 [[data-theme=dark]_&]:text-emerald-300",
|
|
24
|
+
rose: "bg-rose-100 text-rose-700 [[data-theme=dark]_&]:bg-rose-500/15 [[data-theme=dark]_&]:text-rose-300",
|
|
25
|
+
indigo: "bg-indigo-100 text-indigo-700 [[data-theme=dark]_&]:bg-indigo-500/15 [[data-theme=dark]_&]:text-indigo-300",
|
|
26
|
+
fuchsia: "bg-fuchsia-100 text-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-500/15 [[data-theme=dark]_&]:text-fuchsia-300",
|
|
27
|
+
pink: "bg-pink-100 text-pink-700 [[data-theme=dark]_&]:bg-pink-500/15 [[data-theme=dark]_&]:text-pink-300",
|
|
28
|
+
slate: "bg-muted text-muted-foreground"
|
|
29
|
+
};
|
|
30
|
+
function toggleInSet(set, value) {
|
|
31
|
+
const next = new Set(set);
|
|
32
|
+
if (next.has(value)) next.delete(value);
|
|
33
|
+
else next.add(value);
|
|
34
|
+
return next;
|
|
35
|
+
}
|
|
36
|
+
function SessionViewer({
|
|
37
|
+
session,
|
|
38
|
+
className,
|
|
39
|
+
defaultExpanded = false,
|
|
40
|
+
showThinking = true,
|
|
41
|
+
showHeader = true,
|
|
42
|
+
showMenu = true,
|
|
43
|
+
defaultDensity,
|
|
44
|
+
defaultTheme
|
|
45
|
+
}) {
|
|
46
|
+
const allEvents = react.useMemo(() => SessionViewer_model.normalizeSession(session), [session]);
|
|
47
|
+
const pageDensity = useDensity.useDensityValue();
|
|
48
|
+
const [densityOverride, setDensityOverride] = react.useState(defaultDensity);
|
|
49
|
+
const [themeOverride, setThemeOverride] = react.useState(defaultTheme);
|
|
50
|
+
const [hiddenCategories, setHiddenCategories] = react.useState(
|
|
51
|
+
() => /* @__PURE__ */ new Set()
|
|
52
|
+
);
|
|
53
|
+
const [hiddenTools, setHiddenTools] = react.useState(() => /* @__PURE__ */ new Set());
|
|
54
|
+
const [hiddenSources, setHiddenSources] = react.useState(() => /* @__PURE__ */ new Set());
|
|
55
|
+
const [showThinkingOverride, setShowThinkingOverride] = react.useState(void 0);
|
|
56
|
+
const effectiveShowThinking = showThinkingOverride ?? showThinking;
|
|
57
|
+
const filters = react.useMemo(() => sessionCategories.collectSessionFilters(allEvents), [allEvents]);
|
|
58
|
+
const hasThinking = react.useMemo(() => allEvents.some((e) => e.kind === "thinking"), [allEvents]);
|
|
59
|
+
const visibility = { hiddenCategories, hiddenTools, hiddenSources, showThinking: effectiveShowThinking };
|
|
60
|
+
const events = allEvents.filter((event) => sessionCategories.isEventVisible(event, visibility));
|
|
61
|
+
if (allEvents.length === 0) {
|
|
62
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
63
|
+
"div",
|
|
64
|
+
{
|
|
65
|
+
className: utils.cn(
|
|
66
|
+
"rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground",
|
|
67
|
+
className
|
|
68
|
+
),
|
|
69
|
+
children: "No session activity."
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const summary = SessionViewer_model.summarizeSession(allEvents);
|
|
74
|
+
const effectiveDensity = densityOverride ?? pageDensity;
|
|
75
|
+
const dataAttrs = {
|
|
76
|
+
...densityOverride ? { "data-density": densityOverride } : {},
|
|
77
|
+
...themeOverride ? { "data-theme": themeOverride } : {}
|
|
78
|
+
};
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("text-sm", className), ...dataAttrs, children: /* @__PURE__ */ jsxRuntime.jsxs(densityProvider.DensityValueProvider, { density: effectiveDensity, children: [
|
|
80
|
+
(showHeader || showMenu) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-density-3 flex items-center justify-between gap-density-3", children: [
|
|
81
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-x-density-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
82
|
+
showHeader && summary.model && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 font-medium text-foreground", children: [
|
|
83
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiRobotAi.UiRobotAi, className: "size-3.5" }),
|
|
84
|
+
summary.model
|
|
85
|
+
] }),
|
|
86
|
+
showHeader && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
87
|
+
summary.toolCount,
|
|
88
|
+
" actions"
|
|
89
|
+
] }),
|
|
90
|
+
showHeader && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
91
|
+
summary.messageCount,
|
|
92
|
+
" messages"
|
|
93
|
+
] })
|
|
94
|
+
] }),
|
|
95
|
+
showMenu && /* @__PURE__ */ jsxRuntime.jsx(
|
|
96
|
+
SessionViewerMenu.SessionViewerMenu,
|
|
97
|
+
{
|
|
98
|
+
density: densityOverride,
|
|
99
|
+
onDensityChange: setDensityOverride,
|
|
100
|
+
theme: themeOverride,
|
|
101
|
+
onThemeChange: setThemeOverride,
|
|
102
|
+
filters,
|
|
103
|
+
hiddenCategories,
|
|
104
|
+
hiddenTools,
|
|
105
|
+
hiddenSources,
|
|
106
|
+
onToggleCategory: (category) => setHiddenCategories((set) => toggleInSet(set, category)),
|
|
107
|
+
onToggleTool: (tool) => setHiddenTools((set) => toggleInSet(set, tool)),
|
|
108
|
+
onToggleSource: (source) => setHiddenSources((set) => toggleInSet(set, source)),
|
|
109
|
+
showThinking: effectiveShowThinking,
|
|
110
|
+
onToggleThinking: () => setShowThinkingOverride(!effectiveShowThinking),
|
|
111
|
+
hasThinking
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }),
|
|
115
|
+
events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground", children: "All actions are hidden by the active filters." }) : /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "relative", children: events.map((event, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
116
|
+
SessionRow,
|
|
117
|
+
{
|
|
118
|
+
event,
|
|
119
|
+
last: index === events.length - 1,
|
|
120
|
+
defaultExpanded
|
|
121
|
+
},
|
|
122
|
+
event.id
|
|
123
|
+
)) })
|
|
124
|
+
] }) });
|
|
125
|
+
}
|
|
126
|
+
function SessionRow({
|
|
127
|
+
event,
|
|
128
|
+
last,
|
|
129
|
+
defaultExpanded
|
|
130
|
+
}) {
|
|
131
|
+
if (event.kind === "user") return /* @__PURE__ */ jsxRuntime.jsx(UserRow, { event });
|
|
132
|
+
const visual = eventVisual(event);
|
|
133
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { "data-event-kind": event.kind, className: "relative flex gap-density-3 pb-density-4 last:pb-0", children: [
|
|
134
|
+
!last && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute bottom-0 left-[10px] top-[22px] w-px bg-border" }),
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
136
|
+
"span",
|
|
137
|
+
{
|
|
138
|
+
className: utils.cn(
|
|
139
|
+
"relative z-[1] flex h-[21px] w-[21px] shrink-0 items-center justify-center rounded-full",
|
|
140
|
+
DISC_TONE[visual.tone]
|
|
141
|
+
),
|
|
142
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: visual.icon, className: "h-3 w-3" })
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1 pt-px", children: /* @__PURE__ */ jsxRuntime.jsx(EventBody, { event, visual, defaultExpanded }) })
|
|
146
|
+
] });
|
|
147
|
+
}
|
|
148
|
+
function UserRow({ event }) {
|
|
149
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { "data-event-kind": "user", className: "relative flex justify-end pb-density-4 last:pb-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-[85%] items-start gap-density-3", children: [
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-0.5 text-right text-xs font-medium text-muted-foreground", children: "You" }),
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words rounded-lg bg-accent px-density-3 py-density-2 text-right leading-relaxed text-accent-foreground", children: event.text })
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
155
|
+
"span",
|
|
156
|
+
{
|
|
157
|
+
className: utils.cn(
|
|
158
|
+
"relative z-[1] flex h-[21px] w-[21px] shrink-0 items-center justify-center rounded-full",
|
|
159
|
+
DISC_TONE.slate
|
|
160
|
+
),
|
|
161
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiUserCircle.UiUserCircle, className: "h-3 w-3" })
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
] }) });
|
|
165
|
+
}
|
|
166
|
+
function eventVisual(event) {
|
|
167
|
+
switch (event.kind) {
|
|
168
|
+
case "tool": {
|
|
169
|
+
const action = SessionViewer_model.getSessionAction(event.tool ?? "");
|
|
170
|
+
return { icon: action.icon, tone: action.tone, label: action.label };
|
|
171
|
+
}
|
|
172
|
+
case "user":
|
|
173
|
+
return { icon: UiUserCircle.UiUserCircle, tone: "slate", label: "User" };
|
|
174
|
+
case "assistant":
|
|
175
|
+
return { icon: UiSparkles.UiSparkles, tone: "indigo", label: "Assistant" };
|
|
176
|
+
case "thinking":
|
|
177
|
+
return { icon: UiBrain.UiBrain, tone: "slate", label: "Thinking" };
|
|
178
|
+
case "error":
|
|
179
|
+
return { icon: UiWarningTriangle.UiWarningTriangle, tone: "rose", label: "Error" };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function EventBody({
|
|
183
|
+
event,
|
|
184
|
+
visual,
|
|
185
|
+
defaultExpanded
|
|
186
|
+
}) {
|
|
187
|
+
if (event.kind === "tool") return /* @__PURE__ */ jsxRuntime.jsx(ToolBody, { event, label: visual.label, defaultExpanded });
|
|
188
|
+
if (event.kind === "thinking") return /* @__PURE__ */ jsxRuntime.jsx(ThinkingBody, { event });
|
|
189
|
+
if (event.kind === "error") return /* @__PURE__ */ jsxRuntime.jsx(ErrorBody, { event });
|
|
190
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessageBody, { event, label: visual.label });
|
|
191
|
+
}
|
|
192
|
+
function ToolBody({
|
|
193
|
+
event,
|
|
194
|
+
label,
|
|
195
|
+
defaultExpanded
|
|
196
|
+
}) {
|
|
197
|
+
const summary = SessionViewer_model.summarizeToolInput(event.tool ?? "", event.toolInput);
|
|
198
|
+
const hasDetail = event.toolInput !== void 0 || event.toolResponse !== void 0;
|
|
199
|
+
const [open, setOpen] = react.useState(defaultExpanded);
|
|
200
|
+
const header = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
201
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: label }),
|
|
202
|
+
summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono text-xs text-muted-foreground", children: summary })
|
|
203
|
+
] });
|
|
204
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "not-prose", children: [
|
|
205
|
+
hasDetail ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
206
|
+
"button",
|
|
207
|
+
{
|
|
208
|
+
type: "button",
|
|
209
|
+
"aria-expanded": open,
|
|
210
|
+
onClick: () => setOpen((value) => !value),
|
|
211
|
+
className: "flex w-full items-center gap-1.5 text-left hover:text-foreground",
|
|
212
|
+
children: [
|
|
213
|
+
header,
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
+
Icon.Icon,
|
|
216
|
+
{
|
|
217
|
+
icon: UiChevronDown.UiChevronDown,
|
|
218
|
+
className: utils.cn("ml-auto size-3 shrink-0 text-muted-foreground transition-transform", open && "rotate-180")
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: header }),
|
|
224
|
+
(event.model || event.source) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-0.5 flex gap-density-2 text-[11px] text-muted-foreground", children: [
|
|
225
|
+
event.source && /* @__PURE__ */ jsxRuntime.jsx("span", { children: event.source }),
|
|
226
|
+
event.model && /* @__PURE__ */ jsxRuntime.jsx("span", { children: event.model }),
|
|
227
|
+
event.reasoningEffort && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
228
|
+
"effort: ",
|
|
229
|
+
event.reasoningEffort
|
|
230
|
+
] })
|
|
231
|
+
] }),
|
|
232
|
+
open && hasDetail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 space-y-1.5", children: [
|
|
233
|
+
event.toolInput !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(DetailBlock, { language: "json", source: JSON.stringify(event.toolInput, null, 2) }),
|
|
234
|
+
event.toolResponse !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(ResponseBlock, { response: event.toolResponse })
|
|
235
|
+
] })
|
|
236
|
+
] });
|
|
237
|
+
}
|
|
238
|
+
function ResponseBlock({ response }) {
|
|
239
|
+
const trimmed = response.trim();
|
|
240
|
+
const isJson = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
241
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DetailBlock, { language: isJson ? "json" : "text", source: response });
|
|
242
|
+
}
|
|
243
|
+
function DetailBlock({ language, source }) {
|
|
244
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock.CodeBlock, { language, source }) });
|
|
245
|
+
}
|
|
246
|
+
function MessageBody({ event, label }) {
|
|
247
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-0.5 text-xs font-medium text-muted-foreground", children: label }),
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words leading-relaxed text-foreground", children: event.text })
|
|
250
|
+
] });
|
|
251
|
+
}
|
|
252
|
+
function ThinkingBody({ event }) {
|
|
253
|
+
const [open, setOpen] = react.useState(false);
|
|
254
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "not-prose", children: [
|
|
255
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
256
|
+
"button",
|
|
257
|
+
{
|
|
258
|
+
type: "button",
|
|
259
|
+
"aria-expanded": open,
|
|
260
|
+
onClick: () => setOpen((value) => !value),
|
|
261
|
+
className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground",
|
|
262
|
+
children: [
|
|
263
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "Reasoning" }),
|
|
264
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiChevronDown.UiChevronDown, className: utils.cn("size-3 transition-transform", open && "rotate-180") })
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
),
|
|
268
|
+
open && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 whitespace-pre-wrap break-words border-l-2 border-border pl-density-3 text-xs italic leading-relaxed text-muted-foreground", children: event.text })
|
|
269
|
+
] });
|
|
270
|
+
}
|
|
271
|
+
function ErrorBody({ event }) {
|
|
272
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-rose-200 bg-rose-50 px-density-3 py-1.5 text-xs text-rose-700 [[data-theme=dark]_&]:border-rose-500/30 [[data-theme=dark]_&]:bg-rose-500/10 [[data-theme=dark]_&]:text-rose-300", children: event.text });
|
|
273
|
+
}
|
|
274
|
+
exports.SessionViewer = SessionViewer;
|
|
275
|
+
//# sourceMappingURL=SessionViewer.cjs.map
|