fix: add axes to sentinel memory chart
This commit is contained in:
@@ -977,13 +977,12 @@ select {
|
||||
.memory-chart {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 154px;
|
||||
height: 192px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.memory-axis,
|
||||
.memory-legend {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -993,6 +992,44 @@ select {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.memory-grid-line {
|
||||
stroke: #e5ecea;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.memory-axis-line {
|
||||
stroke: #71817c;
|
||||
stroke-width: 1.2;
|
||||
}
|
||||
|
||||
.memory-tick-line {
|
||||
stroke: #71817c;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.memory-axis-label {
|
||||
fill: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.memory-axis-label-y {
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.memory-axis-label-x {
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.memory-axis-title {
|
||||
fill: #4f625d;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.memory-axis-title-x {
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -4,6 +4,15 @@ import { createApp, computed, onMounted, ref, watch } from "./vendor/vue.esm-bro
|
||||
|
||||
const bootstrap = readBootstrap();
|
||||
const internalTextPattern = /水合|投影|Trace|trace|Shell|API|DOM|Console|console|Runner|runner|JSONL|steer|facts|分页|HTTP|http|requestfailed|pageerror|Final Response|Code Agent|web-probe|observe|analyzer|终态/u;
|
||||
const memoryChartFrame = Object.freeze({
|
||||
left: 64,
|
||||
right: 696,
|
||||
top: 20,
|
||||
bottom: 132,
|
||||
yLabelX: 56,
|
||||
xLabelY: 154,
|
||||
xTitleY: 172,
|
||||
});
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
@@ -113,6 +122,18 @@ createApp({
|
||||
});
|
||||
const detailMemoryMax = computed(() => Math.max(1, ...detailMemorySeries.value.flatMap((series) => series.points.map((point) => number(point.memoryMb)))));
|
||||
const detailMemoryDurationMax = computed(() => Math.max(1, ...detailMemorySeries.value.flatMap((series) => series.points.map((point) => number(point.elapsedMinutes)))));
|
||||
const detailMemoryYAxisTicks = computed(() => memoryTickValues(detailMemoryMax.value).map((value) => ({
|
||||
key: `memory-y-${value}`,
|
||||
value,
|
||||
y: memoryYValue(value),
|
||||
label: formatMb(value),
|
||||
})));
|
||||
const detailMemoryXAxisTicks = computed(() => memoryTickValues(detailMemoryDurationMax.value).map((value) => ({
|
||||
key: `memory-x-${value}`,
|
||||
value,
|
||||
x: memoryXValue(value),
|
||||
label: formatMinuteTick(value),
|
||||
})));
|
||||
const scopedCheckFindings = computed(() => {
|
||||
if (checkScope.value === "history") return historicalCheckFindings.value;
|
||||
return runDetailCheckFindings.value;
|
||||
@@ -318,12 +339,22 @@ createApp({
|
||||
|
||||
function memoryX(point) {
|
||||
const elapsed = number(point?.elapsedMinutes);
|
||||
return Math.round(24 + (elapsed / detailMemoryDurationMax.value) * 672);
|
||||
return memoryXValue(elapsed);
|
||||
}
|
||||
|
||||
function memoryY(point) {
|
||||
const memory = number(point?.memoryMb);
|
||||
return Math.round(126 - (memory / detailMemoryMax.value) * 102);
|
||||
return memoryYValue(memory);
|
||||
}
|
||||
|
||||
function memoryXValue(elapsedMinutes) {
|
||||
const ratio = clamp(number(elapsedMinutes) / detailMemoryDurationMax.value, 0, 1);
|
||||
return Math.round(memoryChartFrame.left + ratio * (memoryChartFrame.right - memoryChartFrame.left));
|
||||
}
|
||||
|
||||
function memoryYValue(memoryMb) {
|
||||
const ratio = clamp(number(memoryMb) / detailMemoryMax.value, 0, 1);
|
||||
return Math.round(memoryChartFrame.bottom - ratio * (memoryChartFrame.bottom - memoryChartFrame.top));
|
||||
}
|
||||
|
||||
function memoryLineClass(index) {
|
||||
@@ -402,6 +433,9 @@ createApp({
|
||||
detailMemorySeries,
|
||||
detailMemoryMax,
|
||||
detailMemoryDurationMax,
|
||||
detailMemoryYAxisTicks,
|
||||
detailMemoryXAxisTicks,
|
||||
memoryFrame: memoryChartFrame,
|
||||
scopedCheckFindings,
|
||||
scopedCheckSummary,
|
||||
visibleCheckFindings,
|
||||
@@ -724,10 +758,23 @@ createApp({
|
||||
<span class="tag">每个页面一条线 · 峰值 {{ formatMb(detailMemory.maxMemoryMb || detailMemoryMax) }}</span>
|
||||
</div>
|
||||
<div v-if="detailMemorySeries.length > 0" class="memory-chart-wrap">
|
||||
<svg class="memory-chart" viewBox="0 0 720 142" role="img" aria-label="运行详情页面内存曲线">
|
||||
<line class="trend-grid-line" x1="24" x2="696" y1="24" y2="24"></line>
|
||||
<line class="trend-grid-line" x1="24" x2="696" y1="75" y2="75"></line>
|
||||
<line class="trend-grid-line" x1="24" x2="696" y1="126" y2="126"></line>
|
||||
<svg class="memory-chart" viewBox="0 0 720 178" role="img" aria-label="运行详情页面内存曲线,纵轴为 MB,横轴为运行分钟">
|
||||
<g data-memory-axis-y="true">
|
||||
<g v-for="tick in detailMemoryYAxisTicks" :key="tick.key" data-memory-axis-y-tick="true">
|
||||
<line class="memory-grid-line" :x1="memoryFrame.left" :x2="memoryFrame.right" :y1="tick.y" :y2="tick.y"></line>
|
||||
<text class="memory-axis-label memory-axis-label-y" :x="memoryFrame.yLabelX" :y="tick.y + 4">{{ tick.label }}</text>
|
||||
</g>
|
||||
<line class="memory-axis-line" :x1="memoryFrame.left" :x2="memoryFrame.left" :y1="memoryFrame.top" :y2="memoryFrame.bottom"></line>
|
||||
<text class="memory-axis-title memory-axis-title-y" x="18" y="78" transform="rotate(-90 18 78)">内存 MB</text>
|
||||
</g>
|
||||
<g data-memory-axis-x="true">
|
||||
<line class="memory-axis-line" :x1="memoryFrame.left" :x2="memoryFrame.right" :y1="memoryFrame.bottom" :y2="memoryFrame.bottom"></line>
|
||||
<g v-for="tick in detailMemoryXAxisTicks" :key="tick.key" data-memory-axis-x-tick="true">
|
||||
<line class="memory-tick-line" :x1="tick.x" :x2="tick.x" :y1="memoryFrame.bottom" :y2="memoryFrame.bottom + 6"></line>
|
||||
<text class="memory-axis-label memory-axis-label-x" :x="tick.x" :y="memoryFrame.xLabelY">{{ tick.label }}</text>
|
||||
</g>
|
||||
<text class="memory-axis-title memory-axis-title-x" :x="(memoryFrame.left + memoryFrame.right) / 2" :y="memoryFrame.xTitleY">运行分钟</text>
|
||||
</g>
|
||||
<polyline
|
||||
v-for="(series, index) in detailMemorySeries"
|
||||
:key="series.key || series.label || index"
|
||||
@@ -736,11 +783,6 @@ createApp({
|
||||
:points="memoryPolyline(series)"
|
||||
></polyline>
|
||||
</svg>
|
||||
<div class="memory-axis">
|
||||
<span>0m</span>
|
||||
<span>{{ formatMinutes(detailMemoryDurationMax) }}</span>
|
||||
<span>峰值 {{ formatMb(detailMemory.maxMemoryMb || detailMemoryMax) }}</span>
|
||||
</div>
|
||||
<div class="memory-legend">
|
||||
<span
|
||||
v-for="(series, index) in detailMemorySeries"
|
||||
@@ -1065,6 +1107,14 @@ function formatMinutes(value) {
|
||||
return `${Number.isInteger(rounded) ? String(rounded) : String(rounded).replace(/0+$/u, "").replace(/\.$/u, "")} 分钟`;
|
||||
}
|
||||
|
||||
function formatMinuteTick(value) {
|
||||
const numberValue = optionalNumber(value);
|
||||
if (numberValue === null) return "-";
|
||||
const rounded = Math.round(numberValue * 100) / 100;
|
||||
const text = Number.isInteger(rounded) ? String(rounded) : String(rounded).replace(/0+$/u, "").replace(/\.$/u, "");
|
||||
return `${text}m`;
|
||||
}
|
||||
|
||||
function formatMb(value) {
|
||||
const numberValue = optionalNumber(value);
|
||||
if (numberValue === null) return "-";
|
||||
@@ -1073,6 +1123,11 @@ function formatMb(value) {
|
||||
return `${text} MB`;
|
||||
}
|
||||
|
||||
function memoryTickValues(maxValue) {
|
||||
const max = Math.max(1, number(maxValue));
|
||||
return [0, max / 2, max];
|
||||
}
|
||||
|
||||
function runDurationText(run) {
|
||||
const timing = run?.timing || {};
|
||||
return formatMinutes(optionalNumber(run?.runDurationMinutes, run?.durationMinutes, timing.runDurationMinutes, timing.durationMinutes));
|
||||
|
||||
@@ -687,6 +687,7 @@ const dom = await page.evaluate(async ({ expectedRoutePrefix, expectedSentinelId
|
||||
const targetDetailPayload = objectOrNull(targetDetailResult.body) || {};
|
||||
const targetMemory = objectOrNull(targetDetailPayload.memory) || {};
|
||||
const targetPageSeries = Array.isArray(targetMemory.pageSeries) ? targetMemory.pageSeries : [];
|
||||
const memoryAxisText = String(memoryChart?.textContent || "").replace(/\s+/g, " ").trim();
|
||||
const memorySummary = {
|
||||
present: Boolean(memoryChart),
|
||||
runId: memoryChart?.getAttribute("data-memory-run-id") || null,
|
||||
@@ -703,6 +704,12 @@ const dom = await page.evaluate(async ({ expectedRoutePrefix, expectedSentinelId
|
||||
apiSampleCount: numberValue(targetMemory.sampleCount),
|
||||
apiSource: targetMemory.source || null,
|
||||
perPageLines: false,
|
||||
axisX: Boolean(memoryChart?.querySelector("[data-memory-axis-x='true']")),
|
||||
axisY: Boolean(memoryChart?.querySelector("[data-memory-axis-y='true']")),
|
||||
axisXTickCount: document.querySelectorAll(".memory-chart [data-memory-axis-x-tick='true']").length,
|
||||
axisYTickCount: document.querySelectorAll(".memory-chart [data-memory-axis-y-tick='true']").length,
|
||||
axisHasUnits: memoryAxisText.includes("MB") && memoryAxisText.includes("运行分钟"),
|
||||
axesOk: false,
|
||||
};
|
||||
memorySummary.perPageLines = memorySummary.present === true
|
||||
&& memorySummary.matchesTargetRun === true
|
||||
@@ -711,6 +718,11 @@ const dom = await page.evaluate(async ({ expectedRoutePrefix, expectedSentinelId
|
||||
&& memorySummary.legendCount === memorySummary.pageCount
|
||||
&& memorySummary.apiOk === true
|
||||
&& memorySummary.apiPageCount === memorySummary.pageCount;
|
||||
memorySummary.axesOk = memorySummary.axisX === true
|
||||
&& memorySummary.axisY === true
|
||||
&& memorySummary.axisXTickCount >= 3
|
||||
&& memorySummary.axisYTickCount >= 3
|
||||
&& memorySummary.axisHasUnits === true;
|
||||
const datasetSentinelId = String(dataset.sentinelId || "");
|
||||
const finalPath = new URL(window.location.href).pathname.replace(/\/+$/u, "") || "/";
|
||||
const expectedPath = expectedRoutePrefix.replace(/\/+$/u, "") || "/";
|
||||
@@ -770,6 +782,7 @@ const dom = await page.evaluate(async ({ expectedRoutePrefix, expectedSentinelId
|
||||
latestFindingTypeCount: latestRunCounts.typeCount,
|
||||
trendCurves: chartTiming.ok,
|
||||
memoryPerPageLines: memorySummary.perPageLines,
|
||||
memoryAxes: memorySummary.axesOk,
|
||||
},
|
||||
sentinelBoundary,
|
||||
title: document.title,
|
||||
@@ -822,6 +835,7 @@ const ok = !navigationError
|
||||
&& dom.requestedRunSelection?.ok === true
|
||||
&& dom.chartTiming?.ok === true
|
||||
&& dom.memorySummary?.perPageLines === true
|
||||
&& dom.memorySummary?.axesOk === true
|
||||
&& dom.layout?.horizontalOverflow !== true
|
||||
&& pageErrors.length === 0;
|
||||
|
||||
@@ -1326,7 +1340,7 @@ function renderDashboardResult(result: Record<string, unknown>): string {
|
||||
chartTiming.hasWarningCurve ?? "-",
|
||||
]]),
|
||||
"",
|
||||
table(["MEMORY_CHART", "MEMORY_RUN", "MEMORY_MATCH", "MEMORY_PAGES", "MEMORY_CURVES", "MEMORY_SAMPLES", "API_PAGES", "MEMORY_PAGE_LINES", "MEMORY_SOURCE"], [[
|
||||
table(["MEMORY_CHART", "MEMORY_RUN", "MEMORY_MATCH", "MEMORY_PAGES", "MEMORY_CURVES", "MEMORY_SAMPLES", "API_PAGES", "MEMORY_PAGE_LINES", "MEMORY_AXES", "X_TICKS", "Y_TICKS", "MEMORY_SOURCE"], [[
|
||||
memorySummary.present ?? "-",
|
||||
memorySummary.runId ?? "-",
|
||||
memorySummary.matchesTargetRun ?? "-",
|
||||
@@ -1335,6 +1349,9 @@ function renderDashboardResult(result: Record<string, unknown>): string {
|
||||
memorySummary.sampleCount ?? "-",
|
||||
memorySummary.apiPageCount ?? "-",
|
||||
memorySummary.perPageLines ?? "-",
|
||||
memorySummary.axesOk ?? "-",
|
||||
memorySummary.axisXTickCount ?? "-",
|
||||
memorySummary.axisYTickCount ?? "-",
|
||||
memorySummary.source ?? memorySummary.apiSource ?? "-",
|
||||
]]),
|
||||
"",
|
||||
|
||||
@@ -12,13 +12,19 @@ const checks: Array<{ readonly path: string; readonly contains: readonly string[
|
||||
"data-monitor-independent-scroll",
|
||||
"data-check-row",
|
||||
"data-check-dialog",
|
||||
"data-memory-axis-x",
|
||||
"data-memory-axis-y",
|
||||
"rootCause",
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "scripts/assets/web-probe-sentinel-monitor-web/monitor-web.css",
|
||||
minBytes: 8_000,
|
||||
contains: [],
|
||||
contains: [
|
||||
".memory-axis-line",
|
||||
".memory-axis-label",
|
||||
".memory-axis-title",
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "scripts/assets/web-probe-sentinel-monitor-web/vendor/vue.esm-browser.prod.js",
|
||||
|
||||
Reference in New Issue
Block a user