diff --git a/k8s-scheduler-game/src/ui.js b/k8s-scheduler-game/src/ui.js
index 7098bcf..182dd61 100644
--- a/k8s-scheduler-game/src/ui.js
+++ b/k8s-scheduler-game/src/ui.js
@@ -46,12 +46,14 @@ export class UI {
nodeSel.innerHTML = Object.values(INSTANCE_TYPES)
.map((t) => {
const gpu = t.gpu ? `, ${t.gpu} GPU` : "";
+ const price =
+ t.family === "spot" ? `~$${(t.cost * 0.45).toFixed(2)}/hr spot` : `$${t.cost.toFixed(2)}/hr`;
return ``;
+ )}${gpu} · ${price}`;
})
.join("");
- nodeSel.value = "general-large";
+ nodeSel.value = "c5.2xlarge";
}
wire() {
@@ -285,7 +287,7 @@ export class UI {
{ l: "Running", v: g.runningCount(), cls: "" },
{ l: "Nodes", v: `${ready}/${s.nodes.length}`, cls: "" },
{ l: "Cost", v: `$${g.hourlyCost().toFixed(2)}/hr`, cls: "" },
- { l: "Spot price", v: `${spot.toFixed(2)}×`, cls: spot >= 1.5 ? "bad" : spot <= 0.8 ? "good" : "" },
+ { l: "spot vs on-dmd", v: `${spot.toFixed(2)}×`, cls: spot <= 0.5 ? "good" : spot >= 0.75 ? "bad" : "" },
{
l: s.upgradePending ? `upgrade: ${outdated} left` : "k8s version",
v: `v1.${s.clusterMinor}`,
@@ -346,7 +348,9 @@ export class UI {
.map((t) => `⛔ ${t.key}=${t.value}:${t.effect}`)
.join("");
const spotTag = node.spot
- ? `⚡ spot $${(node.cost * s.spotPrice).toFixed(2)}/hr`
+ ? `⚡ spot $${(node.cost * s.spotPrice).toFixed(2)}/hr · −${Math.round(
+ (1 - s.spotPrice) * 100
+ )}%`
: "";
const cpuBar = this.meter("CPU", used.cpu, node.cpu, meterPods, "cpu");
diff --git a/k8s-scheduler-game/styles.css b/k8s-scheduler-game/styles.css
index 0068665..1f28f92 100644
--- a/k8s-scheduler-game/styles.css
+++ b/k8s-scheduler-game/styles.css
@@ -271,7 +271,7 @@ button:focus-visible {
grid-template-columns: 1fr 340px;
gap: 16px;
padding: 16px 22px;
- align-items: start;
+ align-items: stretch;
min-height: 0;
}
.panel {
@@ -292,11 +292,22 @@ button:focus-visible {
letter-spacing: 0.3px;
}
+/* The node and queue panels are flex columns whose lists scroll internally,
+ so a long cluster never overflows its row and covers the event log. */
+.nodes,
+.queue {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
.nodegrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 14px;
padding: 16px;
+ flex: 1 1 auto;
+ min-height: 0;
+ overflow-y: auto;
}
/* --------------------------------------------------------------- node card */
@@ -543,10 +554,6 @@ button:focus-visible {
}
/* --------------------------------------------------------------- queue */
-.queue {
- position: sticky;
- top: 16px;
-}
.hint {
margin: 0;
padding: 8px 16px;
@@ -559,7 +566,8 @@ button:focus-visible {
display: flex;
flex-direction: column;
gap: 8px;
- max-height: calc(100vh - 360px);
+ flex: 1 1 auto;
+ min-height: 0;
overflow-y: auto;
}
.empty-state {