@64mb/freelens-power 1.0.1 → 1.0.2
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/README.md +4 -4
- package/THIRD_PARTY_LICENSES +41 -0
- package/out/main/ai.js +9 -6
- package/out/main/ai.js.map +1 -1
- package/out/main/common/ai-extras.js +17 -0
- package/out/main/common/ai-extras.js.map +1 -1
- package/out/main/common/dashboard-readiness.js +88 -0
- package/out/main/common/dashboard-readiness.js.map +1 -0
- package/out/main/common/dashboard-selector.js +9 -1
- package/out/main/common/dashboard-selector.js.map +1 -1
- package/out/main/common/dashboard-views.js +2 -0
- package/out/main/common/dashboard-views.js.map +1 -1
- package/out/main/dashboard-network.js +104 -0
- package/out/main/dashboard-network.js.map +1 -0
- package/out/main/dashboard-views.js +98 -21
- package/out/main/dashboard-views.js.map +1 -1
- package/out/renderer/ai-extras.js +41 -12
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-message.js +1 -0
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai-provider-icon.js +44 -0
- package/out/renderer/ai-provider-icon.js.map +1 -0
- package/out/renderer/ai-shortcuts.js +1 -1
- package/out/renderer/ai-shortcuts.js.map +1 -1
- package/out/renderer/ai.js +53 -10
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -1
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/assets/claude.js +6 -0
- package/out/renderer/assets/claude.js.map +1 -0
- package/out/renderer/dashboard.js +39 -4
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js.map +1 -1
- package/out/renderer/monitoring.js +671 -91
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/pod-monitoring.js +214 -0
- package/out/renderer/pod-monitoring.js.map +1 -0
- package/out/renderer/preferences/ai-preferences.js +2 -2
- package/out/renderer/preferences/ai-preferences.js.map +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +1 -8
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +1 -1
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-icon-action.js +3 -1
- package/out/renderer/preferences/settings-icon-action.js.map +1 -1
- package/out/renderer/src/common/ai-extras.js +17 -0
- package/out/renderer/src/common/ai-extras.js.map +1 -1
- package/out/renderer/src/common/dashboard-selector.js.map +1 -1
- package/out/renderer/src/common/dashboard-views.js +2 -0
- package/out/renderer/src/common/dashboard-views.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,59 @@
|
|
|
1
1
|
const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
2
|
const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
|
|
3
3
|
const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
|
|
4
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
4
5
|
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
5
6
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
6
7
|
//#region src/renderer/monitoring.tsx
|
|
8
|
+
function effectivePodResource(pod, metric, kind) {
|
|
9
|
+
const read = (resources) => {
|
|
10
|
+
const raw = resources?.[kind]?.[metric];
|
|
11
|
+
if (raw !== void 0) return parseQuantity(raw, metric === "memory");
|
|
12
|
+
if (kind === "requests" && resources?.limits?.[metric] !== void 0) return parseQuantity(resources.limits[metric], metric === "memory");
|
|
13
|
+
return kind === "requests" ? 0 : Infinity;
|
|
14
|
+
};
|
|
15
|
+
const spec = pod.spec;
|
|
16
|
+
if (!spec?.containers?.length) throw new Error("Missing pod containers");
|
|
17
|
+
let sidecars = 0;
|
|
18
|
+
let initPeak = 0;
|
|
19
|
+
for (const container of spec.initContainers ?? []) {
|
|
20
|
+
const value = read(container.resources);
|
|
21
|
+
if (container.restartPolicy === "Always") {
|
|
22
|
+
sidecars += value;
|
|
23
|
+
initPeak = Math.max(initPeak, sidecars);
|
|
24
|
+
} else initPeak = Math.max(initPeak, sidecars + value);
|
|
25
|
+
}
|
|
26
|
+
let effective = Math.max(initPeak, sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0));
|
|
27
|
+
if (spec.resources?.[kind]?.[metric] !== void 0) effective = read(spec.resources);
|
|
28
|
+
const overhead = spec.overhead?.[metric];
|
|
29
|
+
if (overhead !== void 0 && (kind === "requests" || effective > 0)) effective += parseQuantity(overhead, metric === "memory");
|
|
30
|
+
return effective;
|
|
31
|
+
}
|
|
32
|
+
function aggregateResources(nodes, pods) {
|
|
33
|
+
if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error("Missing node inventory");
|
|
34
|
+
const names = new Set(nodes.map((node) => node.metadata.name));
|
|
35
|
+
const scheduled = pods?.filter((pod) => pod.spec?.nodeName && names.has(pod.spec.nodeName) && pod.status?.phase !== "Succeeded" && pod.status?.phase !== "Failed");
|
|
36
|
+
const sum = (metric) => {
|
|
37
|
+
const capacity = nodes.reduce((total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? "", metric === "memory"), 0);
|
|
38
|
+
const allocatable = nodes.reduce((total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? "", metric === "memory"), 0);
|
|
39
|
+
const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, "requests"), 0);
|
|
40
|
+
const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, "limits"), 0);
|
|
41
|
+
return {
|
|
42
|
+
capacity,
|
|
43
|
+
allocatable,
|
|
44
|
+
requests,
|
|
45
|
+
limits: limits === Infinity ? void 0 : limits,
|
|
46
|
+
unbounded: limits === Infinity
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
cpu: sum("cpu"),
|
|
51
|
+
memory: sum("memory")
|
|
52
|
+
};
|
|
53
|
+
}
|
|
7
54
|
var INTERVAL = 3e4;
|
|
8
55
|
function parseQuantity(raw, memory = false) {
|
|
9
|
-
const match = /^(\d+(?:\.\d+)
|
|
56
|
+
const match = /^(\d+(?:\.\d*)?|\.\d+)([eE][+-]?\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);
|
|
10
57
|
if (!match) throw new Error("Invalid metrics quantity");
|
|
11
58
|
const suffix = match[2] ?? "";
|
|
12
59
|
const value = Number(match[1]) * (suffix.endsWith("i") ? 1024 ** ([
|
|
@@ -16,7 +63,7 @@ function parseQuantity(raw, memory = false) {
|
|
|
16
63
|
"Ti",
|
|
17
64
|
"Pi",
|
|
18
65
|
"Ei"
|
|
19
|
-
].indexOf(suffix) + 1) : {
|
|
66
|
+
].indexOf(suffix) + 1) : /^[eE][+-]?\d+$/.test(suffix) ? 10 ** Number(suffix.slice(1)) : {
|
|
20
67
|
n: 1e-9,
|
|
21
68
|
u: 1e-6,
|
|
22
69
|
m: .001,
|
|
@@ -47,100 +94,562 @@ function aggregateMetrics(nodes, now = Date.now()) {
|
|
|
47
94
|
memory
|
|
48
95
|
};
|
|
49
96
|
}
|
|
50
|
-
function
|
|
51
|
-
return
|
|
97
|
+
function formatMonitoringTime(time) {
|
|
98
|
+
return new Date(time).toLocaleTimeString([], {
|
|
99
|
+
hour: "2-digit",
|
|
100
|
+
minute: "2-digit",
|
|
101
|
+
second: "2-digit",
|
|
102
|
+
hourCycle: "h23"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function ResourceTitle({ metric, label }) {
|
|
106
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("strong", {
|
|
107
|
+
style: {
|
|
108
|
+
display: "inline-flex",
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
gap: 6
|
|
111
|
+
},
|
|
112
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
113
|
+
className: "Icon material",
|
|
114
|
+
"aria-hidden": "true",
|
|
115
|
+
style: {
|
|
116
|
+
"--size": "18px",
|
|
117
|
+
fontWeight: 400
|
|
118
|
+
},
|
|
119
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
120
|
+
className: "icon",
|
|
121
|
+
children: metric === "cpu" ? "developer_board" : "memory"
|
|
122
|
+
})
|
|
123
|
+
}), label ?? (metric === "cpu" ? "CPU" : "Memory")]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
127
|
+
const [selectedTime, setSelectedTime] = require__global_external_react.useState();
|
|
128
|
+
const [pinned, setPinned] = require__global_external_react.useState(false);
|
|
129
|
+
const previousMetric = require__global_external_react.useRef(metric);
|
|
130
|
+
require__global_external_react.useEffect(() => {
|
|
131
|
+
if (previousMetric.current !== metric || selectedTime !== void 0 && !samples.some((sample) => sample.time === selectedTime)) {
|
|
132
|
+
setPinned(false);
|
|
133
|
+
setSelectedTime(void 0);
|
|
134
|
+
}
|
|
135
|
+
previousMetric.current = metric;
|
|
136
|
+
}, [
|
|
137
|
+
metric,
|
|
138
|
+
samples,
|
|
139
|
+
selectedTime
|
|
140
|
+
]);
|
|
141
|
+
require__global_external_react.useEffect(() => {
|
|
142
|
+
if (!pinned) return;
|
|
143
|
+
const escape = (event) => {
|
|
144
|
+
if (event.key === "Escape") {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
event.stopPropagation();
|
|
147
|
+
setPinned(false);
|
|
148
|
+
setSelectedTime(void 0);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
document.addEventListener("keydown", escape, true);
|
|
152
|
+
return () => document.removeEventListener("keydown", escape, true);
|
|
153
|
+
}, [pinned]);
|
|
154
|
+
const chartRef = require__global_external_react.useRef(null);
|
|
155
|
+
const [width, setWidth] = require__global_external_react.useState(600);
|
|
156
|
+
require__global_external_react.useLayoutEffect(() => {
|
|
157
|
+
const chart = chartRef.current;
|
|
158
|
+
if (!chart) return;
|
|
159
|
+
const measure = () => {
|
|
160
|
+
const measured = chart.getBoundingClientRect().width;
|
|
161
|
+
if (measured > 0) setWidth(measured);
|
|
162
|
+
};
|
|
163
|
+
measure();
|
|
164
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
|
|
165
|
+
observer?.observe(chart);
|
|
166
|
+
window.addEventListener("resize", measure);
|
|
167
|
+
return () => {
|
|
168
|
+
observer?.disconnect();
|
|
169
|
+
window.removeEventListener("resize", measure);
|
|
170
|
+
};
|
|
171
|
+
}, []);
|
|
172
|
+
const plotWidth = Math.max(1, width - 112);
|
|
173
|
+
const last = samples.at(-1);
|
|
174
|
+
const selected = samples.find((sample) => sample.time === selectedTime);
|
|
175
|
+
const label = metric === "cpu" ? "CPU" : "Memory";
|
|
176
|
+
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
177
|
+
const series = [{
|
|
178
|
+
key: "usage",
|
|
179
|
+
label: "Usage",
|
|
180
|
+
color: "#c93dce",
|
|
181
|
+
value: (sample) => sample[metric]
|
|
182
|
+
}, ...[
|
|
183
|
+
"requests",
|
|
184
|
+
"allocatable",
|
|
185
|
+
"capacity"
|
|
186
|
+
].map((key) => ({
|
|
187
|
+
key,
|
|
188
|
+
label: {
|
|
189
|
+
requests: "Requests",
|
|
190
|
+
allocatable: "Allocatable Capacity",
|
|
191
|
+
capacity: "Capacity"
|
|
192
|
+
}[key],
|
|
193
|
+
color: {
|
|
194
|
+
requests: "#4caf50",
|
|
195
|
+
allocatable: "#5486b5",
|
|
196
|
+
capacity: "var(--textColorSecondary)"
|
|
197
|
+
}[key],
|
|
198
|
+
value: (sample) => sample.resources?.[metric][key]
|
|
199
|
+
}))];
|
|
200
|
+
const max = Math.max(...samples.flatMap((sample) => series.map((line) => line.value(sample) ?? 0)), .001) * 1.1;
|
|
201
|
+
const end = last?.time ?? Date.now();
|
|
202
|
+
const start = samples[0]?.time ?? end;
|
|
203
|
+
const duration = Math.max(end - start, INTERVAL);
|
|
204
|
+
const x = (sample) => 88 + (sample.time - start) / duration * plotWidth;
|
|
205
|
+
const y = (sample) => 208 - sample[metric] / max * 184;
|
|
206
|
+
const selectNearest = (event) => {
|
|
207
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
208
|
+
if (!bounds.width || !samples.length) return;
|
|
209
|
+
const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);
|
|
210
|
+
const offset = (bounds.width - width * scale) / 2;
|
|
211
|
+
const time = start + ((event.clientX - bounds.left - offset) / scale - 88) / plotWidth * duration;
|
|
212
|
+
const nearest = samples.reduce((nearest, sample) => Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest).time;
|
|
213
|
+
setSelectedTime(nearest);
|
|
214
|
+
return nearest;
|
|
215
|
+
};
|
|
216
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
217
|
+
"aria-label": `${label} metrics-server usage`,
|
|
52
218
|
style: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
219
|
+
padding: "var(--power-monitoring-trend-top, 16px) 16px 16px",
|
|
220
|
+
background: "var(--contentColor)",
|
|
221
|
+
minWidth: 0,
|
|
222
|
+
minHeight: 280,
|
|
223
|
+
position: "relative"
|
|
57
224
|
},
|
|
58
|
-
children: [
|
|
59
|
-
|
|
60
|
-
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
61
|
-
const max = Math.max(...samples.map((sample) => sample[metric]), .001) * 1.1;
|
|
62
|
-
const end = last?.time ?? Date.now();
|
|
63
|
-
const start = samples[0]?.time ?? end;
|
|
64
|
-
const points = samples.map((sample) => `${8 + (sample.time - start) / Math.max(end - start, INTERVAL) * 384},${112 - sample[metric] / max * 100}`).join(" ");
|
|
65
|
-
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
225
|
+
children: [
|
|
226
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
66
227
|
style: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
228
|
+
display: "flex",
|
|
229
|
+
alignItems: "center",
|
|
230
|
+
gap: 12
|
|
231
|
+
},
|
|
232
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(ResourceTitle, { metric }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status ? "Unavailable" : last ? format(last[metric]) : "Collecting…" })]
|
|
233
|
+
}),
|
|
234
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
235
|
+
ref: chartRef,
|
|
236
|
+
"data-monitoring-trend": "",
|
|
237
|
+
viewBox: `0 0 ${width} 250`,
|
|
238
|
+
role: "img",
|
|
239
|
+
tabIndex: 0,
|
|
240
|
+
"aria-label": `${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`,
|
|
241
|
+
onMouseMove: (event) => {
|
|
242
|
+
if (!pinned) selectNearest(event);
|
|
243
|
+
},
|
|
244
|
+
onClick: (event) => {
|
|
245
|
+
if (pinned) setPinned(false);
|
|
246
|
+
else if (samples.length) {
|
|
247
|
+
if (selectNearest(event) === void 0 && !selected) setSelectedTime(last?.time);
|
|
248
|
+
setPinned(true);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
onMouseLeave: () => {
|
|
252
|
+
if (!pinned) setSelectedTime(void 0);
|
|
253
|
+
},
|
|
254
|
+
onFocus: () => {
|
|
255
|
+
if (!pinned) setSelectedTime(last?.time);
|
|
256
|
+
},
|
|
257
|
+
onBlur: () => {
|
|
258
|
+
if (!pinned) setSelectedTime(void 0);
|
|
259
|
+
},
|
|
260
|
+
onKeyDown: (event) => {
|
|
261
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
if (samples.length) {
|
|
264
|
+
if (!selected) setSelectedTime(last?.time);
|
|
265
|
+
setPinned(!pinned);
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
const index = samples.findIndex((sample) => sample.time === selectedTime);
|
|
272
|
+
setSelectedTime(samples[Math.max(0, Math.min(samples.length - 1, (index < 0 ? samples.length - 1 : index) + (event.key === "ArrowLeft" ? -1 : 1)))]?.time);
|
|
273
|
+
},
|
|
274
|
+
style: {
|
|
275
|
+
width: "100%",
|
|
276
|
+
height: 250,
|
|
277
|
+
color: "#c93dce",
|
|
278
|
+
display: "block"
|
|
279
|
+
},
|
|
280
|
+
children: [
|
|
281
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("title", { children: "Click to pin tooltip · click again or Escape to unpin" }),
|
|
282
|
+
[
|
|
283
|
+
0,
|
|
284
|
+
.5,
|
|
285
|
+
1
|
|
286
|
+
].map((fraction) => /* @__PURE__ */ require_jsx_runtime.jsxs("g", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
287
|
+
x1: "88",
|
|
288
|
+
x2: width - 24,
|
|
289
|
+
y1: 208 - fraction * 184,
|
|
290
|
+
y2: 208 - fraction * 184,
|
|
291
|
+
stroke: "var(--borderFaintColor)"
|
|
292
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
293
|
+
x: "80",
|
|
294
|
+
y: 212 - fraction * 184,
|
|
295
|
+
textAnchor: "end",
|
|
296
|
+
fill: "var(--textColorSecondary)",
|
|
297
|
+
fontSize: "11",
|
|
298
|
+
children: format(max * fraction)
|
|
299
|
+
})] }, fraction)),
|
|
300
|
+
series.map((line) => /* @__PURE__ */ require_jsx_runtime.jsx("path", {
|
|
301
|
+
"data-monitoring-series": line.key,
|
|
302
|
+
d: samples.reduce((path, sample, index) => {
|
|
303
|
+
const value = line.value(sample);
|
|
304
|
+
if (value === void 0) return path;
|
|
305
|
+
return `${path} ${(index ? line.value(samples[index - 1]) : void 0) === void 0 ? "M" : "L"}${x(sample)},${208 - value / max * 184}`;
|
|
306
|
+
}, ""),
|
|
307
|
+
fill: "none",
|
|
308
|
+
stroke: line.color,
|
|
309
|
+
strokeWidth: "2",
|
|
310
|
+
strokeDasharray: line.key === "capacity" || line.key === "allocatable" ? "5 3" : void 0
|
|
311
|
+
}, line.key)),
|
|
312
|
+
samples.map((sample) => /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
313
|
+
cx: x(sample),
|
|
314
|
+
cy: y(sample),
|
|
315
|
+
r: "2",
|
|
316
|
+
fill: "currentColor",
|
|
317
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsxs("title", { children: [
|
|
318
|
+
formatMonitoringTime(sample.time),
|
|
319
|
+
": ",
|
|
320
|
+
format(sample[metric])
|
|
321
|
+
] })
|
|
322
|
+
}, sample.time)),
|
|
323
|
+
samples.length > 0 && [
|
|
324
|
+
0,
|
|
325
|
+
.5,
|
|
326
|
+
1
|
|
327
|
+
].map((fraction) => /* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
328
|
+
x: 88 + fraction * plotWidth,
|
|
329
|
+
y: "235",
|
|
330
|
+
textAnchor: fraction === 0 ? "start" : fraction === 1 ? "end" : "middle",
|
|
331
|
+
fill: "var(--textColorSecondary)",
|
|
332
|
+
fontSize: "11",
|
|
333
|
+
children: formatMonitoringTime(start + fraction * duration)
|
|
334
|
+
}, fraction)),
|
|
335
|
+
selected && /* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
336
|
+
"data-monitoring-crosshair": "",
|
|
337
|
+
x1: x(selected),
|
|
338
|
+
x2: x(selected),
|
|
339
|
+
y1: "24",
|
|
340
|
+
y2: "208",
|
|
341
|
+
stroke: "var(--textColorSecondary)",
|
|
342
|
+
strokeDasharray: "4 3"
|
|
343
|
+
})
|
|
344
|
+
]
|
|
345
|
+
}),
|
|
346
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
347
|
+
@keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }
|
|
348
|
+
[data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }
|
|
349
|
+
@media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }
|
|
350
|
+
` }),
|
|
351
|
+
selected && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
352
|
+
"data-monitoring-tooltip": "",
|
|
353
|
+
role: "tooltip",
|
|
354
|
+
style: {
|
|
355
|
+
position: "absolute",
|
|
356
|
+
top: 44,
|
|
357
|
+
right: 24,
|
|
358
|
+
padding: 8,
|
|
70
359
|
background: "var(--contentColor)",
|
|
71
|
-
|
|
360
|
+
border: "1px solid var(--borderFaintColor)",
|
|
361
|
+
pointerEvents: "none"
|
|
72
362
|
},
|
|
73
|
-
"aria-label": `${metric === "cpu" ? "CPU" : "Memory"} metrics-server usage`,
|
|
74
363
|
children: [
|
|
75
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
justifyContent: "space-between",
|
|
79
|
-
gap: 8
|
|
80
|
-
},
|
|
81
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: metric === "cpu" ? "CPU" : "Memory" }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status ? "Unavailable" : last ? format(last[metric]) : "Collecting…" })]
|
|
82
|
-
}),
|
|
83
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
84
|
-
viewBox: "0 0 400 128",
|
|
85
|
-
role: "img",
|
|
86
|
-
"aria-label": `${metric} usage over collected samples`,
|
|
87
|
-
style: {
|
|
88
|
-
width: "100%",
|
|
89
|
-
height: 128,
|
|
90
|
-
color: "var(--primary)"
|
|
91
|
-
},
|
|
364
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [formatMonitoringTime(selected.time), pinned ? " · Pinned" : ""] }),
|
|
365
|
+
series.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
366
|
+
style: { color: line.color },
|
|
92
367
|
children: [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
112
|
|
97
|
-
].map((y) => /* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
98
|
-
x1: "8",
|
|
99
|
-
x2: "392",
|
|
100
|
-
y1: y,
|
|
101
|
-
y2: y,
|
|
102
|
-
stroke: "var(--borderFaintColor)"
|
|
103
|
-
}, y)),
|
|
104
|
-
/* @__PURE__ */ require_jsx_runtime.jsx("polyline", {
|
|
105
|
-
points,
|
|
106
|
-
fill: "none",
|
|
107
|
-
stroke: "currentColor",
|
|
108
|
-
strokeWidth: "2"
|
|
109
|
-
}),
|
|
110
|
-
samples.map((sample) => /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
111
|
-
cx: 8 + (sample.time - start) / Math.max(end - start, INTERVAL) * 384,
|
|
112
|
-
cy: 112 - sample[metric] / max * 100,
|
|
113
|
-
r: "2",
|
|
114
|
-
fill: "currentColor",
|
|
115
|
-
children: /* @__PURE__ */ require_jsx_runtime.jsxs("title", { children: [
|
|
116
|
-
new Date(sample.time).toLocaleTimeString(),
|
|
117
|
-
": ",
|
|
118
|
-
format(sample[metric])
|
|
119
|
-
] })
|
|
120
|
-
}, sample.time))
|
|
368
|
+
line.label,
|
|
369
|
+
": ",
|
|
370
|
+
line.value(selected) === void 0 ? "Unavailable" : format(line.value(selected))
|
|
121
371
|
]
|
|
122
|
-
}),
|
|
123
|
-
/* @__PURE__ */ require_jsx_runtime.
|
|
372
|
+
}, line.key)),
|
|
373
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
124
374
|
style: {
|
|
125
|
-
display: "flex",
|
|
126
|
-
justifyContent: "space-between",
|
|
127
375
|
color: "var(--textColorSecondary)",
|
|
128
|
-
|
|
376
|
+
marginTop: 4,
|
|
377
|
+
fontSize: 10
|
|
129
378
|
},
|
|
130
|
-
children:
|
|
379
|
+
children: pinned ? "Click chart or Escape to unpin" : "Click or Enter to pin"
|
|
380
|
+
})
|
|
381
|
+
]
|
|
382
|
+
}),
|
|
383
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
384
|
+
"aria-label": `${label} chart legend`,
|
|
385
|
+
style: {
|
|
386
|
+
display: "flex",
|
|
387
|
+
flexWrap: "wrap",
|
|
388
|
+
gap: "6px 16px",
|
|
389
|
+
marginBottom: 8
|
|
390
|
+
},
|
|
391
|
+
children: series.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
392
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
393
|
+
style: { color: line.color },
|
|
394
|
+
children: "●"
|
|
131
395
|
}),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
396
|
+
" ",
|
|
397
|
+
line.label
|
|
398
|
+
] }, line.key))
|
|
399
|
+
}),
|
|
400
|
+
last?.resourceStatus && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
401
|
+
role: "status",
|
|
402
|
+
style: {
|
|
403
|
+
color: "var(--textColorSecondary)",
|
|
404
|
+
marginBottom: 8
|
|
405
|
+
},
|
|
406
|
+
children: last.resourceStatus
|
|
407
|
+
}),
|
|
408
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
409
|
+
style: {
|
|
410
|
+
fontSize: 11,
|
|
411
|
+
color: "var(--textColorSecondary)",
|
|
412
|
+
display: "flex",
|
|
413
|
+
alignItems: "center",
|
|
414
|
+
gap: 4
|
|
415
|
+
},
|
|
416
|
+
role: "status",
|
|
417
|
+
"aria-label": `${scope} metrics status`,
|
|
418
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
419
|
+
"aria-hidden": "true",
|
|
420
|
+
style: { fontSize: 12 },
|
|
421
|
+
children: "*"
|
|
422
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status || (samples.length ? "metrics-server data" : "Waiting for metrics-server data") })]
|
|
423
|
+
})
|
|
424
|
+
]
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
function ResourceDonut({ metric, sample, status }) {
|
|
428
|
+
const [activeRing, setActiveRing] = require__global_external_react.useState();
|
|
429
|
+
require__global_external_react.useEffect(() => {
|
|
430
|
+
if (activeRing === void 0) return;
|
|
431
|
+
const dismiss = (event) => {
|
|
432
|
+
if (event.key !== "Escape") return;
|
|
433
|
+
event.preventDefault();
|
|
434
|
+
event.stopPropagation();
|
|
435
|
+
setActiveRing(void 0);
|
|
436
|
+
};
|
|
437
|
+
document.addEventListener("keydown", dismiss, true);
|
|
438
|
+
return () => document.removeEventListener("keydown", dismiss, true);
|
|
439
|
+
}, [activeRing]);
|
|
440
|
+
const label = metric === "cpu" ? "CPU" : "Memory";
|
|
441
|
+
const totals = sample?.resources?.[metric];
|
|
442
|
+
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
443
|
+
const available = (value) => value === void 0 ? "Unavailable" : format(value);
|
|
444
|
+
const denominator = totals?.allocatable;
|
|
445
|
+
const rings = [{
|
|
446
|
+
label: "Usage",
|
|
447
|
+
color: "#c93dce",
|
|
448
|
+
value: sample?.[metric]
|
|
449
|
+
}, {
|
|
450
|
+
label: "Requests",
|
|
451
|
+
color: "#4caf50",
|
|
452
|
+
value: totals?.requests
|
|
453
|
+
}];
|
|
454
|
+
const percentage = (value) => value !== void 0 && denominator !== void 0 && denominator > 0 ? value / denominator * 100 : void 0;
|
|
455
|
+
const usagePercent = percentage(sample?.[metric]);
|
|
456
|
+
const unused = sample?.completeUsage && denominator !== void 0 ? format(denominator - sample[metric]) : "Unavailable";
|
|
457
|
+
const unrequested = denominator !== void 0 && totals?.requests !== void 0 ? format(denominator - totals.requests) : "Unavailable";
|
|
458
|
+
const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;
|
|
459
|
+
const hovered = activeRing === void 0 ? void 0 : rings[activeRing];
|
|
460
|
+
const tooltipId = `power-monitoring-${metric}-donut-tooltip`;
|
|
461
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
462
|
+
"aria-label": `${label} resource summary`,
|
|
463
|
+
style: {
|
|
464
|
+
padding: "var(--power-monitoring-summary-top, 16px) 16px 16px",
|
|
465
|
+
minWidth: 0,
|
|
466
|
+
background: "var(--contentColor)",
|
|
467
|
+
position: "relative",
|
|
468
|
+
overflow: "visible"
|
|
469
|
+
},
|
|
470
|
+
children: [
|
|
471
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(ResourceTitle, {
|
|
472
|
+
metric,
|
|
473
|
+
label: metric === "memory" ? "RAM" : "CPU"
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
476
|
+
"data-monitoring-donut": "",
|
|
477
|
+
viewBox: "0 0 200 200",
|
|
478
|
+
role: "img",
|
|
479
|
+
"aria-label": `${label} usage and requests relative to allocatable capacity`,
|
|
480
|
+
style: {
|
|
481
|
+
display: "block",
|
|
482
|
+
width: "100%",
|
|
483
|
+
height: 200
|
|
484
|
+
},
|
|
485
|
+
children: [
|
|
486
|
+
rings.map((ring, index) => {
|
|
487
|
+
const radius = 76 - index * 18;
|
|
488
|
+
const percent = percentage(ring.value);
|
|
489
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("g", {
|
|
490
|
+
tabIndex: 0,
|
|
491
|
+
role: "img",
|
|
492
|
+
"aria-label": `${ring.label}: ${available(ring.value)}${percent === void 0 ? "" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? ", over allocatable" : ""})`}`,
|
|
493
|
+
"aria-describedby": activeRing === index ? tooltipId : void 0,
|
|
494
|
+
onMouseEnter: () => setActiveRing(index),
|
|
495
|
+
onMouseMove: () => setActiveRing(index),
|
|
496
|
+
onMouseLeave: () => setActiveRing(void 0),
|
|
497
|
+
onFocus: () => setActiveRing(index),
|
|
498
|
+
onBlur: () => setActiveRing(void 0),
|
|
499
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
500
|
+
cx: "100",
|
|
501
|
+
cy: "100",
|
|
502
|
+
r: radius,
|
|
503
|
+
fill: "none",
|
|
504
|
+
stroke: "var(--borderFaintColor)",
|
|
505
|
+
strokeWidth: "12"
|
|
506
|
+
}), percent !== void 0 && /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
507
|
+
"data-monitoring-ring": ring.label,
|
|
508
|
+
cx: "100",
|
|
509
|
+
cy: "100",
|
|
510
|
+
r: radius,
|
|
511
|
+
fill: "none",
|
|
512
|
+
stroke: ring.color,
|
|
513
|
+
strokeWidth: "12",
|
|
514
|
+
pathLength: "100",
|
|
515
|
+
strokeDasharray: `${Math.min(100, Math.max(0, percent))} 100`,
|
|
516
|
+
transform: "rotate(-90 100 100)"
|
|
517
|
+
})]
|
|
518
|
+
}, ring.label);
|
|
519
|
+
}),
|
|
520
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
521
|
+
"data-monitoring-usage-percentage": "",
|
|
522
|
+
x: "100",
|
|
523
|
+
y: "100",
|
|
524
|
+
textAnchor: "middle",
|
|
525
|
+
fill: "var(--textColorPrimary)",
|
|
526
|
+
fontSize: "26",
|
|
527
|
+
fontWeight: "600",
|
|
528
|
+
children: usagePercent === void 0 ? "N/A" : `${usagePercent.toFixed(1)}%`
|
|
529
|
+
}),
|
|
530
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
531
|
+
x: "100",
|
|
532
|
+
y: "120",
|
|
533
|
+
textAnchor: "middle",
|
|
534
|
+
fill: "var(--textColorPrimary)",
|
|
535
|
+
fontSize: "12",
|
|
536
|
+
children: "Usage"
|
|
140
537
|
})
|
|
141
538
|
]
|
|
142
|
-
},
|
|
143
|
-
|
|
539
|
+
}),
|
|
540
|
+
hovered && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
541
|
+
id: tooltipId,
|
|
542
|
+
"data-monitoring-tooltip": "",
|
|
543
|
+
role: "tooltip",
|
|
544
|
+
style: {
|
|
545
|
+
position: "absolute",
|
|
546
|
+
top: 48,
|
|
547
|
+
left: 8,
|
|
548
|
+
right: 8,
|
|
549
|
+
zIndex: 20,
|
|
550
|
+
padding: 8,
|
|
551
|
+
border: "1px solid var(--borderFaintColor)",
|
|
552
|
+
background: "var(--contentColor)",
|
|
553
|
+
pointerEvents: "none",
|
|
554
|
+
overflowWrap: "anywhere",
|
|
555
|
+
boxShadow: "0 3px 12px #0003"
|
|
556
|
+
},
|
|
557
|
+
children: [
|
|
558
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
559
|
+
style: { color: hovered.color },
|
|
560
|
+
children: [
|
|
561
|
+
hovered.label,
|
|
562
|
+
": ",
|
|
563
|
+
available(hovered.value)
|
|
564
|
+
]
|
|
565
|
+
}),
|
|
566
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", { children: percentage(hovered.value) === void 0 ? "Percentage unavailable" : `${percentage(hovered.value).toFixed(1)}% of allocatable${percentage(hovered.value) > 100 ? " (over allocatable)" : ""}` }),
|
|
567
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: ["Allocatable: ", available(denominator)] })
|
|
568
|
+
]
|
|
569
|
+
}),
|
|
570
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
571
|
+
"aria-label": `${label} resource totals`,
|
|
572
|
+
style: {
|
|
573
|
+
display: "grid",
|
|
574
|
+
gap: 6,
|
|
575
|
+
fontSize: 12
|
|
576
|
+
},
|
|
577
|
+
children: [rings.map((ring) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
578
|
+
title: `${ring.label} relative to allocatable capacity. ${ring.label === "Usage" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`,
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
581
|
+
style: { color: ring.color },
|
|
582
|
+
children: "●"
|
|
583
|
+
}),
|
|
584
|
+
" ",
|
|
585
|
+
ring.label,
|
|
586
|
+
": ",
|
|
587
|
+
available(ring.value),
|
|
588
|
+
percentage(ring.value) !== void 0 && /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
589
|
+
" (",
|
|
590
|
+
percentage(ring.value).toFixed(1),
|
|
591
|
+
"%)"
|
|
592
|
+
] })
|
|
593
|
+
]
|
|
594
|
+
}, ring.label)), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
595
|
+
title: allocationDetail,
|
|
596
|
+
children: [
|
|
597
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
598
|
+
style: { color: "#5486b5" },
|
|
599
|
+
children: "●"
|
|
600
|
+
}),
|
|
601
|
+
" Allocatable: ",
|
|
602
|
+
available(denominator)
|
|
603
|
+
]
|
|
604
|
+
})]
|
|
605
|
+
}),
|
|
606
|
+
(status || sample?.resourceStatus) && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
607
|
+
role: "status",
|
|
608
|
+
style: {
|
|
609
|
+
color: "var(--textColorSecondary)",
|
|
610
|
+
fontSize: 11,
|
|
611
|
+
marginTop: 8
|
|
612
|
+
},
|
|
613
|
+
children: status || sample?.resourceStatus
|
|
614
|
+
})
|
|
615
|
+
]
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
function MonitoringCharts({ samples, status }) {
|
|
619
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
620
|
+
style: { containerType: "inline-size" },
|
|
621
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
622
|
+
[data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }
|
|
623
|
+
[data-monitoring-trends], [data-monitoring-summaries] { display:contents }
|
|
624
|
+
@container (min-width: 961px) {
|
|
625
|
+
[data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }
|
|
626
|
+
}
|
|
627
|
+
@container (max-width: 960px) {
|
|
628
|
+
[data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }
|
|
629
|
+
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }
|
|
630
|
+
}
|
|
631
|
+
@container (max-width: 580px) {
|
|
632
|
+
[data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }
|
|
633
|
+
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }
|
|
634
|
+
}
|
|
635
|
+
` }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
636
|
+
"data-monitoring-layout": "",
|
|
637
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
638
|
+
"data-monitoring-trends": "",
|
|
639
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(MetricChart, {
|
|
640
|
+
metric,
|
|
641
|
+
samples,
|
|
642
|
+
status
|
|
643
|
+
}, metric))
|
|
644
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
645
|
+
"data-monitoring-summaries": "",
|
|
646
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(ResourceDonut, {
|
|
647
|
+
metric,
|
|
648
|
+
sample: samples.at(-1),
|
|
649
|
+
status
|
|
650
|
+
}, metric))
|
|
651
|
+
})]
|
|
652
|
+
})]
|
|
144
653
|
});
|
|
145
654
|
}
|
|
146
655
|
function startMonitoring() {
|
|
@@ -156,10 +665,23 @@ function startMonitoring() {
|
|
|
156
665
|
};
|
|
157
666
|
}
|
|
158
667
|
function mountMonitoring() {
|
|
668
|
+
const suppression = document.createElement("style");
|
|
669
|
+
suppression.setAttribute("data-freelens-power-native-metrics-suppression", "");
|
|
670
|
+
suppression.textContent = `
|
|
671
|
+
[data-testid="cluster-overview-page"] > [class*="ClusterMetrics"],
|
|
672
|
+
[data-testid="cluster-overview-page"] > :has([class*="ClusterMetrics"]),
|
|
673
|
+
[data-testid="cluster-overview-page"] > :is([class*="noMetrics"], [data-testid="no-metrics-message"]):not(:has(.PieChart)),
|
|
674
|
+
[data-testid="cluster-overview-page"] > :has([class*="noMetrics"], [data-testid="no-metrics-message"], [class*="cluster-pie-charts"][class*="empty"]):not(:has(.PieChart)) {
|
|
675
|
+
display:none !important;
|
|
676
|
+
}
|
|
677
|
+
`;
|
|
678
|
+
document.head.append(suppression);
|
|
159
679
|
let host;
|
|
160
680
|
let overview = null;
|
|
161
681
|
let samples = [];
|
|
162
682
|
let status = "";
|
|
683
|
+
let renderedSamples;
|
|
684
|
+
let renderedStatus;
|
|
163
685
|
let cluster;
|
|
164
686
|
let stopped = false;
|
|
165
687
|
let pendingGeneration;
|
|
@@ -174,6 +696,8 @@ function mountMonitoring() {
|
|
|
174
696
|
require__global_external_react_dom.default.unmountComponentAtNode(host);
|
|
175
697
|
host.remove();
|
|
176
698
|
host = void 0;
|
|
699
|
+
renderedSamples = void 0;
|
|
700
|
+
renderedStatus = void 0;
|
|
177
701
|
}
|
|
178
702
|
};
|
|
179
703
|
const update = async () => {
|
|
@@ -188,26 +712,50 @@ function mountMonitoring() {
|
|
|
188
712
|
cluster = currentCluster;
|
|
189
713
|
overview = currentOverview;
|
|
190
714
|
}
|
|
191
|
-
|
|
715
|
+
const cutoff = Date.now() - 60 * INTERVAL;
|
|
716
|
+
if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);
|
|
192
717
|
if (!overview || !cluster || document.visibilityState === "hidden") return;
|
|
193
718
|
if (!host) {
|
|
194
719
|
host = document.createElement("div");
|
|
195
720
|
host.setAttribute("data-freelens-power-monitoring", "");
|
|
721
|
+
host.style.gridColumn = "1 / -1";
|
|
722
|
+
hidden.set(overview, overview.getAttribute("style"));
|
|
723
|
+
Object.assign(overview.style, {
|
|
724
|
+
gridTemplateAreas: "none",
|
|
725
|
+
gridTemplateColumns: "minmax(0, 1fr)",
|
|
726
|
+
gridTemplateRows: "none",
|
|
727
|
+
height: "auto"
|
|
728
|
+
});
|
|
196
729
|
overview.prepend(host);
|
|
197
730
|
}
|
|
198
731
|
for (const title of overview.querySelectorAll(".PieChart > .chart-title")) {
|
|
199
732
|
if (title.textContent !== "CPU" && title.textContent !== "Memory") continue;
|
|
200
733
|
const panel = title.closest(".PieChart")?.parentElement;
|
|
201
|
-
if (panel &&
|
|
202
|
-
hidden.set(panel, panel.getAttribute("style"));
|
|
734
|
+
if (panel && panel.style.display !== "none") {
|
|
735
|
+
if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute("style"));
|
|
203
736
|
panel.style.display = "none";
|
|
204
737
|
}
|
|
205
738
|
}
|
|
739
|
+
for (const child of overview.children) {
|
|
740
|
+
if (!(child instanceof HTMLElement) || child === host) continue;
|
|
741
|
+
const nativeMetrics = child.matches("[class*=\"ClusterMetrics\"]") || child.querySelector("[class*=\"ClusterMetrics\"]");
|
|
742
|
+
const unavailablePies = (child.matches("[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]") || child.querySelector("[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]")) && !child.querySelector(".PieChart");
|
|
743
|
+
if ((nativeMetrics || unavailablePies) && child.style.display !== "none") {
|
|
744
|
+
if (!hidden.has(child)) hidden.set(child, child.getAttribute("style"));
|
|
745
|
+
child.style.display = "none";
|
|
746
|
+
}
|
|
747
|
+
if (!hidden.has(child)) hidden.set(child, child.getAttribute("style"));
|
|
748
|
+
child.style.gridArea = "auto";
|
|
749
|
+
}
|
|
206
750
|
const render = () => {
|
|
207
|
-
if (host
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
751
|
+
if (host && (renderedSamples !== samples || renderedStatus !== status)) {
|
|
752
|
+
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(MonitoringCharts, {
|
|
753
|
+
samples,
|
|
754
|
+
status
|
|
755
|
+
}), host);
|
|
756
|
+
renderedSamples = samples;
|
|
757
|
+
renderedStatus = status;
|
|
758
|
+
}
|
|
211
759
|
};
|
|
212
760
|
render();
|
|
213
761
|
if (pendingGeneration === generation || Date.now() < nextSample) return;
|
|
@@ -216,9 +764,28 @@ function mountMonitoring() {
|
|
|
216
764
|
const requestGeneration = generation;
|
|
217
765
|
const isCurrent = () => !stopped && requestGeneration === generation && document.visibilityState !== "hidden" && overview?.isConnected === true && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;
|
|
218
766
|
try {
|
|
219
|
-
const
|
|
767
|
+
const [metrics, inventory, pods] = await Promise.allSettled([
|
|
768
|
+
require_extensions.Renderer.K8sApi.nodesMetricsApi.list(),
|
|
769
|
+
Promise.resolve().then(() => require_extensions.Renderer.K8sApi.nodesApi.list()),
|
|
770
|
+
Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsApi.list())
|
|
771
|
+
]);
|
|
220
772
|
if (!isCurrent()) return;
|
|
221
|
-
|
|
773
|
+
if (metrics.status !== "fulfilled") throw new Error("Metrics unavailable");
|
|
774
|
+
const sample = aggregateMetrics(metrics.value ?? []);
|
|
775
|
+
const warnings = [];
|
|
776
|
+
if (inventory.status === "fulfilled") try {
|
|
777
|
+
sample.resources = aggregateResources(inventory.value ?? [], pods.status === "fulfilled" ? pods.value ?? void 0 : void 0);
|
|
778
|
+
const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));
|
|
779
|
+
const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));
|
|
780
|
+
sample.completeUsage = expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));
|
|
781
|
+
if (!sample.completeUsage) warnings.push("Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable");
|
|
782
|
+
} catch {
|
|
783
|
+
warnings.push("Resource totals unavailable: incomplete or invalid node/pod data");
|
|
784
|
+
}
|
|
785
|
+
else warnings.push("Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable");
|
|
786
|
+
if (pods.status !== "fulfilled") warnings.push("Cluster-wide Pods unavailable or access denied; requests unavailable");
|
|
787
|
+
sample.resourceStatus = warnings.join(". ");
|
|
788
|
+
samples = [...samples, sample].slice(-60);
|
|
222
789
|
status = "";
|
|
223
790
|
} catch {
|
|
224
791
|
if (!isCurrent()) return;
|
|
@@ -228,6 +795,13 @@ function mountMonitoring() {
|
|
|
228
795
|
}
|
|
229
796
|
if (!stopped && requestGeneration === generation) render();
|
|
230
797
|
};
|
|
798
|
+
const observer = new MutationObserver(() => {
|
|
799
|
+
update();
|
|
800
|
+
});
|
|
801
|
+
observer.observe(document.body, {
|
|
802
|
+
childList: true,
|
|
803
|
+
subtree: true
|
|
804
|
+
});
|
|
231
805
|
const timer = window.setInterval(() => {
|
|
232
806
|
update();
|
|
233
807
|
}, 1e3);
|
|
@@ -236,12 +810,18 @@ function mountMonitoring() {
|
|
|
236
810
|
stopped = true;
|
|
237
811
|
generation++;
|
|
238
812
|
window.clearInterval(timer);
|
|
813
|
+
observer.disconnect();
|
|
814
|
+
suppression.remove();
|
|
239
815
|
restore();
|
|
240
816
|
samples = [];
|
|
241
817
|
};
|
|
242
818
|
}
|
|
243
819
|
//#endregion
|
|
820
|
+
exports.MetricChart = MetricChart;
|
|
244
821
|
exports.aggregateMetrics = aggregateMetrics;
|
|
822
|
+
exports.aggregateResources = aggregateResources;
|
|
823
|
+
exports.effectivePodResource = effectivePodResource;
|
|
824
|
+
exports.formatMonitoringTime = formatMonitoringTime;
|
|
245
825
|
exports.parseQuantity = parseQuantity;
|
|
246
826
|
exports.startMonitoring = startMonitoring;
|
|
247
827
|
|