@flanksource/clicky-ui 0.2.12 → 0.2.14
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/components/Combobox.cjs +47 -33
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts +12 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +48 -34
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/IconButton.cjs +26 -0
- package/dist/components/IconButton.cjs.map +1 -0
- package/dist/components/IconButton.d.ts +25 -0
- package/dist/components/IconButton.d.ts.map +1 -0
- package/dist/components/IconButton.js +26 -0
- package/dist/components/IconButton.js.map +1 -0
- package/dist/components/SecretKeySelector.cjs +151 -0
- package/dist/components/SecretKeySelector.cjs.map +1 -0
- package/dist/components/SecretKeySelector.d.ts +51 -0
- package/dist/components/SecretKeySelector.d.ts.map +1 -0
- package/dist/components/SecretKeySelector.js +151 -0
- package/dist/components/SecretKeySelector.js.map +1 -0
- package/dist/components/WorkloadPicker.cjs +144 -0
- package/dist/components/WorkloadPicker.cjs.map +1 -0
- package/dist/components/WorkloadPicker.d.ts +52 -0
- package/dist/components/WorkloadPicker.d.ts.map +1 -0
- package/dist/components/WorkloadPicker.js +144 -0
- package/dist/components/WorkloadPicker.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +83 -112
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.d.ts.map +1 -1
- package/dist/components/icon-menu-picker.js +85 -114
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +10 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +11 -1
- package/dist/components.js.map +1 -1
- package/dist/data/Clicky.cjs +1 -1
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +1 -1
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/TimeseriesCoreBars.cjs +8 -1
- package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
- package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
- package/dist/data/TimeseriesCoreBars.js +8 -1
- package/dist/data/TimeseriesCoreBars.js.map +1 -1
- package/dist/data/Version.cjs +42 -0
- package/dist/data/Version.cjs.map +1 -0
- package/dist/data/Version.d.ts +36 -0
- package/dist/data/Version.d.ts.map +1 -0
- package/dist/data/Version.js +42 -0
- package/dist/data/Version.js.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
- package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.js +165 -0
- package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
- package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.js +78 -0
- package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
- package/dist/data/test-runner/TestFilterBar.cjs +108 -0
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
- package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
- package/dist/data/test-runner/TestFilterBar.js +108 -0
- package/dist/data/test-runner/TestFilterBar.js.map +1 -0
- package/dist/data/test-runner/TestRunSummary.cjs +81 -0
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
- package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunSummary.js +81 -0
- package/dist/data/test-runner/TestRunSummary.js.map +1 -0
- package/dist/data/test-runner/TestRunner.cjs +143 -0
- package/dist/data/test-runner/TestRunner.cjs.map +1 -0
- package/dist/data/test-runner/TestRunner.d.ts +42 -0
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.js +143 -0
- package/dist/data/test-runner/TestRunner.js.map +1 -0
- package/dist/data/test-runner/TestTree.cjs +42 -0
- package/dist/data/test-runner/TestTree.cjs.map +1 -0
- package/dist/data/test-runner/TestTree.d.ts +9 -0
- package/dist/data/test-runner/TestTree.d.ts.map +1 -0
- package/dist/data/test-runner/TestTree.js +42 -0
- package/dist/data/test-runner/TestTree.js.map +1 -0
- package/dist/data/test-runner/TestTreeNode.cjs +99 -0
- package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
- package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
- package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
- package/dist/data/test-runner/TestTreeNode.js +99 -0
- package/dist/data/test-runner/TestTreeNode.js.map +1 -0
- package/dist/data/test-runner/adapter.cjs +57 -0
- package/dist/data/test-runner/adapter.cjs.map +1 -0
- package/dist/data/test-runner/adapter.d.ts +60 -0
- package/dist/data/test-runner/adapter.d.ts.map +1 -0
- package/dist/data/test-runner/adapter.js +57 -0
- package/dist/data/test-runner/adapter.js.map +1 -0
- package/dist/data/test-runner/context.cjs +15 -0
- package/dist/data/test-runner/context.cjs.map +1 -0
- package/dist/data/test-runner/context.d.ts +40 -0
- package/dist/data/test-runner/context.d.ts.map +1 -0
- package/dist/data/test-runner/context.js +15 -0
- package/dist/data/test-runner/context.js.map +1 -0
- package/dist/data/test-runner/filterState.cjs +47 -0
- package/dist/data/test-runner/filterState.cjs.map +1 -0
- package/dist/data/test-runner/filterState.d.ts +14 -0
- package/dist/data/test-runner/filterState.d.ts.map +1 -0
- package/dist/data/test-runner/filterState.js +47 -0
- package/dist/data/test-runner/filterState.js.map +1 -0
- package/dist/data/test-runner/frameworkIcon.cjs +27 -0
- package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
- package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
- package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
- package/dist/data/test-runner/frameworkIcon.js +27 -0
- package/dist/data/test-runner/frameworkIcon.js.map +1 -0
- package/dist/data/test-runner/index.d.ts +14 -0
- package/dist/data/test-runner/index.d.ts.map +1 -0
- package/dist/data/test-runner/status.cjs +234 -0
- package/dist/data/test-runner/status.cjs.map +1 -0
- package/dist/data/test-runner/status.d.ts +52 -0
- package/dist/data/test-runner/status.d.ts.map +1 -0
- package/dist/data/test-runner/status.js +234 -0
- package/dist/data/test-runner/status.js.map +1 -0
- package/dist/data/test-runner/types.d.ts +148 -0
- package/dist/data/test-runner/types.d.ts.map +1 -0
- package/dist/data/version-info.cjs +26 -0
- package/dist/data/version-info.cjs.map +1 -0
- package/dist/data/version-info.d.ts +11 -0
- package/dist/data/version-info.d.ts.map +1 -0
- package/dist/data/version-info.js +26 -0
- package/dist/data/version-info.js.map +1 -0
- package/dist/data.cjs +49 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +3 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +50 -1
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +59 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/overlay/DropdownMenu.cjs +57 -30
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +57 -30
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/overlay/Modal.cjs +62 -6
- package/dist/overlay/Modal.cjs.map +1 -1
- package/dist/overlay/Modal.d.ts +19 -1
- package/dist/overlay/Modal.d.ts.map +1 -1
- package/dist/overlay/Modal.js +62 -6
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/rpc/CommandForm.cjs +2 -2
- package/dist/rpc/CommandForm.cjs.map +1 -1
- package/dist/rpc/CommandForm.d.ts.map +1 -1
- package/dist/rpc/CommandForm.js +3 -3
- package/dist/rpc/CommandForm.js.map +1 -1
- package/dist/rpc/formMetadata.cjs +5 -4
- package/dist/rpc/formMetadata.cjs.map +1 -1
- package/dist/rpc/formMetadata.d.ts.map +1 -1
- package/dist/rpc/formMetadata.js +6 -5
- package/dist/rpc/formMetadata.js.map +1 -1
- package/dist/rpc/types.cjs +4 -0
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +10 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js +5 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -1
|
@@ -35,7 +35,14 @@ function deriveCoreBars(usageMilli, limitMilli) {
|
|
|
35
35
|
const bars = Array.from({ length: coreCount }, (_, i) => ({
|
|
36
36
|
fill: clamp01(usageCores - i)
|
|
37
37
|
}));
|
|
38
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
hasUsage,
|
|
40
|
+
usageCores,
|
|
41
|
+
coreCount,
|
|
42
|
+
pct,
|
|
43
|
+
bars,
|
|
44
|
+
...limitCores !== void 0 && { limitCores }
|
|
45
|
+
};
|
|
39
46
|
}
|
|
40
47
|
function CoreBarsIcon({ icon }) {
|
|
41
48
|
if (typeof icon === "string") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeseriesCoreBars.cjs","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\";\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 CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\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 /** 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\nexport interface CoreBar {\n /** Fraction of this core that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface CoreBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in cores (millicores / 1000). */\n usageCores: number;\n /** Capacity in cores; undefined when no limit was resolved. */\n limitCores?: number;\n /** Number of bars rendered (one per core). */\n coreCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-core fill fractions, left to right. */\n bars: CoreBar[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveCoreBars turns the latest usage/limit millicore readings into a per-core\n// bar model: one bar per core (ceil of the limit), filled left to right by usage\n// so the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly.\nexport function deriveCoreBars(\n usageMilli: number | undefined,\n limitMilli: number | undefined,\n): CoreBarsModel {\n const hasUsage = usageMilli !== undefined;\n const usageCores = (usageMilli ?? 0) / 1000;\n const hasLimit = limitMilli !== undefined && limitMilli > 0;\n const limitCores = hasLimit ? (limitMilli as number) / 1000 : undefined;\n\n const coreCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitCores as number) : usageCores),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageCores / (limitCores as number)) * 100))\n : 0;\n\n const bars: CoreBar[] = Array.from({ length: coreCount }, (_, i) => ({\n fill: clamp01(usageCores - i),\n }));\n\n return { hasUsage, usageCores, limitCores, coreCount, pct, bars };\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\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 title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\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 = rawValue === undefined ? undefined : value.transform ? value.transform(rawValue) : 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 = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"flex h-10 items-end gap-0.5\">\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <div\n key={i}\n className=\"relative h-full w-2 overflow-hidden rounded-sm bg-muted\"\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={`core ${i + 1}: ${corePct}%`}\n data-fill={bar.fill}\n >\n <div\n className={cn(\"absolute inset-x-0 bottom-0 transition-all duration-300\", tone)}\n style={{ height: `${corePct}%` }}\n />\n </div>\n );\n })}\n </div>\n <span className=\"text-xs font-semibold text-foreground\">{caption}</span>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n </div>\n );\n}\n"],"names":["jsx","Icon","useMemo","useQueries","cn","jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TimeseriesCoreBars.cjs","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\";\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 CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\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 /** 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\nexport interface CoreBar {\n /** Fraction of this core that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface CoreBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in cores (millicores / 1000). */\n usageCores: number;\n /** Capacity in cores; undefined when no limit was resolved. */\n limitCores?: number;\n /** Number of bars rendered (one per core). */\n coreCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-core fill fractions, left to right. */\n bars: CoreBar[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveCoreBars turns the latest usage/limit millicore readings into a per-core\n// bar model: one bar per core (ceil of the limit), filled left to right by usage\n// so the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly.\nexport function deriveCoreBars(\n usageMilli: number | undefined,\n limitMilli: number | undefined,\n): CoreBarsModel {\n const hasUsage = usageMilli !== undefined;\n const usageCores = (usageMilli ?? 0) / 1000;\n const hasLimit = limitMilli !== undefined && limitMilli > 0;\n const limitCores = hasLimit ? (limitMilli as number) / 1000 : undefined;\n\n const coreCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitCores as number) : usageCores),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageCores / (limitCores as number)) * 100))\n : 0;\n\n const bars: CoreBar[] = Array.from({ length: coreCount }, (_, i) => ({\n fill: clamp01(usageCores - i),\n }));\n\n // limitCores is an optional field under exactOptionalPropertyTypes — only set\n // the key when a limit resolved, rather than assigning an explicit undefined.\n return {\n hasUsage,\n usageCores,\n coreCount,\n pct,\n bars,\n ...(limitCores !== undefined && { limitCores }),\n };\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\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 title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\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 = rawValue === undefined ? undefined : value.transform ? value.transform(rawValue) : 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 = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"flex h-10 items-end gap-0.5\">\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <div\n key={i}\n className=\"relative h-full w-2 overflow-hidden rounded-sm bg-muted\"\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={`core ${i + 1}: ${corePct}%`}\n data-fill={bar.fill}\n >\n <div\n className={cn(\"absolute inset-x-0 bottom-0 transition-all duration-300\", tone)}\n style={{ height: `${corePct}%` }}\n />\n </div>\n );\n })}\n </div>\n <span className=\"text-xs font-semibold text-foreground\">{caption}</span>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n </div>\n );\n}\n"],"names":["jsx","Icon","useMemo","useQueries","cn","jsxs"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeseriesCoreBars.d.ts","sourceRoot":"","sources":["../../src/data/TimeseriesCoreBars.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoBD,MAAM,WAAW,OAAO;IACtB,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AASD,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,aAAa,
|
|
1
|
+
{"version":3,"file":"TimeseriesCoreBars.d.ts","sourceRoot":"","sources":["../../src/data/TimeseriesCoreBars.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoBD,MAAM,WAAW,OAAO;IACtB,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AASD,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,aAAa,CA4Bf;AAaD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAY,EACZ,KAAK,EACL,GAAG,EACH,KAAK,EACL,IAAI,EACJ,KAAY,EACZ,SAAgB,EAChB,UAAqB,EACrB,OAAwB,EACxB,SAAS,GACV,EAAE,uBAAuB,2CAqEzB"}
|
|
@@ -33,7 +33,14 @@ function deriveCoreBars(usageMilli, limitMilli) {
|
|
|
33
33
|
const bars = Array.from({ length: coreCount }, (_, i) => ({
|
|
34
34
|
fill: clamp01(usageCores - i)
|
|
35
35
|
}));
|
|
36
|
-
return {
|
|
36
|
+
return {
|
|
37
|
+
hasUsage,
|
|
38
|
+
usageCores,
|
|
39
|
+
coreCount,
|
|
40
|
+
pct,
|
|
41
|
+
bars,
|
|
42
|
+
...limitCores !== void 0 && { limitCores }
|
|
43
|
+
};
|
|
37
44
|
}
|
|
38
45
|
function CoreBarsIcon({ icon }) {
|
|
39
46
|
if (typeof icon === "string") {
|
|
@@ -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\";\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 CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\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 /** 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\nexport interface CoreBar {\n /** Fraction of this core that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface CoreBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in cores (millicores / 1000). */\n usageCores: number;\n /** Capacity in cores; undefined when no limit was resolved. */\n limitCores?: number;\n /** Number of bars rendered (one per core). */\n coreCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-core fill fractions, left to right. */\n bars: CoreBar[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveCoreBars turns the latest usage/limit millicore readings into a per-core\n// bar model: one bar per core (ceil of the limit), filled left to right by usage\n// so the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly.\nexport function deriveCoreBars(\n usageMilli: number | undefined,\n limitMilli: number | undefined,\n): CoreBarsModel {\n const hasUsage = usageMilli !== undefined;\n const usageCores = (usageMilli ?? 0) / 1000;\n const hasLimit = limitMilli !== undefined && limitMilli > 0;\n const limitCores = hasLimit ? (limitMilli as number) / 1000 : undefined;\n\n const coreCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitCores as number) : usageCores),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageCores / (limitCores as number)) * 100))\n : 0;\n\n const bars: CoreBar[] = Array.from({ length: coreCount }, (_, i) => ({\n fill: clamp01(usageCores - i),\n }));\n\n return { hasUsage, usageCores, limitCores, coreCount, pct, bars };\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\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 title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\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 = rawValue === undefined ? undefined : value.transform ? value.transform(rawValue) : 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 = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"flex h-10 items-end gap-0.5\">\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <div\n key={i}\n className=\"relative h-full w-2 overflow-hidden rounded-sm bg-muted\"\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={`core ${i + 1}: ${corePct}%`}\n data-fill={bar.fill}\n >\n <div\n className={cn(\"absolute inset-x-0 bottom-0 transition-all duration-300\", tone)}\n style={{ height: `${corePct}%` }}\n />\n </div>\n );\n })}\n </div>\n <span className=\"text-xs font-semibold text-foreground\">{caption}</span>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n </div>\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 { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\nimport type { TimeseriesResponse } from \"./TimeseriesPanel\";\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 CPU capacity. Either a `.limit`-style metric series (its latest value in\n * millicores) or a fixed number of millicores. Determines the number of bars\n * (one per core). When omitted or 0, the bar count falls back to the ceiling of\n * the usage in cores so an unbounded reading still renders.\n */\n max?: GaugeSeries | number;\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 /** 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\nexport interface CoreBar {\n /** Fraction of this core that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface CoreBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in cores (millicores / 1000). */\n usageCores: number;\n /** Capacity in cores; undefined when no limit was resolved. */\n limitCores?: number;\n /** Number of bars rendered (one per core). */\n coreCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-core fill fractions, left to right. */\n bars: CoreBar[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveCoreBars turns the latest usage/limit millicore readings into a per-core\n// bar model: one bar per core (ceil of the limit), filled left to right by usage\n// so the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly.\nexport function deriveCoreBars(\n usageMilli: number | undefined,\n limitMilli: number | undefined,\n): CoreBarsModel {\n const hasUsage = usageMilli !== undefined;\n const usageCores = (usageMilli ?? 0) / 1000;\n const hasLimit = limitMilli !== undefined && limitMilli > 0;\n const limitCores = hasLimit ? (limitMilli as number) / 1000 : undefined;\n\n const coreCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitCores as number) : usageCores),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageCores / (limitCores as number)) * 100))\n : 0;\n\n const bars: CoreBar[] = Array.from({ length: coreCount }, (_, i) => ({\n fill: clamp01(usageCores - i),\n }));\n\n // limitCores is an optional field under exactOptionalPropertyTypes — only set\n // the key when a limit resolved, rather than assigning an explicit undefined.\n return {\n hasUsage,\n usageCores,\n coreCount,\n pct,\n bars,\n ...(limitCores !== undefined && { limitCores }),\n };\n}\n\nfunction CoreBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\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 title,\n icon,\n range = \"1h\",\n refreshMs = 5000,\n thresholds = [75, 90],\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 = rawValue === undefined ? undefined : value.transform ? value.transform(rawValue) : 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 = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const model = deriveCoreBars(usage, limit);\n const tone = toneClass(model.pct, thresholds);\n const caption = model.hasUsage\n ? `${formatCores(model.usageCores)} / ${model.limitCores !== undefined ? formatCores(model.limitCores) : \"?\"} cores`\n : \"—\";\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"flex h-10 items-end gap-0.5\">\n {model.bars.map((bar, i) => {\n const corePct = Math.round(bar.fill * 100);\n return (\n <div\n key={i}\n className=\"relative h-full w-2 overflow-hidden rounded-sm bg-muted\"\n title={`core ${i + 1}: ${corePct}%`}\n aria-label={`core ${i + 1}: ${corePct}%`}\n data-fill={bar.fill}\n >\n <div\n className={cn(\"absolute inset-x-0 bottom-0 transition-all duration-300\", tone)}\n style={{ height: `${corePct}%` }}\n />\n </div>\n );\n })}\n </div>\n <span className=\"text-xs font-semibold text-foreground\">{caption}</span>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <CoreBarsIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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 Badge = require("./Badge.cjs");
|
|
7
|
+
const versionInfo = require("./version-info.cjs");
|
|
8
|
+
const MODE_BADGE = {
|
|
9
|
+
dev: { label: "dev", tone: "info" },
|
|
10
|
+
storybook: { label: "storybook", tone: "neutral" }
|
|
11
|
+
};
|
|
12
|
+
function formatDate(iso) {
|
|
13
|
+
if (!iso) return "";
|
|
14
|
+
const parsed = new Date(iso);
|
|
15
|
+
return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();
|
|
16
|
+
}
|
|
17
|
+
const Version = react.forwardRef(
|
|
18
|
+
({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {
|
|
19
|
+
const resolved = { ...versionInfo.getVersionInfo(), ...info };
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (tag && resolved.tag) parts.push(resolved.tag);
|
|
22
|
+
if (commit && resolved.commit) parts.push(resolved.commit);
|
|
23
|
+
if (date && resolved.date) parts.push(formatDate(resolved.date));
|
|
24
|
+
const mode = resolved.mode !== "production" ? MODE_BADGE[resolved.mode] : null;
|
|
25
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26
|
+
"span",
|
|
27
|
+
{
|
|
28
|
+
ref,
|
|
29
|
+
className: utils.cn("inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground", className),
|
|
30
|
+
...props,
|
|
31
|
+
children: [
|
|
32
|
+
parts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { children: parts.join(" · ") }),
|
|
33
|
+
resolved.dirty && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "warning", variant: "soft", size: "xs", children: "dirty" }),
|
|
34
|
+
mode && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: mode.tone, variant: "soft", size: "xs", children: mode.label })
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
Version.displayName = "Version";
|
|
41
|
+
exports.Version = Version;
|
|
42
|
+
//# sourceMappingURL=Version.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.cjs","sources":["../../src/data/Version.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge, type BadgeTone } from \"./Badge\";\nimport { getVersionInfo, type RuntimeMode, type VersionInfo } from \"./version-info\";\n\nexport type VersionProps = HTMLAttributes<HTMLSpanElement> & {\n /** Show the short git commit hash. */\n commit?: boolean;\n /** Show the git tag. */\n tag?: boolean;\n /** Show the build date. */\n date?: boolean;\n /**\n * Override the resolved build/runtime info. Primarily for stories and tests;\n * when omitted the values injected at build time are used.\n */\n info?: Partial<VersionInfo>;\n};\n\nconst MODE_BADGE: Record<Exclude<RuntimeMode, \"production\">, { label: string; tone: BadgeTone }> = {\n dev: { label: \"dev\", tone: \"info\" },\n storybook: { label: \"storybook\", tone: \"neutral\" },\n};\n\nfunction formatDate(iso: string): string {\n if (!iso) return \"\";\n const parsed = new Date(iso);\n return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();\n}\n\n/**\n * Renders clicky-ui build metadata: git commit, tag, and build date, plus a\n * status badge for a dirty working tree, Vite dev mode, or Storybook.\n *\n * Commit/tag/date/dirty are captured when the library is built (Vite `define`);\n * `dev` and `storybook` are detected at runtime.\n */\nexport const Version = forwardRef<HTMLSpanElement, VersionProps>(\n ({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {\n const resolved = { ...getVersionInfo(), ...info };\n\n const parts: string[] = [];\n if (tag && resolved.tag) parts.push(resolved.tag);\n if (commit && resolved.commit) parts.push(resolved.commit);\n if (date && resolved.date) parts.push(formatDate(resolved.date));\n\n const mode = resolved.mode !== \"production\" ? MODE_BADGE[resolved.mode] : null;\n\n return (\n <span\n ref={ref}\n className={cn(\"inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground\", className)}\n {...props}\n >\n {parts.length > 0 && <span>{parts.join(\" · \")}</span>}\n {resolved.dirty && (\n <Badge tone=\"warning\" variant=\"soft\" size=\"xs\">\n dirty\n </Badge>\n )}\n {mode && (\n <Badge tone={mode.tone} variant=\"soft\" size=\"xs\">\n {mode.label}\n </Badge>\n )}\n </span>\n );\n },\n);\n\nVersion.displayName = \"Version\";\n"],"names":["forwardRef","getVersionInfo","jsxs","cn","jsx","Badge"],"mappings":";;;;;;;AAmBA,MAAM,aAA6F;AAAA,EACjG,KAAK,EAAE,OAAO,OAAO,MAAM,OAAA;AAAA,EAC3B,WAAW,EAAE,OAAO,aAAa,MAAM,UAAA;AACzC;AAEA,SAAS,WAAW,KAAqB;AACvC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,SAAO,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,MAAM,OAAO,YAAA;AACvD;AASO,MAAM,UAAUA,MAAAA;AAAAA,EACrB,CAAC,EAAE,SAAS,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC9E,UAAM,WAAW,EAAE,GAAGC,2BAAA,GAAkB,GAAG,KAAA;AAE3C,UAAM,QAAkB,CAAA;AACxB,QAAI,OAAO,SAAS,IAAK,OAAM,KAAK,SAAS,GAAG;AAChD,QAAI,UAAU,SAAS,OAAQ,OAAM,KAAK,SAAS,MAAM;AACzD,QAAI,QAAQ,SAAS,KAAM,OAAM,KAAK,WAAW,SAAS,IAAI,CAAC;AAE/D,UAAM,OAAO,SAAS,SAAS,eAAe,WAAW,SAAS,IAAI,IAAI;AAE1E,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,MAAAA,GAAG,4EAA4E,SAAS;AAAA,QAClG,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,MAAM,SAAS,KAAKC,2BAAAA,IAAC,UAAM,UAAA,MAAM,KAAK,KAAK,GAAE;AAAA,UAC7C,SAAS,SACRA,2BAAAA,IAACC,MAAAA,OAAA,EAAM,MAAK,WAAU,SAAQ,QAAO,MAAK,MAAK,UAAA,QAAA,CAE/C;AAAA,UAED,QACCD,2BAAAA,IAACC,MAAAA,OAAA,EAAM,MAAM,KAAK,MAAM,SAAQ,QAAO,MAAK,MACzC,UAAA,KAAK,MAAA,CACR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,QAAQ,cAAc;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { VersionInfo } from './version-info';
|
|
3
|
+
export type VersionProps = HTMLAttributes<HTMLSpanElement> & {
|
|
4
|
+
/** Show the short git commit hash. */
|
|
5
|
+
commit?: boolean;
|
|
6
|
+
/** Show the git tag. */
|
|
7
|
+
tag?: boolean;
|
|
8
|
+
/** Show the build date. */
|
|
9
|
+
date?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Override the resolved build/runtime info. Primarily for stories and tests;
|
|
12
|
+
* when omitted the values injected at build time are used.
|
|
13
|
+
*/
|
|
14
|
+
info?: Partial<VersionInfo>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Renders clicky-ui build metadata: git commit, tag, and build date, plus a
|
|
18
|
+
* status badge for a dirty working tree, Vite dev mode, or Storybook.
|
|
19
|
+
*
|
|
20
|
+
* Commit/tag/date/dirty are captured when the library is built (Vite `define`);
|
|
21
|
+
* `dev` and `storybook` are detected at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare const Version: import('react').ForwardRefExoticComponent<HTMLAttributes<HTMLSpanElement> & {
|
|
24
|
+
/** Show the short git commit hash. */
|
|
25
|
+
commit?: boolean;
|
|
26
|
+
/** Show the git tag. */
|
|
27
|
+
tag?: boolean;
|
|
28
|
+
/** Show the build date. */
|
|
29
|
+
date?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Override the resolved build/runtime info. Primarily for stories and tests;
|
|
32
|
+
* when omitted the values injected at build time are used.
|
|
33
|
+
*/
|
|
34
|
+
info?: Partial<VersionInfo>;
|
|
35
|
+
} & import('react').RefAttributes<HTMLSpanElement>>;
|
|
36
|
+
//# sourceMappingURL=Version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.d.ts","sourceRoot":"","sources":["../../src/data/Version.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEpF,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG;IAC3D,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wBAAwB;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2BAA2B;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7B,CAAC;AAaF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO;IA/BlB,sCAAsC;aAC7B,OAAO;IAChB,wBAAwB;UAClB,OAAO;IACb,2BAA2B;WACpB,OAAO;IACd;;;OAGG;WACI,OAAO,CAAC,WAAW,CAAC;mDAoD5B,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { Badge } from "./Badge.js";
|
|
5
|
+
import { getVersionInfo } from "./version-info.js";
|
|
6
|
+
const MODE_BADGE = {
|
|
7
|
+
dev: { label: "dev", tone: "info" },
|
|
8
|
+
storybook: { label: "storybook", tone: "neutral" }
|
|
9
|
+
};
|
|
10
|
+
function formatDate(iso) {
|
|
11
|
+
if (!iso) return "";
|
|
12
|
+
const parsed = new Date(iso);
|
|
13
|
+
return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();
|
|
14
|
+
}
|
|
15
|
+
const Version = forwardRef(
|
|
16
|
+
({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {
|
|
17
|
+
const resolved = { ...getVersionInfo(), ...info };
|
|
18
|
+
const parts = [];
|
|
19
|
+
if (tag && resolved.tag) parts.push(resolved.tag);
|
|
20
|
+
if (commit && resolved.commit) parts.push(resolved.commit);
|
|
21
|
+
if (date && resolved.date) parts.push(formatDate(resolved.date));
|
|
22
|
+
const mode = resolved.mode !== "production" ? MODE_BADGE[resolved.mode] : null;
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
"span",
|
|
25
|
+
{
|
|
26
|
+
ref,
|
|
27
|
+
className: cn("inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground", className),
|
|
28
|
+
...props,
|
|
29
|
+
children: [
|
|
30
|
+
parts.length > 0 && /* @__PURE__ */ jsx("span", { children: parts.join(" · ") }),
|
|
31
|
+
resolved.dirty && /* @__PURE__ */ jsx(Badge, { tone: "warning", variant: "soft", size: "xs", children: "dirty" }),
|
|
32
|
+
mode && /* @__PURE__ */ jsx(Badge, { tone: mode.tone, variant: "soft", size: "xs", children: mode.label })
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
Version.displayName = "Version";
|
|
39
|
+
export {
|
|
40
|
+
Version
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=Version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.js","sources":["../../src/data/Version.tsx"],"sourcesContent":["import { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Badge, type BadgeTone } from \"./Badge\";\nimport { getVersionInfo, type RuntimeMode, type VersionInfo } from \"./version-info\";\n\nexport type VersionProps = HTMLAttributes<HTMLSpanElement> & {\n /** Show the short git commit hash. */\n commit?: boolean;\n /** Show the git tag. */\n tag?: boolean;\n /** Show the build date. */\n date?: boolean;\n /**\n * Override the resolved build/runtime info. Primarily for stories and tests;\n * when omitted the values injected at build time are used.\n */\n info?: Partial<VersionInfo>;\n};\n\nconst MODE_BADGE: Record<Exclude<RuntimeMode, \"production\">, { label: string; tone: BadgeTone }> = {\n dev: { label: \"dev\", tone: \"info\" },\n storybook: { label: \"storybook\", tone: \"neutral\" },\n};\n\nfunction formatDate(iso: string): string {\n if (!iso) return \"\";\n const parsed = new Date(iso);\n return Number.isNaN(parsed.getTime()) ? iso : parsed.toISOString();\n}\n\n/**\n * Renders clicky-ui build metadata: git commit, tag, and build date, plus a\n * status badge for a dirty working tree, Vite dev mode, or Storybook.\n *\n * Commit/tag/date/dirty are captured when the library is built (Vite `define`);\n * `dev` and `storybook` are detected at runtime.\n */\nexport const Version = forwardRef<HTMLSpanElement, VersionProps>(\n ({ commit = true, tag = true, date = true, info, className, ...props }, ref) => {\n const resolved = { ...getVersionInfo(), ...info };\n\n const parts: string[] = [];\n if (tag && resolved.tag) parts.push(resolved.tag);\n if (commit && resolved.commit) parts.push(resolved.commit);\n if (date && resolved.date) parts.push(formatDate(resolved.date));\n\n const mode = resolved.mode !== \"production\" ? MODE_BADGE[resolved.mode] : null;\n\n return (\n <span\n ref={ref}\n className={cn(\"inline-flex items-center gap-1.5 font-mono text-xs text-muted-foreground\", className)}\n {...props}\n >\n {parts.length > 0 && <span>{parts.join(\" · \")}</span>}\n {resolved.dirty && (\n <Badge tone=\"warning\" variant=\"soft\" size=\"xs\">\n dirty\n </Badge>\n )}\n {mode && (\n <Badge tone={mode.tone} variant=\"soft\" size=\"xs\">\n {mode.label}\n </Badge>\n )}\n </span>\n );\n },\n);\n\nVersion.displayName = \"Version\";\n"],"names":[],"mappings":";;;;;AAmBA,MAAM,aAA6F;AAAA,EACjG,KAAK,EAAE,OAAO,OAAO,MAAM,OAAA;AAAA,EAC3B,WAAW,EAAE,OAAO,aAAa,MAAM,UAAA;AACzC;AAEA,SAAS,WAAW,KAAqB;AACvC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,SAAO,OAAO,MAAM,OAAO,QAAA,CAAS,IAAI,MAAM,OAAO,YAAA;AACvD;AASO,MAAM,UAAU;AAAA,EACrB,CAAC,EAAE,SAAS,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC9E,UAAM,WAAW,EAAE,GAAG,eAAA,GAAkB,GAAG,KAAA;AAE3C,UAAM,QAAkB,CAAA;AACxB,QAAI,OAAO,SAAS,IAAK,OAAM,KAAK,SAAS,GAAG;AAChD,QAAI,UAAU,SAAS,OAAQ,OAAM,KAAK,SAAS,MAAM;AACzD,QAAI,QAAQ,SAAS,KAAM,OAAM,KAAK,WAAW,SAAS,IAAI,CAAC;AAE/D,UAAM,OAAO,SAAS,SAAS,eAAe,WAAW,SAAS,IAAI,IAAI;AAE1E,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,4EAA4E,SAAS;AAAA,QAClG,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,MAAM,SAAS,KAAK,oBAAC,UAAM,UAAA,MAAM,KAAK,KAAK,GAAE;AAAA,UAC7C,SAAS,SACR,oBAAC,OAAA,EAAM,MAAK,WAAU,SAAQ,QAAO,MAAK,MAAK,UAAA,QAAA,CAE/C;AAAA,UAED,QACC,oBAAC,OAAA,EAAM,MAAM,KAAK,MAAM,SAAQ,QAAO,MAAK,MACzC,UAAA,KAAK,MAAA,CACR;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,QAAQ,cAAc;"}
|
|
@@ -0,0 +1,165 @@
|
|
|
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 Icon = require("../Icon.cjs");
|
|
6
|
+
const button = require("../../components/button.cjs");
|
|
7
|
+
const JsonView = require("../JsonView.cjs");
|
|
8
|
+
const TabButton = require("../TabButton.cjs");
|
|
9
|
+
const utils = require("../../lib/utils.cjs");
|
|
10
|
+
const context = require("./context.cjs");
|
|
11
|
+
const status = require("./status.cjs");
|
|
12
|
+
const frameworkIcon = require("./frameworkIcon.cjs");
|
|
13
|
+
const TestFailureDetail = require("./TestFailureDetail.cjs");
|
|
14
|
+
const UiClass = require("../../icons/components/UiClass.cjs");
|
|
15
|
+
const UiRefresh = require("../../icons/components/UiRefresh.cjs");
|
|
16
|
+
const UiStop = require("../../icons/components/UiStop.cjs");
|
|
17
|
+
const UiClock = require("../../icons/components/UiClock.cjs");
|
|
18
|
+
const ICON_TONE = {
|
|
19
|
+
danger: "rose",
|
|
20
|
+
warning: "amber",
|
|
21
|
+
success: "emerald",
|
|
22
|
+
info: "sky",
|
|
23
|
+
neutral: "neutral"
|
|
24
|
+
};
|
|
25
|
+
function DefaultDetailBody({ node }) {
|
|
26
|
+
var _a;
|
|
27
|
+
const hasChildren = (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
|
|
28
|
+
const counts = hasChildren ? status.sum(node) : null;
|
|
29
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 p-density-4", children: [
|
|
30
|
+
node.message && !node.failure_detail && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap break-words text-sm", children: node.message }),
|
|
31
|
+
node.failure_detail && /* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestFailureDetail, { failure: node.failure_detail }),
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestOutput, { node }),
|
|
33
|
+
node.attempts && node.attempts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TestFailureDetail.TestAttempts, { attempts: node.attempts }),
|
|
34
|
+
counts && counts.total > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-3 text-xs text-muted-foreground", children: [
|
|
35
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
36
|
+
counts.total,
|
|
37
|
+
" total"
|
|
38
|
+
] }),
|
|
39
|
+
counts.passed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
40
|
+
counts.passed,
|
|
41
|
+
" passed"
|
|
42
|
+
] }),
|
|
43
|
+
counts.failed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
44
|
+
counts.failed,
|
|
45
|
+
" failed"
|
|
46
|
+
] }),
|
|
47
|
+
counts.skipped > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
48
|
+
counts.skipped,
|
|
49
|
+
" skipped"
|
|
50
|
+
] })
|
|
51
|
+
] }),
|
|
52
|
+
node.detail != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
53
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Detail" }),
|
|
54
|
+
/* @__PURE__ */ jsxRuntime.jsx(JsonView.JsonView, { data: node.detail })
|
|
55
|
+
] })
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
function DetailHeader({ node, actions }) {
|
|
59
|
+
const runner = context.useTestRunner();
|
|
60
|
+
const fwIcon = frameworkIcon.frameworkIcon(node.framework);
|
|
61
|
+
const canRerun = !!runner.onRerun && node.framework !== "task";
|
|
62
|
+
const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;
|
|
63
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-b border-border p-density-4", children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
65
|
+
Icon.Icon,
|
|
66
|
+
{
|
|
67
|
+
icon: status.statusIconFor(node),
|
|
68
|
+
className: utils.cn("mt-0.5 shrink-0 text-2xl", node.running && "animate-spin"),
|
|
69
|
+
tone: ICON_TONE[status.statusToneFor(node)]
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
73
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
74
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "break-words text-base font-semibold", children: node.name }),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
76
|
+
actions,
|
|
77
|
+
canRerun && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
78
|
+
button.Button,
|
|
79
|
+
{
|
|
80
|
+
size: "sm",
|
|
81
|
+
disabled: runner.busy.rerun,
|
|
82
|
+
onClick: () => runner.onRerun(node),
|
|
83
|
+
title: "Rerun this test or subtree",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiRefresh.UiRefresh, className: "mr-1 text-sm" }),
|
|
86
|
+
runner.busy.rerun ? "Running..." : "Rerun"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
canStop && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
91
|
+
button.Button,
|
|
92
|
+
{
|
|
93
|
+
size: "sm",
|
|
94
|
+
variant: "destructive",
|
|
95
|
+
disabled: runner.busy.stop,
|
|
96
|
+
onClick: () => runner.onStop(node),
|
|
97
|
+
title: "Stop this running task",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiStop.UiStop, className: "mr-1 text-sm" }),
|
|
100
|
+
runner.busy.stop ? "Stopping..." : "Stop"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: [
|
|
107
|
+
fwIcon && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5", children: [
|
|
108
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: fwIcon, className: "text-sm" }),
|
|
109
|
+
node.framework
|
|
110
|
+
] }),
|
|
111
|
+
node.duration ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClock.UiClock, className: "text-xs" }),
|
|
113
|
+
status.formatTestDuration(node.duration)
|
|
114
|
+
] }) : null,
|
|
115
|
+
node.file && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
116
|
+
node.file,
|
|
117
|
+
node.line ? `:${node.line}` : ""
|
|
118
|
+
] })
|
|
119
|
+
] })
|
|
120
|
+
] })
|
|
121
|
+
] });
|
|
122
|
+
}
|
|
123
|
+
function TestDetailPanel() {
|
|
124
|
+
var _a;
|
|
125
|
+
const runner = context.useTestRunner();
|
|
126
|
+
const node = runner.selected;
|
|
127
|
+
const [activeTab, setActiveTab] = react.useState("detail");
|
|
128
|
+
if (!node) {
|
|
129
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClass.UiClass, className: "mb-2 block text-4xl" }),
|
|
131
|
+
"Select a test to view its details."
|
|
132
|
+
] }) });
|
|
133
|
+
}
|
|
134
|
+
const detailAdapter = runner.adapters.resolveDetail(node, runner);
|
|
135
|
+
const actionAdapter = runner.adapters.resolveActions(node, runner);
|
|
136
|
+
const tabs = runner.adapters.resolveTabs(node, runner);
|
|
137
|
+
const ownsScroll = runner.adapters.ownsScroll(node, runner);
|
|
138
|
+
const body = (detailAdapter == null ? void 0 : detailAdapter.renderDetail) ? detailAdapter.renderDetail({ node, runner }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultDetailBody, { node });
|
|
139
|
+
const actions = (_a = actionAdapter == null ? void 0 : actionAdapter.nodeActions) == null ? void 0 : _a.call(actionAdapter, { node, runner });
|
|
140
|
+
const detailRegion = ownsScroll ? "min-h-0 flex-1" : "min-h-0 flex-1 overflow-y-auto";
|
|
141
|
+
if (tabs.length === 0) {
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsx(DetailHeader, { node, actions }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: detailRegion, children: body })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
const detailTab = { id: "detail", label: "Detail", render: () => body };
|
|
148
|
+
const allTabs = [detailTab, ...tabs];
|
|
149
|
+
const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
151
|
+
/* @__PURE__ */ jsxRuntime.jsx(DetailHeader, { node, actions }),
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1", children: allTabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
+
TabButton.TabButton,
|
|
154
|
+
{
|
|
155
|
+
active: active.id === t.id,
|
|
156
|
+
onClick: () => setActiveTab(t.id),
|
|
157
|
+
label: t.label
|
|
158
|
+
},
|
|
159
|
+
t.id
|
|
160
|
+
)) }),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: detailRegion, children: active.render() })
|
|
162
|
+
] });
|
|
163
|
+
}
|
|
164
|
+
exports.TestDetailPanel = TestDetailPanel;
|
|
165
|
+
//# sourceMappingURL=TestDetailPanel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.cjs","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { JsonView } from \"../JsonView\";\nimport { TabButton } from \"../TabButton\";\nimport { cn } from \"../../lib/utils\";\nimport { UiClass, UiClock, UiRefresh, UiStop } from \"../../icons\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { formatTestDuration, statusIconFor, statusToneFor, sum } from \"./status\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { TestAttempts, TestFailureDetail, TestOutput } from \"./TestFailureDetail\";\n\nconst ICON_TONE = {\n danger: \"rose\",\n warning: \"amber\",\n success: \"emerald\",\n info: \"sky\",\n neutral: \"neutral\",\n} as const;\n\n/** Built-in detail body — used when no adapter contributes `renderDetail`. */\nfunction DefaultDetailBody({ node }: { node: Test }) {\n const hasChildren = (node.children?.length ?? 0) > 0;\n const counts = hasChildren ? sum(node) : null;\n return (\n <div className=\"space-y-4 p-density-4\">\n {node.message && !node.failure_detail && (\n <p className=\"whitespace-pre-wrap break-words text-sm\">{node.message}</p>\n )}\n {node.failure_detail && <TestFailureDetail failure={node.failure_detail} />}\n <TestOutput node={node} />\n {node.attempts && node.attempts.length > 0 && <TestAttempts attempts={node.attempts} />}\n {counts && counts.total > 0 && (\n <div className=\"flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span>{counts.total} total</span>\n {counts.passed > 0 && <span>{counts.passed} passed</span>}\n {counts.failed > 0 && <span>{counts.failed} failed</span>}\n {counts.skipped > 0 && <span>{counts.skipped} skipped</span>}\n </div>\n )}\n {node.detail != null && (\n <div className=\"space-y-1\">\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">Detail</div>\n <JsonView data={node.detail} />\n </div>\n )}\n </div>\n );\n}\n\nfunction DetailHeader({ node, actions }: { node: Test; actions?: ReactNode }) {\n const runner = useTestRunner();\n const fwIcon = frameworkIcon(node.framework);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n return (\n <div className=\"flex items-start gap-2 border-b border-border p-density-4\">\n <Icon\n icon={statusIconFor(node)}\n className={cn(\"mt-0.5 shrink-0 text-2xl\", node.running && \"animate-spin\")}\n tone={ICON_TONE[statusToneFor(node)]}\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"break-words text-base font-semibold\">{node.name}</h2>\n <div className=\"flex shrink-0 items-center gap-1\">\n {actions}\n {canRerun && (\n <Button\n size=\"sm\"\n disabled={runner.busy.rerun}\n onClick={() => runner.onRerun!(node)}\n title=\"Rerun this test or subtree\"\n >\n <Icon icon={UiRefresh} className=\"mr-1 text-sm\" />\n {runner.busy.rerun ? \"Running...\" : \"Rerun\"}\n </Button>\n )}\n {canStop && (\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={runner.busy.stop}\n onClick={() => runner.onStop!(node)}\n title=\"Stop this running task\"\n >\n <Icon icon={UiStop} className=\"mr-1 text-sm\" />\n {runner.busy.stop ? \"Stopping...\" : \"Stop\"}\n </Button>\n )}\n </div>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground\">\n {fwIcon && (\n <span className=\"inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5\">\n <Icon icon={fwIcon} className=\"text-sm\" />\n {node.framework}\n </span>\n )}\n {node.duration ? (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {formatTestDuration(node.duration)}\n </span>\n ) : null}\n {node.file && (\n <span className=\"font-mono\">\n {node.file}\n {node.line ? `:${node.line}` : \"\"}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The right pane. Resolves the node's adapter for body / tabs / header actions,\n * falling back to the built-in default body. Shows an empty state when nothing\n * is selected.\n */\nexport function TestDetailPanel() {\n const runner = useTestRunner();\n const node = runner.selected;\n const [activeTab, setActiveTab] = useState<string>(\"detail\");\n\n if (!node) {\n return (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n <div className=\"text-center\">\n <Icon icon={UiClass} className=\"mb-2 block text-4xl\" />\n Select a test to view its details.\n </div>\n </div>\n );\n }\n\n const detailAdapter = runner.adapters.resolveDetail(node, runner);\n const actionAdapter = runner.adapters.resolveActions(node, runner);\n const tabs = runner.adapters.resolveTabs(node, runner);\n const ownsScroll = runner.adapters.ownsScroll(node, runner);\n\n const body = detailAdapter?.renderDetail\n ? detailAdapter.renderDetail({ node, runner })\n : <DefaultDetailBody node={node} />;\n const actions = actionAdapter?.nodeActions?.({ node, runner });\n\n const detailRegion = ownsScroll ? \"min-h-0 flex-1\" : \"min-h-0 flex-1 overflow-y-auto\";\n\n // No extra tabs → just the body under the header.\n if (tabs.length === 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className={detailRegion}>{body}</div>\n </div>\n );\n }\n\n const detailTab = { id: \"detail\", label: \"Detail\", render: () => body };\n const allTabs = [detailTab, ...tabs];\n const active = allTabs.find((t) => t.id === activeTab) ?? detailTab;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <DetailHeader node={node} actions={actions} />\n <div className=\"flex shrink-0 items-center gap-1 border-b border-border px-density-2 py-density-1\">\n {allTabs.map((t) => (\n <TabButton\n key={t.id}\n active={active.id === t.id}\n onClick={() => setActiveTab(t.id)}\n label={t.label}\n />\n ))}\n </div>\n <div className={detailRegion}>{active.render()}</div>\n </div>\n );\n}\n"],"names":["sum","jsxs","jsx","TestFailureDetail","TestOutput","TestAttempts","JsonView","useTestRunner","frameworkIcon","Icon","statusIconFor","cn","statusToneFor","Button","UiRefresh","UiStop","UiClock","formatTestDuration","useState","UiClass","TabButton"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The right pane. Resolves the node's adapter for body / tabs / header actions,
|
|
3
|
+
* falling back to the built-in default body. Shows an empty state when nothing
|
|
4
|
+
* is selected.
|
|
5
|
+
*/
|
|
6
|
+
export declare function TestDetailPanel(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=TestDetailPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestDetailPanel.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestDetailPanel.tsx"],"names":[],"mappings":"AAsHA;;;;GAIG;AACH,wBAAgB,eAAe,4CA0D9B"}
|