feat: hot topics daily rotation — 30+ topic pool, seeded shuffle, next-refresh countdown

- Expanded research pool to 9 topics (was 3), evergreen to 12 (was 3)
- Conference topics: added Photonics West, CIOE, NFOEC follow-up, year-end review
- Standards topics: 3 rotating variants (IEEE tracker, SFF-8024 registry, OIF CEI-112G)
- seededShuffle(): day-of-year as seed — stable within the day, different every day
- API response adds refreshes_at (next midnight UTC) for frontend countdown
- Dashboard subtitle shows 'rotates daily · next refresh in Xh'
- Hot topic cards now pass full title + angle into generateBlog() correctly
This commit is contained in:
Rene Fichtmueller 2026-04-01 11:12:38 +02:00
parent 8b187a5dac
commit 085d83c5fb
3 changed files with 278 additions and 22 deletions

View File

@ -121,7 +121,8 @@ hotTopicsRouter.get("/", async (_req, res) => {
`).catch(() => ({ rows: [] }));
// Cluster news by theme
const newsThemes: Record<string, typeof recentNews.rows> = {};
type NewsRow = (typeof recentNews.rows)[number];
const newsThemes: Record<string, NewsRow[]> = {};
for (const n of recentNews.rows) {
const theme = detectNewsTheme(n.title);
if (!newsThemes[theme]) newsThemes[theme] = [];
@ -157,10 +158,17 @@ hotTopicsRouter.get("/", async (_req, res) => {
const urgencyOrder: Record<string, number> = { breaking: 0, hot: 1, trending: 2, emerging: 3 };
topics.sort((a, b) => (urgencyOrder[a.urgency] ?? 4) - (urgencyOrder[b.urgency] ?? 4));
// Next daily rotation: tomorrow 00:00 UTC
const tomorrow = new Date();
tomorrow.setUTCDate(tomorrow.getUTCDate() + 1);
tomorrow.setUTCHours(0, 0, 0, 0);
res.json({
topics: topics.slice(0, 12),
topics: topics.slice(0, 6),
total: topics.length,
generated_at: new Date().toISOString(),
refreshes_at: tomorrow.toISOString(),
day_seed: getDaySeed(),
sources: ["internal_price_data", "competitor_alerts", "hype_cycle_model", "news_articles", "conference_calendar", "research_papers"],
});
} catch (err) {
@ -189,6 +197,17 @@ function getConferenceTopics(year: number): HotTopic[] {
const topics: HotTopic[] = [];
// OFC = March, ECOC = September, CIOE = September, Photonics West = January
if (month >= 1 && month <= 2) {
topics.push({
title: `Photonics West ${year}: What the Laser and Photonics Research Means for Transceivers`,
description: "SPIE Photonics West is where the component-level research lands first. What was shown and how long until it ships?",
blog_type: "technology_deep_dive",
urgency: month === 1 ? "hot" : "trending",
source: "SPIE Photonics West",
source_type: "conference",
suggested_angle: "Photonics West signal reading: The research demos that become products in 2-3 years",
});
}
if (month >= 2 && month <= 4) {
topics.push({
title: `OFC ${year} Highlights: What was announced and what matters`,
@ -200,6 +219,17 @@ function getConferenceTopics(year: number): HotTopic[] {
suggested_angle: "OFC show floor reality: 5 announcements that actually change your procurement strategy",
});
}
if (month >= 5 && month <= 7) {
topics.push({
title: `NFOEC / OFC ${year} Follow-Up: Which Products Actually Shipped?`,
description: "Six months after OFC demos — checking which announcements turned into shipping hardware and which were still vapor.",
blog_type: "market_alert",
urgency: "trending",
source: "OFC Conference Follow-Up",
source_type: "conference",
suggested_angle: "Post-OFC reality check: Announcement vs reality 6 months later",
});
}
if (month >= 8 && month <= 10) {
topics.push({
title: `ECOC ${year}: European market signals and new products`,
@ -211,23 +241,89 @@ function getConferenceTopics(year: number): HotTopic[] {
suggested_angle: "ECOC takeaways: What European carriers are actually deploying (not just demoing)",
});
}
if (month >= 9 && month <= 11) {
topics.push({
title: `CIOE ${year} Shenzhen: Chinese Manufacturers Show Their Cards`,
description: "CIOE is where InnoLight, HG Genuine, and Chinese OEMs announce next-gen products. First look at the 2025 roadmap.",
blog_type: "technology_deep_dive",
urgency: "trending",
source: "CIOE Shenzhen",
source_type: "conference",
suggested_angle: "CIOE market signal: What Chinese manufacturers shipping in 12 months means for Western pricing",
});
}
if (month >= 11 || month <= 1) {
topics.push({
title: `${year} Year-End Optics Market Review: Who Won, Who Lost`,
description: "Annual review: which speeds gained share, which vendors shipped on roadmap, and what the price curves looked like.",
blog_type: "market_alert",
urgency: "hot",
source: "TIP Market Data",
source_type: "internal_data",
suggested_angle: "Year-end optics market: The numbers that matter for your ${year + 1} budget",
});
}
// Always relevant
topics.push({
title: `${year} Standards Update: IEEE 802.3 / OIF / MSA tracker`,
description: "What's ratified, what's in draft, and what it means for your next-gen optics roadmap.",
blog_type: "technology_deep_dive",
urgency: "trending",
source: "IEEE / OIF / MSA",
source_type: "research",
suggested_angle: "Standards reality check: Which specs are production-ready vs. still in committee",
});
// Always relevant — rotate between 3 variants using day seed
const standardsTopics: HotTopic[] = [
{
title: `${year} Standards Update: IEEE 802.3 / OIF / MSA tracker`,
description: "What's ratified, what's in draft, and what it means for your next-gen optics roadmap.",
blog_type: "technology_deep_dive",
urgency: "trending",
source: "IEEE / OIF / MSA",
source_type: "research",
suggested_angle: "Standards reality check: Which specs are production-ready vs. still in committee",
},
{
title: `SFF-8024 Transceiver Management Update ${year}: What's New in the Identifier Registry`,
description: "The SFF Committee's transceiver identifier registry updated again. New form factors, management interfaces, and what switches need firmware updates to support them.",
blog_type: "technology_deep_dive",
urgency: "trending",
source: "SFF Committee",
source_type: "research",
suggested_angle: "SFF registry changes: The low-level updates that cause high-level compatibility headaches",
},
{
title: `OIF CEI-112G Standard: What It Means for the Next Generation of DAC and AOC`,
description: "CEI-112G is the electrical interface spec that enables 112G PAM4 — the building block of 400G and 800G. What it enables and when it matters.",
blog_type: "technology_deep_dive",
urgency: "emerging",
source: "OIF CEI",
source_type: "research",
suggested_angle: "CEI-112G explained: Why the electrical interface standard matters more than the optics spec",
},
];
const seed = getDaySeed() + 3;
topics.push(seededShuffle(standardsTopics, seed)[0]);
return topics;
}
/**
* Seeded shuffle same seed = same order. Uses day-of-year as seed so
* topics rotate daily but are stable within the same day.
*/
function seededShuffle<T>(arr: T[], seed: number): T[] {
const out = [...arr];
let s = seed;
for (let i = out.length - 1; i > 0; i--) {
s = (s * 1664525 + 1013904223) & 0x7fffffff;
const j = s % (i + 1);
[out[i], out[j]] = [out[j], out[i]];
}
return out;
}
function getDaySeed(): number {
const now = new Date();
const start = new Date(now.getFullYear(), 0, 0);
const dayOfYear = Math.floor((now.getTime() - start.getTime()) / 86400000);
return now.getFullYear() * 1000 + dayOfYear;
}
function getResearchTopics(year: number): HotTopic[] {
return [
const ALL_RESEARCH: HotTopic[] = [
{
title: "Silicon Photonics: From Lab to Production — Where Are We Really?",
description: "Intel, Broadcom, Marvell, and startups (Lightmatter, Ayar Labs) are all pushing SiPho. But production yields and packaging costs tell a different story.",
@ -255,11 +351,68 @@ function getResearchTopics(year: number): HotTopic[] {
source_type: "manufacturer",
suggested_angle: "CPO: Why it's 3 years away (and has been for the last 5 years)",
},
{
title: "1.6T Optics: Who Ships First and What the Specs Actually Mean",
description: "Vendors are announcing 1.6T but the standards aren't final. Here's what's real, what's vaporware, and what lead times to expect.",
blog_type: "technology_deep_dive",
urgency: "emerging",
source: "OIF / IEEE",
source_type: "research",
suggested_angle: "1.6T reality check: Sorting PR from actual shipping hardware",
},
{
title: "OSFP vs QSFP-DD: The 400G/800G Form Factor Fight Isn't Over",
description: "Both coexist in the market. Hyperscalers went OSFP for 800G; enterprise is on QSFP-DD. What does this mean for your optics inventory?",
blog_type: "comparison",
urgency: "trending",
source: "QSFP-DD MSA / OSFP MSA",
source_type: "research",
suggested_angle: "Form factor fragmentation: How to not end up with the wrong 800G optics",
},
{
title: "Coherent ZR vs ZR+: The Spec Sheet Lies You Need to Know",
description: "OpenZR+ vs 400ZR implementations from different vendors are NOT interoperable by default. The details matter.",
blog_type: "comparison",
urgency: "trending",
source: "OIF Implementation Agreement",
source_type: "research",
suggested_angle: "Coherent interop truth: Why your ZR link won't work with mixed vendors out of the box",
},
{
title: "BiDi Optics: The Most Misunderstood Product in the Catalog",
description: "Single-strand operation sounds simple. The wavelength pairing rules, fiber polarity requirements, and what goes wrong when teams mix Tx/Rx wavelengths.",
blog_type: "tutorial",
urgency: "trending",
source: "Field Cases",
source_type: "internal_data",
suggested_angle: "BiDi done right: Why half your BiDi deployments have the wrong wavelength on the wrong end",
},
{
title: "Transceiver DDM/DOM: How to Actually Use the Diagnostic Data",
description: "Every modern optic reports temperature, voltage, Tx power, Rx power, and bias current. Most teams never check it. Here's what the numbers tell you.",
blog_type: "tutorial",
urgency: "trending",
source: "SFF Committee / Field Data",
source_type: "research",
suggested_angle: "DDM as a maintenance tool: Catching failing optics before they take down your link",
},
{
title: "800G Deployment Wave: What Hyperscalers Are Actually Installing in ${year}",
description: "Meta, Google, and Microsoft are ordering 800G at scale. What form factors, vendors, and reach variants are winning?",
blog_type: "market_alert",
urgency: "hot",
source: "LightCounting / Hyperscaler CapEx Reports",
source_type: "research",
suggested_angle: "800G market signal: What hyperscaler spending tells enterprise about their 2027 refresh cycle",
},
];
// Pick 2 different research topics per day
const shuffled = seededShuffle(ALL_RESEARCH, getDaySeed() + 1);
return shuffled.slice(0, 2);
}
function getEvergreenTopics(year: number): HotTopic[] {
return [
const ALL_EVERGREEN: HotTopic[] = [
{
title: `The ${year} Transceiver Buying Guide: OEM vs Compatible`,
description: "Updated pricing data, vendor quality tiers, and the procurement playbook for every speed class.",
@ -287,5 +440,89 @@ function getEvergreenTopics(year: number): HotTopic[] {
source_type: "internal_data",
suggested_angle: "Your $350 optic isn't broken — your connector is dirty. The MPO reality guide.",
},
{
title: "Transceiver Compatibility: Why Your New Optic Doesn't Work and How to Fix It",
description: "DOM lockout, software coding, MSA compliance, and the vendor unlock codes that nobody documents.",
blog_type: "tutorial",
urgency: "trending",
source: "Support Cases",
source_type: "internal_data",
suggested_angle: "Compatibility reality: The 4 reasons optics get rejected and exactly how to resolve each one",
},
{
title: `${year} Price Tracker: Which Transceiver Speeds Are Getting Cheaper Fastest?`,
description: "25G SFP28 hit floor pricing. 100G QSFP28 is still dropping. 400G is at inflection. Where to buy now, where to wait.",
blog_type: "market_alert",
urgency: "hot",
source: "TIP Price Observations",
source_type: "internal_data",
suggested_angle: "Buy signal analysis: The optic speeds where waiting 6 months saves 30%",
},
{
title: "Power Budget 101: Why Your 10km LR Link Only Reaches 7km",
description: "TX power, receiver sensitivity, fiber loss, connector loss, and the splice margin most engineers forget to account for.",
blog_type: "tutorial",
urgency: "trending",
source: "Field Experience",
source_type: "internal_data",
suggested_angle: "Loss budget done right: The calculation every engineer should do before ordering LR optics",
},
{
title: "Cisco vs Arista vs Juniper Optics Lock-In: The True Cost Analysis",
description: "OEM optics from major vendors carry a 300-800% premium. Here's the legal situation, the unlock process, and when it's actually worth paying OEM prices.",
blog_type: "buying_guide",
urgency: "trending",
source: "Vendor Pricing / Legal Docs",
source_type: "internal_data",
suggested_angle: "OEM lock-in economics: The numbers behind why compatible optics work and when they don't",
},
{
title: "40G End-of-Life: What to Do With Your QSFP+ Inventory in ${year}",
description: "40G is in decline. Resale values, upgrade paths to 100G, and when to write off the inventory.",
blog_type: "market_alert",
urgency: "trending",
source: "TIP Price Data",
source_type: "internal_data",
suggested_angle: "40G sunset planning: The migration math that justifies skipping 40G refresh entirely",
},
{
title: "DAC vs AOC vs Optic+Cable: The Short-Reach Decision Tree",
description: "For ≤5m: DAC. For 5-100m: AOC. For >100m: pluggable. But the breakeven points shift with port density and heat budgets.",
blog_type: "comparison",
urgency: "trending",
source: "Field Data",
source_type: "internal_data",
suggested_angle: "Short-reach optics: The decision framework that saves budget without sacrificing reliability",
},
{
title: "Gray Optics: Market Pricing vs New — When the Risk Is Worth It",
description: "eBay, Alibaba, and gray market brokers sell transceiver inventory at 40-70% below retail. What to check, what can go wrong, and what the warranty situation actually is.",
blog_type: "buying_guide",
urgency: "trending",
source: "Market Research",
source_type: "internal_data",
suggested_angle: "Gray market guide: The due diligence checklist that separates a deal from a disaster",
},
{
title: "Wavelength Division Multiplexing in ${year}: CWDM4 vs LWDM vs DWDM Decision Guide",
description: "WDM optics for data center interconnect. The wavelength plans, the reach limits, and when DWDM pays off vs CWDM.",
blog_type: "technology_deep_dive",
urgency: "trending",
source: "IEEE / OIF",
source_type: "research",
suggested_angle: "WDM selection guide: Matching wavelength technology to your actual DCI requirements",
},
{
title: "Temperature-Hardened Optics: When ETSI Class 4.1 Actually Matters",
description: "Industrial, outdoor, and telecom deployments require optics rated for -40°C to +85°C. What the specs mean and which products are actually qualified.",
blog_type: "buying_guide",
urgency: "emerging",
source: "ETSI / Telecom Field Cases",
source_type: "internal_data",
suggested_angle: "Industrial optics selection: When your data center optic will kill your outdoor deployment",
},
];
// Pick 3 different evergreen topics per day
const shuffled = seededShuffle(ALL_EVERGREEN, getDaySeed() + 2);
return shuffled.slice(0, 3);
}

View File

@ -123,6 +123,7 @@
// Hot topics loader
window.loadHotTopics = function() {
var grid = document.getElementById('hot-topics-grid');
var subtitle = document.getElementById('hot-topics-subtitle');
if (!grid) return;
grid.innerHTML = '<div class="loading pulse">Discovering hot topics...</div>';
@ -131,17 +132,28 @@
grid.innerHTML = '<div class="gen-card" style="cursor:pointer" onclick="generateBlog(\'hype_cycle\',\'800G\')"><div class="gen-card-title">Hype Cycle Analysis</div></div>';
return;
}
// Update subtitle with next-refresh countdown
if (subtitle && data.refreshes_at) {
var nextRefresh = new Date(data.refreshes_at);
var hoursLeft = Math.round((nextRefresh - new Date()) / 3600000);
subtitle.textContent = 'auto-discovered · rotates daily · next refresh in ' + hoursLeft + 'h';
}
var colors = { breaking: '#c1121f', hot: '#FF8100', trending: '#e6a800', emerging: '#2d6a4f' };
grid.innerHTML = data.topics.slice(0, 6).map(function(t) {
grid.innerHTML = data.topics.map(function(t) {
var c = colors[t.urgency] || '#888';
var escaped = encodeURIComponent(t.blog_type || 'hype_cycle');
var title = (t.title || '').replace(/'/g, "\\'").replace(/"/g, '&quot;');
return '<div class="gen-card" style="cursor:pointer;border-left:3px solid ' + c + '" onclick="generateBlog(\'' + escaped + '\')">' +
// Pass full topic title and angle as data attributes to avoid quote-escaping hell
var cardId = 'ht-' + Math.random().toString(36).slice(2, 8);
// Store topic data for onclick
window['_ht_' + cardId] = t;
return '<div class="gen-card" style="cursor:pointer;border-left:3px solid ' + c + '" ' +
'onclick="window._generateFromHotTopic(\'' + cardId + '\')">' +
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">' +
'<span style="font-size:0.65rem;text-transform:uppercase;font-weight:600;color:' + c + '">' + t.urgency + '</span>' +
'<span style="font-size:0.65rem;text-transform:uppercase;font-weight:600;color:' + c + '">' + (t.urgency || '') + '</span>' +
'<span style="font-size:0.6rem;color:var(--text-dim)">' + (t.source_type || '') + '</span></div>' +
'<div class="gen-card-title" style="font-size:0.85rem;line-height:1.3">' + t.title + '</div>' +
'<div class="gen-card-sub" style="font-size:0.7rem;margin-top:4px">' + ((t.suggested_angle || t.description || '')).slice(0, 80) + '</div>' +
'<div class="gen-card-title" style="font-size:0.85rem;line-height:1.3">' + (t.title || '') + '</div>' +
'<div class="gen-card-sub" style="font-size:0.7rem;margin-top:4px">' + (t.suggested_angle || t.description || '').slice(0, 90) + '</div>' +
'</div>';
}).join('');
}).catch(function() {
@ -152,6 +164,13 @@
});
};
// Generate blog from hot topic card — uses title + angle from stored topic object
window._generateFromHotTopic = function(cardId) {
var t = window['_ht_' + cardId];
if (!t) return;
generateBlog(t.blog_type || 'hype_cycle', null, t.title, t.suggested_angle || t.description);
};
// Auto-load hot topics when blog tab activates
var origActivateTab = window.activateTab;
if (origActivateTab) {

View File

@ -862,7 +862,7 @@
<!-- BLOG -->
<div id="tab-blog" class="hidden">
<div style="margin-bottom:0.8rem;display:flex;justify-content:space-between;align-items:center">
<h3 style="font-size:1rem;font-weight:600">Hot Topics <span style="font-size:0.7rem;color:var(--text-dim);font-weight:400">auto-discovered from market data + conferences</span></h3>
<h3 style="font-size:1rem;font-weight:600">Hot Topics <span id="hot-topics-subtitle" style="font-size:0.7rem;color:var(--text-dim);font-weight:400">auto-discovered from market data + conferences</span></h3>
<button onclick="loadHotTopics()" style="background:var(--accent);color:white;border:none;padding:5px 12px;border-radius:6px;cursor:pointer;font-size:0.75rem">Refresh</button>
</div>
<div id="hot-topics-grid" class="grid g3 mb">