From 154e6940c4af6d189d1afba386ce00151fbdf16c Mon Sep 17 00:00:00 2001 From: Nubenetes Bot Date: Sat, 20 Jun 2026 22:20:02 +0200 Subject: [PATCH] feat(v2): add Resource Density Heatmap to Topic Map page Render a true colour-matrix heatmap on the Topic Map page (distinct from the size-based Label cloud on index/Tags). One tile per category, grouped by strategic dimension, with tile warmth (.th-1..6) encoding how many AI-curated resources it holds. Log scale since counts span ~1..160. The existing directory grid is preserved below a new "Full Category Directory" heading. Generator-only change; the publisher regenerates topic-map.md. CSS adds .topic-heatmap / .topic-heatcell with light + slate palettes. Co-Authored-By: Claude Opus 4.8 --- docs/static/v2_elite.css | 98 ++++++++++++++++++++++++++++++++++++++++ src/v2_optimizer.py | 55 ++++++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/docs/static/v2_elite.css b/docs/static/v2_elite.css index f5b78c62..bc32730b 100644 --- a/docs/static/v2_elite.css +++ b/docs/static/v2_elite.css @@ -1576,6 +1576,104 @@ input[type="text"] { .topic-map-grid { column-count: 1; } } +/* =================================================================== + Topic Map: Resource Density Heatmap + A true colour matrix (distinct from the size-based Label cloud). Each + category is a tile whose warmth (.th-1..6) encodes resource density, + grouped by strategic dimension. Generated by v2_optimizer.py. + =================================================================== */ +.topic-heatmap { + margin: 1rem 0 2rem; + padding: 1.1rem 1.2rem 1.3rem; + border: 1px solid rgba(38, 50, 56, 0.12); + border-radius: 10px; + background: rgba(38, 50, 56, 0.04); +} +[data-md-color-scheme="slate"] .topic-heatmap { + background: rgba(255, 255, 255, 0.03); + border-color: rgba(255, 255, 255, 0.1); +} +.topic-heatmap__legend { + display: flex; + align-items: center; + gap: 0.3rem; + margin-bottom: 1rem; + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.03em; + color: var(--md-default-fg-color--light); +} +.topic-heatmap__legend .topic-heatcell { + width: 26px; + height: 14px; + padding: 0; + border-radius: 3px; +} +.topic-heatmap__cap { text-transform: uppercase; opacity: 0.7; } +.topic-heatmap__row { + display: grid; + grid-template-columns: 190px 1fr; + gap: 0.5rem 1rem; + align-items: start; + padding: 0.6rem 0; + border-top: 1px solid rgba(38, 50, 56, 0.08); +} +[data-md-color-scheme="slate"] .topic-heatmap__row { + border-top-color: rgba(255, 255, 255, 0.07); +} +.topic-heatmap__row:first-of-type { border-top: none; padding-top: 0; } +.topic-heatmap__dim { + font-size: 0.78rem; + font-weight: 700; + line-height: 1.25; + padding-top: 0.25rem; + color: var(--md-default-fg-color); +} +.topic-heatmap__cells { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; +} +.topic-heatcell { + display: inline-flex; + align-items: center; + gap: 0.3em; + padding: 0.28rem 0.5rem; + border-radius: 5px; + font-size: 0.7rem; + font-weight: 600; + line-height: 1.2; + text-decoration: none; + color: #1a2327; + transition: transform 0.12s ease, box-shadow 0.12s ease; +} +.topic-heatcell:hover { + transform: translateY(-2px); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.18); +} +.topic-heatcell__n { + font-size: 0.62em; + font-weight: 700; + opacity: 0.7; +} +/* Sequential warmth ramp: th-1 = sparsest, th-6 = densest. */ +.th-1 { background: #e3f2fd; } +.th-2 { background: #ffe0b2; } +.th-3 { background: #ffcc80; } +.th-4 { background: #ffa726; } +.th-5 { background: #fb8c00; color: #fff; } +.th-6 { background: #e64a19; color: #fff; } +[data-md-color-scheme="slate"] .th-1 { background: #2d4456; color: #d7e3ea; } +[data-md-color-scheme="slate"] .th-2 { background: #6d4c1f; color: #f5e7d3; } +[data-md-color-scheme="slate"] .th-3 { background: #97601f; color: #fff3e0; } +[data-md-color-scheme="slate"] .th-4 { background: #c2701a; color: #fff; } +[data-md-color-scheme="slate"] .th-5 { background: #e2701a; color: #fff; } +[data-md-color-scheme="slate"] .th-6 { background: #ff5722; color: #fff; } +@media screen and (max-width: 44.9375em) { + .topic-heatmap__row { grid-template-columns: 1fr; gap: 0.35rem; } + .topic-heatmap__dim { padding-top: 0; } +} + /* Visible label for each YouTube mosaic category block (was hidden in title=) */ .mosaic-cat-label { margin: 0 0 0.5rem; diff --git a/src/v2_optimizer.py b/src/v2_optimizer.py index 960d8a80..05184699 100644 --- a/src/v2_optimizer.py +++ b/src/v2_optimizer.py @@ -1257,6 +1257,59 @@ class V2VisionEngine: total += _count_links(v) return total + # Resource-density heatmap: a true colour matrix (distinct from the + # size-based Label cloud on the index/Tags pages). One tile per category, + # grouped by the same strategic dimensions as the directory below, with + # tile warmth encoding how many AI-curated resources it holds. Counts span + # ~1..160, so a LOG scale is used to avoid collapsing everything into the + # lowest bucket. Six levels map onto .th-1..6. + cat_counts = {f_name: _count_links(info["content"]) for f_name, info in data.items()} + _all_counts = [c for c in cat_counts.values() if c > 0] or [1] + _hmin, _hmax = math.log(min(_all_counts)), math.log(max(_all_counts)) + + def _topic_heat(count): + if count <= 0: + return 1 + if _hmax == _hmin: + return 3 + return 1 + round((math.log(count) - _hmin) / (_hmax - _hmin) * 5) # 1..6 + + heatmap_md = ( + "## Resource Density Heatmap\n\n" + "Each tile is a category; the warmer the colour, the more AI-curated " + "resources it holds. A fast read of where the Cloud Native ecosystem's " + "depth concentrates — click any tile to open its page.\n\n" + "
\n" + "
" + "Fewer" + "" + "" + "" + "" + "" + "" + "More" + "
\n" + ) + for dim in self.dimensions.keys(): + if dim not in dim_groups: + continue + heatmap_md += "
\n" + heatmap_md += f"
{dim}
\n" + heatmap_md += "
\n" + for f in sorted(dim_groups[dim], key=lambda x: cat_counts[x], reverse=True): + count = cat_counts[f] + lvl = _topic_heat(count) + slug = f[:-3] if f.endswith(".md") else f + heatmap_md += ( + f"" + f"{data[f]['title']}" + f"{count}\n" + ) + heatmap_md += "
\n
\n" + heatmap_md += "
\n\n" + topic_md = ( "---\n" "description: \"The complete Nubenetes V2 directory: every curated Kubernetes, " @@ -1267,6 +1320,8 @@ class V2VisionEngine: " Every curated category across all strategic dimensions, with the number of " "AI-selected resources in each. Looking for the landing page? Back to the " "[**2026 Vision**](/).\n\n" + + heatmap_md + + "## Full Category Directory\n\n" "
\n\n" ) for dim in self.dimensions.keys():