feat(dashboard): add light-mode theme awareness to ModelCard and TopologyGraph
Port v1's component-level theme color mappings into v2's architecture: - ModelCard: import theme store, add derived `tc` color object with light/dark variants for SVG strokes, fills, labels, and scanlines - TopologyGraph: import theme store, add derived `themeColors` object with 20+ D3 color properties (device cases, screens, wires, labels, GPU chips, shadows, highlights); re-render graph on theme change All hardcoded dark-mode-only colors (#FFD700, #4B5563, #0a0a0a, etc.) replaced with theme-reactive values using v2's warm parchment palette. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
412f66d523
commit
ab427f1b75
@@ -6,6 +6,27 @@
|
||||
TopologyEdge,
|
||||
} from "$lib/stores/app.svelte";
|
||||
import { debugMode, topologyData } from "$lib/stores/app.svelte";
|
||||
import { theme } from "$lib/stores/theme.svelte";
|
||||
|
||||
// Theme-aware colors for SVG elements
|
||||
let tc = $derived({
|
||||
accent: theme.isLight ? "#374151" : "#FFD700",
|
||||
accentDim: theme.isLight ? "rgba(26,26,26,0.06)" : "rgba(255,215,0,0.1)",
|
||||
accentGlow: theme.isLight ? "rgba(0,0,0,0.03)" : "rgba(255,215,0,0.1)",
|
||||
outlineActive: theme.isLight ? "#374151" : "#FFD700",
|
||||
outlineInactive: theme.isLight ? "#d1d5db" : "#4B5563",
|
||||
strokeActive: theme.isLight ? "#374151" : "#FFD700",
|
||||
strokeInactive: theme.isLight ? "#d1d5db" : "#4B5563",
|
||||
caseFill: theme.isLight ? "#f3f4f6" : "#0a0a0a",
|
||||
caseDetail: theme.isLight ? "#d1d5db" : "#374151",
|
||||
errorDot: theme.isLight ? "#dc2626" : "#f87171",
|
||||
statusInactive: theme.isLight ? "#9ca3af" : "#4B5563",
|
||||
connGood: theme.isLight ? "rgba(30,30,30,0.85)" : "rgba(255,255,255,0.85)",
|
||||
connBad: theme.isLight ? "rgba(220,38,38,0.85)" : "rgba(248,113,113,0.85)",
|
||||
scanlineColor: theme.isLight
|
||||
? "rgba(0,0,0,0.01)"
|
||||
: "rgba(255,215,0,0.02)",
|
||||
});
|
||||
|
||||
interface Props {
|
||||
model: { id: string; name?: string; storage_size_megabytes?: number };
|
||||
@@ -491,7 +512,7 @@
|
||||
<div
|
||||
class="bg-exo-dark-gray/60 border {canFit
|
||||
? 'border-exo-yellow/20 group-hover:border-exo-yellow/40'
|
||||
: 'border-red-500/20'} p-3 transition-all duration-200 group-hover:shadow-[0_0_15px_rgba(255,215,0,0.1)]"
|
||||
: 'border-red-500/20'} p-3 transition-all duration-200 group-hover:shadow-[0_0_15px_var(--exo-glow-yellow)]"
|
||||
>
|
||||
<!-- Model Name & Memory Required -->
|
||||
<div class="flex items-start justify-between gap-2 mb-2">
|
||||
@@ -589,7 +610,8 @@
|
||||
>
|
||||
<!-- Scanline effect -->
|
||||
<div
|
||||
class="absolute inset-0 bg-[repeating-linear-gradient(0deg,transparent,transparent_2px,rgba(255,215,0,0.02)_2px,rgba(255,215,0,0.02)_4px)] pointer-events-none"
|
||||
style="background: repeating-linear-gradient(0deg, transparent, transparent 2px, {tc.scanlineColor} 2px, {tc.scanlineColor} 4px)"
|
||||
class="absolute inset-0 pointer-events-none"
|
||||
></div>
|
||||
|
||||
<svg
|
||||
@@ -670,7 +692,9 @@
|
||||
y1={node.y}
|
||||
x2={node2.x}
|
||||
y2={node2.y}
|
||||
stroke={node.isUsed && node2.isUsed ? "#FFD700" : "#374151"}
|
||||
stroke={node.isUsed && node2.isUsed
|
||||
? tc.strokeActive
|
||||
: tc.strokeInactive}
|
||||
stroke-width="1"
|
||||
stroke-dasharray={node.isUsed && node2.isUsed ? "4,2" : "2,4"}
|
||||
opacity={node.isUsed && node2.isUsed ? 0.4 : 0.15}
|
||||
@@ -706,9 +730,7 @@
|
||||
dominant-baseline="hanging"
|
||||
font-size="6"
|
||||
font-family="SF Mono, Monaco, monospace"
|
||||
fill={conn.iface
|
||||
? "rgba(255,255,255,0.85)"
|
||||
: "rgba(248,113,113,0.85)"}
|
||||
fill={conn.iface ? tc.connGood : tc.connBad}
|
||||
>
|
||||
{conn.arrow}
|
||||
{isRdma
|
||||
@@ -725,9 +747,7 @@
|
||||
dominant-baseline="hanging"
|
||||
font-size="6"
|
||||
font-family="SF Mono, Monaco, monospace"
|
||||
fill={conn.iface
|
||||
? "rgba(255,255,255,0.85)"
|
||||
: "rgba(248,113,113,0.85)"}
|
||||
fill={conn.iface ? tc.connGood : tc.connBad}
|
||||
>
|
||||
{conn.arrow}
|
||||
{isRdma
|
||||
@@ -746,9 +766,7 @@
|
||||
dominant-baseline="auto"
|
||||
font-size="6"
|
||||
font-family="SF Mono, Monaco, monospace"
|
||||
fill={conn.iface
|
||||
? "rgba(255,255,255,0.85)"
|
||||
: "rgba(248,113,113,0.85)"}
|
||||
fill={conn.iface ? tc.connGood : tc.connBad}
|
||||
>
|
||||
{conn.arrow}
|
||||
{isRdma
|
||||
@@ -767,9 +785,7 @@
|
||||
dominant-baseline="auto"
|
||||
font-size="6"
|
||||
font-family="SF Mono, Monaco, monospace"
|
||||
fill={conn.iface
|
||||
? "rgba(255,255,255,0.85)"
|
||||
: "rgba(248,113,113,0.85)"}
|
||||
fill={conn.iface ? tc.connGood : tc.connBad}
|
||||
>
|
||||
{conn.arrow}
|
||||
{isRdma
|
||||
@@ -801,7 +817,7 @@
|
||||
height={node.iconSize * 0.65}
|
||||
rx="2"
|
||||
fill="none"
|
||||
stroke={node.isUsed ? "#FFD700" : "#4B5563"}
|
||||
stroke={node.isUsed ? tc.strokeActive : tc.outlineInactive}
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Screen area (memory fill container) -->
|
||||
@@ -810,7 +826,7 @@
|
||||
y="2"
|
||||
width={node.iconSize - 8}
|
||||
height={node.screenHeight}
|
||||
fill="#0a0a0a"
|
||||
fill={tc.caseFill}
|
||||
/>
|
||||
<!-- Current memory fill (gray) -->
|
||||
<rect
|
||||
@@ -818,7 +834,7 @@
|
||||
y={2 + node.screenHeight - node.currentFillHeight}
|
||||
width={node.iconSize - 8}
|
||||
height={node.currentFillHeight}
|
||||
fill="#374151"
|
||||
fill={tc.caseDetail}
|
||||
/>
|
||||
<!-- New model memory fill (glowing yellow) -->
|
||||
{#if node.modelUsageGB > 0 && node.isUsed}
|
||||
@@ -830,7 +846,7 @@
|
||||
node.modelFillHeight}
|
||||
width={node.iconSize - 8}
|
||||
height={node.modelFillHeight}
|
||||
fill="#FFD700"
|
||||
fill={tc.accent}
|
||||
filter="url(#memGlow-{filterId})"
|
||||
class="animate-pulse-slow"
|
||||
/>
|
||||
@@ -842,7 +858,7 @@
|
||||
0.68} L {node.iconSize - 2} {node.iconSize *
|
||||
0.78} L 2 {node.iconSize * 0.78} Z"
|
||||
fill="none"
|
||||
stroke={node.isUsed ? "#FFD700" : "#4B5563"}
|
||||
stroke={node.isUsed ? tc.strokeActive : tc.outlineInactive}
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</g>
|
||||
@@ -859,7 +875,7 @@
|
||||
height={node.iconSize - 4}
|
||||
rx="4"
|
||||
fill="none"
|
||||
stroke={node.isUsed ? "#FFD700" : "#4B5563"}
|
||||
stroke={node.isUsed ? tc.strokeActive : tc.outlineInactive}
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Memory fill background -->
|
||||
@@ -868,7 +884,7 @@
|
||||
y="4"
|
||||
width={node.iconSize - 8}
|
||||
height={node.iconSize - 8}
|
||||
fill="#0a0a0a"
|
||||
fill={tc.caseFill}
|
||||
/>
|
||||
<!-- Current memory fill -->
|
||||
<rect
|
||||
@@ -877,7 +893,7 @@
|
||||
(node.iconSize - 8) * (1 - node.currentPercent / 100)}
|
||||
width={node.iconSize - 8}
|
||||
height={(node.iconSize - 8) * (node.currentPercent / 100)}
|
||||
fill="#374151"
|
||||
fill={tc.caseDetail}
|
||||
/>
|
||||
<!-- New model memory fill -->
|
||||
{#if node.modelUsageGB > 0 && node.isUsed}
|
||||
@@ -887,7 +903,7 @@
|
||||
width={node.iconSize - 8}
|
||||
height={(node.iconSize - 8) *
|
||||
((node.newPercent - node.currentPercent) / 100)}
|
||||
fill="#FFD700"
|
||||
fill={tc.accent}
|
||||
filter="url(#memGlow-{filterId})"
|
||||
class="animate-pulse-slow"
|
||||
/>
|
||||
@@ -906,7 +922,7 @@
|
||||
height={node.iconSize * 0.4}
|
||||
rx="3"
|
||||
fill="none"
|
||||
stroke={node.isUsed ? "#FFD700" : "#4B5563"}
|
||||
stroke={node.isUsed ? tc.strokeActive : tc.outlineInactive}
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<!-- Memory fill background -->
|
||||
@@ -915,7 +931,7 @@
|
||||
y={node.iconSize * 0.32}
|
||||
width={node.iconSize - 8}
|
||||
height={node.iconSize * 0.36}
|
||||
fill="#0a0a0a"
|
||||
fill={tc.caseFill}
|
||||
/>
|
||||
<!-- Current memory fill -->
|
||||
<rect
|
||||
@@ -924,7 +940,7 @@
|
||||
node.iconSize * 0.36 * (1 - node.currentPercent / 100)}
|
||||
width={node.iconSize - 8}
|
||||
height={node.iconSize * 0.36 * (node.currentPercent / 100)}
|
||||
fill="#374151"
|
||||
fill={tc.caseDetail}
|
||||
/>
|
||||
<!-- New model memory fill -->
|
||||
{#if node.modelUsageGB > 0 && node.isUsed}
|
||||
@@ -936,7 +952,7 @@
|
||||
height={node.iconSize *
|
||||
0.36 *
|
||||
((node.newPercent - node.currentPercent) / 100)}
|
||||
fill="#FFD700"
|
||||
fill={tc.accent}
|
||||
filter="url(#memGlow-{filterId})"
|
||||
class="animate-pulse-slow"
|
||||
/>
|
||||
@@ -955,8 +971,8 @@
|
||||
0.75} {node.iconSize /
|
||||
2},{node.iconSize} 0,{node.iconSize *
|
||||
0.75} 0,{node.iconSize * 0.25}"
|
||||
fill={node.isUsed ? "rgba(255,215,0,0.1)" : "#0a0a0a"}
|
||||
stroke={node.isUsed ? "#FFD700" : "#4B5563"}
|
||||
fill={node.isUsed ? tc.accentDim : tc.caseFill}
|
||||
stroke={node.isUsed ? tc.strokeActive : tc.outlineInactive}
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</g>
|
||||
@@ -970,9 +986,9 @@
|
||||
font-family="SF Mono, Monaco, monospace"
|
||||
fill={node.isUsed
|
||||
? node.newPercent > 90
|
||||
? "#f87171"
|
||||
: "#FFD700"
|
||||
: "#4B5563"}
|
||||
? tc.errorDot
|
||||
: tc.accent
|
||||
: tc.statusInactive}
|
||||
>
|
||||
{node.newPercent.toFixed(0)}%
|
||||
</text>
|
||||
|
||||
@@ -10,6 +10,53 @@
|
||||
nodeIdentities,
|
||||
type NodeInfo,
|
||||
} from "$lib/stores/app.svelte";
|
||||
import { theme } from "$lib/stores/theme.svelte";
|
||||
|
||||
// Theme-aware colors for D3-rendered SVG elements
|
||||
let themeColors = $derived({
|
||||
accent: theme.isLight ? "oklch(0.50 0.14 65)" : "oklch(0.85 0.18 85)",
|
||||
accentRgb: theme.isLight ? "55,40,15" : "255,215,0",
|
||||
deviceCase: theme.isLight ? "#e8e8e8" : "#1a1a1a",
|
||||
deviceCaseDark: theme.isLight ? "#d4d4d4" : "#2c2c2c",
|
||||
deviceScreen: theme.isLight ? "#f0f0f5" : "#0a0a12",
|
||||
deviceScreenFill: theme.isLight
|
||||
? "rgba(240,242,248,0.95)"
|
||||
: "rgba(0,20,40,0.9)",
|
||||
labelWhite: theme.isLight ? "#1a1a1a" : "#FFFFFF",
|
||||
labelMuted: theme.isLight ? "rgba(80,80,80,0.9)" : "rgba(179,179,179,0.9)",
|
||||
labelDim: theme.isLight ? "rgba(100,100,100,0.7)" : "rgba(179,179,179,0.7)",
|
||||
wireDefault: theme.isLight
|
||||
? "rgba(120,120,120,0.6)"
|
||||
: "rgba(179,179,179,0.8)",
|
||||
wireBright: theme.isLight ? "rgba(30,30,30,0.9)" : "rgba(255,255,255,0.9)",
|
||||
wireFiltered: theme.isLight
|
||||
? "rgba(160,160,160,0.5)"
|
||||
: "rgba(140,140,140,0.6)",
|
||||
gridStroke: theme.isLight
|
||||
? "var(--exo-light-gray, #888888)"
|
||||
: "var(--exo-light-gray, #B3B3B3)",
|
||||
errorText: theme.isLight
|
||||
? "rgba(220,38,38,0.9)"
|
||||
: "rgba(248,113,113,0.9)",
|
||||
normalText: theme.isLight
|
||||
? "rgba(30,30,30,0.85)"
|
||||
: "rgba(255,255,255,0.85)",
|
||||
gpuChip: theme.isLight
|
||||
? "rgba(180, 180, 190, 0.7)"
|
||||
: "rgba(80, 80, 90, 0.7)",
|
||||
detailOverlay: theme.isLight ? "rgba(0,0,0,0.08)" : "rgba(0,0,0,0.35)",
|
||||
deviceShadow: theme.isLight ? "rgba(0,0,0,0.06)" : "rgba(0,0,0,0.2)",
|
||||
deviceHighlight: theme.isLight
|
||||
? "rgba(255,255,255,0.5)"
|
||||
: "rgba(255,255,255,0.08)",
|
||||
tbActive: theme.isLight
|
||||
? "rgba(30,28,20,0.9)"
|
||||
: "rgba(234,179,8,0.9)",
|
||||
tbInactive: theme.isLight
|
||||
? "rgba(160,160,160,0.7)"
|
||||
: "rgba(100,100,100,0.7)",
|
||||
deviceDetail: theme.isLight ? "#c0c0c0" : "#374151",
|
||||
});
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
@@ -127,7 +174,7 @@
|
||||
|
||||
function getTemperatureColor(temp: number): string {
|
||||
// Default for N/A temp - light gray
|
||||
if (isNaN(temp) || temp === null) return "rgba(179, 179, 179, 0.8)";
|
||||
if (isNaN(temp) || temp === null) return themeColors.wireDefault;
|
||||
|
||||
const coolTemp = 45; // Temp for pure blue
|
||||
const midTemp = 57.5; // Temp for pure yellow
|
||||
@@ -208,7 +255,7 @@
|
||||
.append("path")
|
||||
.attr("d", "M 0 0 L 10 5 L 0 10")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "var(--exo-light-gray, #B3B3B3)")
|
||||
.attr("stroke", themeColors.gridStroke)
|
||||
.attr("stroke-width", "1.6")
|
||||
.attr("stroke-linecap", "round")
|
||||
.attr("stroke-linejoin", "round")
|
||||
@@ -221,7 +268,7 @@
|
||||
.attr("y", centerY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("dominant-baseline", "middle")
|
||||
.attr("fill", "rgba(255,215,0,0.4)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.4)`)
|
||||
.attr("font-size", isMinimized ? 10 : 12)
|
||||
.attr("font-family", "SF Mono, monospace")
|
||||
.attr("letter-spacing", "0.1em")
|
||||
@@ -505,8 +552,8 @@
|
||||
.attr(
|
||||
"fill",
|
||||
conn.missingIface
|
||||
? "rgba(248,113,113,0.9)"
|
||||
: "rgba(255,255,255,0.85)",
|
||||
? themeColors.errorText
|
||||
: themeColors.normalText,
|
||||
)
|
||||
.text(label);
|
||||
currentY += isTop ? lineHeight : -lineHeight;
|
||||
@@ -563,24 +610,24 @@
|
||||
filteredNodes.size > 0 && !filteredNodes.has(nodeInfo.id);
|
||||
const isHovered = hoveredNodeId === nodeInfo.id && !isInFilter;
|
||||
|
||||
// Holographic wireframe colors - bright yellow for filter, subtle yellow for hover, grey for filtered out
|
||||
// Holographic wireframe colors - accent for filter, subtle for hover, grey for filtered out
|
||||
const wireColor = isInFilter
|
||||
? "rgba(255,215,0,1)" // Bright yellow for filter selection
|
||||
? `rgba(${themeColors.accentRgb},1)`
|
||||
: isHovered
|
||||
? "rgba(255,215,0,0.7)" // Subtle yellow for hover
|
||||
? `rgba(${themeColors.accentRgb},0.7)`
|
||||
: isHighlighted
|
||||
? "rgba(255,215,0,0.9)" // Yellow for instance highlight
|
||||
? `rgba(${themeColors.accentRgb},0.9)`
|
||||
: isFilteredOut
|
||||
? "rgba(140,140,140,0.6)" // Grey for filtered out
|
||||
: "rgba(179,179,179,0.8)"; // Default
|
||||
const wireColorBright = "rgba(255,255,255,0.9)";
|
||||
? themeColors.wireFiltered
|
||||
: themeColors.wireDefault;
|
||||
const wireColorBright = themeColors.wireBright;
|
||||
const fillColor = isInFilter
|
||||
? "rgba(255,215,0,0.25)"
|
||||
? `rgba(${themeColors.accentRgb},0.25)`
|
||||
: isHovered
|
||||
? "rgba(255,215,0,0.12)"
|
||||
? `rgba(${themeColors.accentRgb},0.12)`
|
||||
: isHighlighted
|
||||
? "rgba(255,215,0,0.15)"
|
||||
: "rgba(255,215,0,0.08)";
|
||||
? `rgba(${themeColors.accentRgb},0.15)`
|
||||
: `rgba(${themeColors.accentRgb},0.08)`;
|
||||
const strokeWidth = isInFilter
|
||||
? 3
|
||||
: isHovered
|
||||
@@ -588,8 +635,8 @@
|
||||
: isHighlighted
|
||||
? 2.5
|
||||
: 1.5;
|
||||
const screenFill = "rgba(0,20,40,0.9)";
|
||||
const glowColor = "rgba(255,215,0,0.3)";
|
||||
const screenFill = themeColors.deviceScreenFill;
|
||||
const glowColor = `rgba(${themeColors.accentRgb},0.3)`;
|
||||
|
||||
const nodeG = nodesGroup
|
||||
.append("g")
|
||||
@@ -653,7 +700,7 @@
|
||||
.attr("width", iconBaseWidth)
|
||||
.attr("height", iconBaseHeight)
|
||||
.attr("rx", cornerRadius)
|
||||
.attr("fill", "#1a1a1a")
|
||||
.attr("fill", themeColors.deviceCase)
|
||||
.attr("stroke", wireColor)
|
||||
.attr("stroke-width", strokeWidth);
|
||||
|
||||
@@ -671,12 +718,12 @@
|
||||
)
|
||||
.attr("width", iconBaseWidth)
|
||||
.attr("height", memFillActualHeight)
|
||||
.attr("fill", "rgba(255,215,0,0.75)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.75)`)
|
||||
.attr("clip-path", `url(#${studioClipId})`);
|
||||
}
|
||||
|
||||
// Front panel details - vertical slots
|
||||
const detailColor = "rgba(0,0,0,0.35)";
|
||||
const detailColor = themeColors.detailOverlay;
|
||||
const slotHeight = iconBaseHeight * 0.14;
|
||||
const vSlotWidth = iconBaseWidth * 0.05;
|
||||
const vSlotY =
|
||||
@@ -736,7 +783,7 @@
|
||||
.attr("width", iconBaseWidth)
|
||||
.attr("height", iconBaseHeight)
|
||||
.attr("rx", cornerRadius)
|
||||
.attr("fill", "#1a1a1a")
|
||||
.attr("fill", themeColors.deviceCase)
|
||||
.attr("stroke", wireColor)
|
||||
.attr("stroke-width", strokeWidth);
|
||||
|
||||
@@ -754,12 +801,12 @@
|
||||
)
|
||||
.attr("width", iconBaseWidth)
|
||||
.attr("height", memFillActualHeight)
|
||||
.attr("fill", "rgba(255,215,0,0.75)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.75)`)
|
||||
.attr("clip-path", `url(#${miniClipId})`);
|
||||
}
|
||||
|
||||
// Front panel details - vertical slots (no horizontal slot for Mini)
|
||||
const detailColor = "rgba(0,0,0,0.35)";
|
||||
const detailColor = themeColors.detailOverlay;
|
||||
const slotHeight = iconBaseHeight * 0.2;
|
||||
const vSlotWidth = iconBaseWidth * 0.045;
|
||||
const vSlotY =
|
||||
@@ -814,7 +861,7 @@
|
||||
.attr("width", screenWidth)
|
||||
.attr("height", screenHeight)
|
||||
.attr("rx", 3)
|
||||
.attr("fill", "#1a1a1a")
|
||||
.attr("fill", themeColors.deviceCase)
|
||||
.attr("stroke", wireColor)
|
||||
.attr("stroke-width", strokeWidth);
|
||||
|
||||
@@ -826,7 +873,7 @@
|
||||
.attr("width", screenWidth - screenBezel * 2)
|
||||
.attr("height", screenHeight - screenBezel * 2)
|
||||
.attr("rx", 2)
|
||||
.attr("fill", "#0a0a12");
|
||||
.attr("fill", themeColors.deviceScreen);
|
||||
|
||||
// Memory fill on screen (fills from bottom up - classic style)
|
||||
if (ramUsagePercent > 0) {
|
||||
@@ -842,7 +889,7 @@
|
||||
)
|
||||
.attr("width", screenWidth - screenBezel * 2)
|
||||
.attr("height", memFillActualHeight)
|
||||
.attr("fill", "rgba(255,215,0,0.85)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.85)`)
|
||||
.attr("clip-path", `url(#${screenClipId})`);
|
||||
}
|
||||
|
||||
@@ -859,7 +906,7 @@
|
||||
"transform",
|
||||
`translate(${logoX}, ${logoY}) scale(${logoScale})`,
|
||||
)
|
||||
.attr("fill", "#FFFFFF")
|
||||
.attr("fill", themeColors.labelWhite)
|
||||
.attr("opacity", 0.9);
|
||||
|
||||
// Base (keyboard) - trapezoidal
|
||||
@@ -875,7 +922,7 @@
|
||||
"d",
|
||||
`M ${baseTopX} ${baseY} L ${baseTopX + baseTopWidth} ${baseY} L ${baseBottomX + baseBottomWidth} ${baseY + baseHeight} L ${baseBottomX} ${baseY + baseHeight} Z`,
|
||||
)
|
||||
.attr("fill", "#2c2c2c")
|
||||
.attr("fill", themeColors.deviceCaseDark)
|
||||
.attr("stroke", wireColor)
|
||||
.attr("stroke-width", 1);
|
||||
|
||||
@@ -890,7 +937,7 @@
|
||||
.attr("y", keyboardY)
|
||||
.attr("width", keyboardWidth)
|
||||
.attr("height", keyboardHeight)
|
||||
.attr("fill", "rgba(0,0,0,0.2)")
|
||||
.attr("fill", themeColors.deviceShadow)
|
||||
.attr("rx", 2);
|
||||
|
||||
// Trackpad
|
||||
@@ -904,7 +951,7 @@
|
||||
.attr("y", trackpadY)
|
||||
.attr("width", trackpadWidth)
|
||||
.attr("height", trackpadHeight)
|
||||
.attr("fill", "rgba(255,255,255,0.08)")
|
||||
.attr("fill", themeColors.deviceHighlight)
|
||||
.attr("rx", 2);
|
||||
} else {
|
||||
// Default/Unknown - holographic hexagon
|
||||
@@ -942,7 +989,7 @@
|
||||
.attr("y", gpuBarY)
|
||||
.attr("width", gpuBarWidth)
|
||||
.attr("height", gpuBarHeight)
|
||||
.attr("fill", "rgba(80, 80, 90, 0.7)")
|
||||
.attr("fill", themeColors.gpuChip)
|
||||
.attr("rx", 2);
|
||||
|
||||
// GPU Bar Fill (from bottom up, colored by temperature)
|
||||
@@ -979,7 +1026,7 @@
|
||||
.attr("y", gpuTextY - lineSpacing)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("dominant-baseline", "middle")
|
||||
.attr("fill", "#FFFFFF")
|
||||
.attr("fill", themeColors.labelWhite)
|
||||
.attr("font-size", gpuTextFontSize)
|
||||
.attr("font-weight", "700")
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
@@ -992,7 +1039,7 @@
|
||||
.attr("y", gpuTextY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("dominant-baseline", "middle")
|
||||
.attr("fill", "#FFFFFF")
|
||||
.attr("fill", themeColors.labelWhite)
|
||||
.attr("font-size", gpuTextFontSize)
|
||||
.attr("font-weight", "700")
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
@@ -1005,7 +1052,7 @@
|
||||
.attr("y", gpuTextY + lineSpacing)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("dominant-baseline", "middle")
|
||||
.attr("fill", "#FFFFFF")
|
||||
.attr("fill", themeColors.labelWhite)
|
||||
.attr("font-size", gpuTextFontSize)
|
||||
.attr("font-weight", "700")
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
@@ -1033,7 +1080,7 @@
|
||||
.attr("y", nameY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("dominant-baseline", "middle")
|
||||
.attr("fill", "#FFD700")
|
||||
.attr("fill", themeColors.accent)
|
||||
.attr("font-size", fontSize)
|
||||
.attr("font-weight", 500)
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
@@ -1050,15 +1097,15 @@
|
||||
.attr("font-family", "SF Mono, Monaco, monospace");
|
||||
memText
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(255,215,0,0.9)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.9)`)
|
||||
.text(`${formatBytes(ramUsed)}`);
|
||||
memText
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(179,179,179,0.9)")
|
||||
.attr("fill", themeColors.labelMuted)
|
||||
.text(`/${formatBytes(ramTotal)}`);
|
||||
memText
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(179,179,179,0.7)")
|
||||
.attr("fill", themeColors.labelDim)
|
||||
.text(` (${ramUsagePercent.toFixed(0)}%)`);
|
||||
} else if (showCompactLabels) {
|
||||
// COMPACT MODE: Just name and basic info (4+ nodes)
|
||||
@@ -1075,7 +1122,7 @@
|
||||
.attr("x", nodeInfo.x)
|
||||
.attr("y", nameY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "#FFD700")
|
||||
.attr("fill", themeColors.accent)
|
||||
.attr("font-size", fontSize)
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
.text(shortName);
|
||||
@@ -1087,7 +1134,7 @@
|
||||
.attr("x", nodeInfo.x)
|
||||
.attr("y", statsY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "rgba(255,215,0,0.7)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.7)`)
|
||||
.attr("font-size", fontSize * 0.85)
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
.text(
|
||||
@@ -1108,7 +1155,7 @@
|
||||
.attr("x", nodeInfo.x)
|
||||
.attr("y", nameY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "#FFD700")
|
||||
.attr("fill", themeColors.accent)
|
||||
.attr("font-size", fontSize)
|
||||
.attr("font-weight", "500")
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
@@ -1125,15 +1172,15 @@
|
||||
.attr("font-family", "SF Mono, Monaco, monospace");
|
||||
memTextMini
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(255,215,0,0.9)")
|
||||
.attr("fill", `rgba(${themeColors.accentRgb},0.9)`)
|
||||
.text(`${formatBytes(ramUsed)}`);
|
||||
memTextMini
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(179,179,179,0.9)")
|
||||
.attr("fill", themeColors.labelMuted)
|
||||
.text(`/${formatBytes(ramTotal)}`);
|
||||
memTextMini
|
||||
.append("tspan")
|
||||
.attr("fill", "rgba(179,179,179,0.7)")
|
||||
.attr("fill", themeColors.labelDim)
|
||||
.text(` (${ramUsagePercent.toFixed(0)}%)`);
|
||||
}
|
||||
|
||||
@@ -1149,8 +1196,8 @@
|
||||
const tbStatus = tbBridgeData[nodeInfo.id];
|
||||
if (tbStatus) {
|
||||
const tbColor = tbStatus.enabled
|
||||
? "rgba(234,179,8,0.9)"
|
||||
: "rgba(100,100,100,0.7)";
|
||||
? themeColors.tbActive
|
||||
: themeColors.tbInactive;
|
||||
const tbText = tbStatus.enabled ? "TB:ON" : "TB:OFF";
|
||||
nodeG
|
||||
.append("text")
|
||||
@@ -1168,7 +1215,7 @@
|
||||
if (rdmaStatus !== undefined) {
|
||||
const rdmaColor = rdmaStatus.enabled
|
||||
? "rgba(74,222,128,0.9)"
|
||||
: "rgba(100,100,100,0.7)";
|
||||
: themeColors.tbInactive;
|
||||
const rdmaText = rdmaStatus.enabled ? "RDMA:ON" : "RDMA:OFF";
|
||||
nodeG
|
||||
.append("text")
|
||||
@@ -1189,7 +1236,7 @@
|
||||
.attr("x", nodeInfo.x)
|
||||
.attr("y", debugLabelY)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "rgba(179,179,179,0.7)")
|
||||
.attr("fill", themeColors.labelDim)
|
||||
.attr("font-size", debugFontSize)
|
||||
.attr("font-family", "SF Mono, Monaco, monospace")
|
||||
.text(
|
||||
@@ -1206,6 +1253,7 @@
|
||||
const _hoveredNodeId = hoveredNodeId;
|
||||
const _filteredNodes = filteredNodes;
|
||||
const _highlightedNodes = highlightedNodes;
|
||||
const _themeColors = themeColors;
|
||||
if (_data) {
|
||||
renderGraph();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user