From 0ebdbbe07c382c2211fa814849b084408d1fe9ef Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 10 Jul 2026 14:09:17 +0100 Subject: [PATCH] feat(frontend): stripe the reclaimable-cache memory segment At warning level the used segment turns threshold-yellow, which sat next to the amber cache segment as one indistinguishable yellow mass. Give the cache segment a vertical-stripe SVG pattern so the used/cache split stays readable at every severity color. Stripes are vertical because the bar's preserveAspectRatio=none scaling would shear a diagonal texture. --- .../components/Workloads/StackedMemoryBar.tsx | 28 ++++++++++++++++++- .../__tests__/stackedMemoryBarModel.test.ts | 3 ++ .../Workloads/stackedMemoryBarModel.ts | 3 ++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/frontend-modern/src/components/Workloads/StackedMemoryBar.tsx b/frontend-modern/src/components/Workloads/StackedMemoryBar.tsx index 0d72eeabbb..5dd79e218d 100644 --- a/frontend-modern/src/components/Workloads/StackedMemoryBar.tsx +++ b/frontend-modern/src/components/Workloads/StackedMemoryBar.tsx @@ -1,4 +1,4 @@ -import { Show, For } from 'solid-js'; +import { Show, For, createUniqueId } from 'solid-js'; import { AnimatedNumber } from '@/components/shared/AnimatedNumber'; import { TooltipPortal } from '@/components/shared/TooltipPortal'; import { formatPercent } from '@/utils/format'; @@ -8,6 +8,7 @@ import { useStackedMemoryBarState } from './useStackedMemoryBarState'; export function StackedMemoryBar(props: StackedMemoryBarProps) { const state = useStackedMemoryBarState(props); const presentation = state.presentation; + const stripePatternId = createUniqueId(); const swapBarWidth = () => String(Math.max(0, Math.min(presentation().swapBarPercent, 100))); const segmentEdge = (leftPercent: number, widthPercent: number) => String(Math.max(0, Math.min(leftPercent + widthPercent, 100))); @@ -28,6 +29,19 @@ export function StackedMemoryBar(props: StackedMemoryBarProps) { viewBox="0 0 100 100" preserveAspectRatio="none" > + {/* Vertical stripes only: the non-uniform viewBox scaling would + shear any diagonal texture. Keeps the reclaimable-cache segment + distinguishable from a warning-yellow used segment. */} + + + + + {(segment, idx) => ( <> @@ -41,6 +55,18 @@ export function StackedMemoryBar(props: StackedMemoryBarProps) { rx="3" fill={segment.color} /> + + + { ]); expect(presentation.segments[1].leftPercent).toBeCloseTo(25); expect(presentation.segments[1].widthPercent).toBeCloseTo(37.5); + // Cache is textured so it never blends into a warning-yellow used segment. + expect(presentation.segments[1].striped).toBe(true); + expect(presentation.segments[0].striped).toBeUndefined(); const rows = Object.fromEntries(presentation.tooltipRows.map((row) => [row.label, row.value])); expect(rows['Used']).toBe('4.00 GB'); diff --git a/frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts b/frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts index 06e1970118..6ee8997510 100644 --- a/frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts +++ b/frontend-modern/src/components/Workloads/stackedMemoryBarModel.ts @@ -29,6 +29,8 @@ export interface StackedMemorySegment { label: string; leftPercent: number; widthPercent: number; + /** Diagonal-stripe texture so reclaimable cache stays distinguishable from a warning-yellow used segment. */ + striped?: boolean; } export interface StackedMemoryTooltipRow { @@ -129,6 +131,7 @@ function getSegments( label: 'Reclaimable', leftPercent: usedPercent, widthPercent: cachePercent, + striped: true, }); }