From 3584758b86106249404df82462e199c2770f0570 Mon Sep 17 00:00:00 2001 From: Alexander Heldt Date: Tue, 22 Sep 2026 10:42:30 +0000 Subject: [PATCH] Keep a deleted kind's colour, not just its name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A kind that is deleted keeps its tombstone so meals logged as it stay readable, and the chart recovered its name from there — but not its colour, falling back to grey. Grey is what "No kind" uses, so a deleted kind's food and unlabelled food drew as the same colour: two distinct series, indistinguishable in the stack and in the legend beneath it. The tombstone has the colorIndex all along, so reading the whole record rather than just the name fixes it. The check now pins the colour as well as the name, since the name alone was what let this through. Deleting a kind still leaves the meals alone — confirmed as the wanted behaviour. This only makes that behaviour legible. --- checks/food-trend.mjs | 9 +++++++-- src/app.js | 14 +++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/checks/food-trend.mjs b/checks/food-trend.mjs index baa23a1..1b6046f 100644 --- a/checks/food-trend.mjs +++ b/checks/food-trend.mjs @@ -147,6 +147,7 @@ suite("what the sentence is allowed to say"); "foodTrendMoves", "foodSeriesSentences", "foodSeriesFor"], stubs: { liveFoodKinds: () => kinds, + loadFoodKinds: () => kinds, foodKindNames: () => new Map(kinds.map(k => [k.id, k.name])), }, }); @@ -203,12 +204,16 @@ suite("what the sentence is allowed to say"); names: ["NO_KIND", "FOOD_COLORS", "foodTrend", "foodSeriesFor"], stubs: { liveFoodKinds: () => [], - foodKindNames: () => new Map([["gone", "Old recipe"]]), + // The tombstone: gone from the picker, still carrying name and colour. + loadFoodKinds: () => [{ id: "gone", name: "Old recipe", colorIndex: 2, deleted: true }], }, }); const days = byKind([{ gone: 100 }, { gone: 110 }, { gone: 120 }, { gone: 130 }, { gone: 0 }]); - eq(namesOnly.foodSeriesFor(days).map(s => s.name), ["Old recipe"], + const series = namesOnly.foodSeriesFor(days); + eq(series.map(s => s.name), ["Old recipe"], "it keeps its name rather than vanishing or reading as 'No kind'"); + eq(series[0].colorIndex, 2, + "…and its colour, which grey would confuse with the 'No kind' series"); } suite("the caption stays bounded as kinds are added"); diff --git a/src/app.js b/src/app.js index 35aacd6..b5b8219 100644 --- a/src/app.js +++ b/src/app.js @@ -2068,7 +2068,9 @@ // With no kinds defined this returns a single unnamed series, which is what // makes the whole feature invisible to anyone not using it. function foodSeriesFor(days) { - const names = foodKindNames(); + // Every kind ever, tombstones included — a deleted one keeps both its name + // and its colour, which is what lets its food stay identifiable below. + const all = new Map(loadFoodKinds().map(k => [k.id, k])); const used = (id) => days.some(d => (d.gramsByKind?.[id] || 0) > 0); const out = []; for (const k of liveFoodKinds()) { @@ -2079,13 +2081,19 @@ }); } // Kinds deleted since, but still on meals in this window: their food is - // real and has to appear somewhere, under the name the tombstone kept. + // real and has to appear somewhere, under the name and colour the tombstone + // kept. Falling back to grey here would be wrong twice over — it is also + // "No kind"'s colour, so the two series would be indistinguishable in both + // the stack and the legend. for (const d of days) { for (const id of Object.keys(d.gramsByKind || {})) { if (id === NO_KIND || out.some(s => s.id === id)) continue; if (!used(id)) continue; + const gone = all.get(id); out.push({ - id, name: names.get(id) || "Deleted kind", colorIndex: null, + id, + name: gone?.name || "Deleted kind", + colorIndex: gone ? (gone.colorIndex ?? 0) : null, of: (dd) => dd.gramsByKind?.[id] || 0, }); }