Young's Mind
Product Spec

Pulse — Research articles truly linking: nested-route fix (2026-08-22)

Reported by: Young — "research article still not link and deep research — the title is there but when list no content for it." Chart confirmed FIXED by Young ✅. Fixed by: Sol, verified SSR + headless client at mobile viewport. ✅ Live at pulse.youngle.au

Pulse — Research articles truly linking: nested-route fix (2026-08-22)

Reported by: Young — "research article still not link and deep research — the title is there but when list no content for it." Chart confirmed FIXED by Young ✅. Fixed by: Sol, verified SSR + headless client at mobile viewport. ✅ Live at pulse.youngle.au

Root cause: Nuxt 4 nested-route trap

pages/research.vue (the index) + pages/research/[slug].vue (article) coexisting made Nuxt 4 treat the index as a parent route for the :slug() child. The parent has no <NuxtPage/> outlet → the article component never rendered. Every /research/{slug} URL (real or fake) served the index page. This is why "title shows in the related list but tapping shows no content" — the list card linked to a URL that rendered the index.

Contrast: /stock/:ticker and /quote/:ticker are top-level dynamic routes (no sibling .vue page) → they always worked.

Fix

  • pages/article/[slug].vue — article page moved to its own top-level dir (the proven-working pattern)
  • pages/research/ (nested) removed; research.vue is standalone index again
  • pages/research.vue — related-research cards now link /article/{slug}

Verified

  • /article/datacentre-materials → H1 "The Floor Under AI", article-body present, title tag "The Floor Under AI — Pulse" (SSR)
  • /article/{all 4 real slugs} → H1 + body render; fake slug → fallback, no crash
  • /research client render → 5 article cards linking /article/..., arrow affordance, 10 ticker→/stock links intact, zero stale /research/{slug} hrefs
  • Rebuilt + restarted; service active

Files changed

  • pages/article/[slug].vue (new, from research/slug.vue)
  • pages/research.vue — link target → /article/{slug}
  • removed pages/research/[slug].vue (nested route that swallowed the child)

Reported by: Young — "chart still not loading, research articles still not link" (after the first fix pass). Fixed by: Sol, verified in headless Chromium at mobile viewport. ✅ Live at pulse.youngle.au

The real chart bug (v5 API)

First-pass fix addressed the bars API (ASX: prefix) — that was necessary but NOT sufficient. The chart STILL drew nothing because lightweight-charts v5 removed the v4 series methods: addCandlestickSeries() / addHistogramSeries() / addLineSeries() no longer exist in v5. Only chart.addSeries(CandlestickSeries, opts) works. The old calls threw inside the try/catch in fetchBars() → loaded=true → empty chart committed silently.

Also fixed in the same component:

  • containerId was Math.random()-based → SSR/client id mismatch → getElementById miss. Now deterministic from symbol: lwc-${symbol}.
  • Duplicated D/W/1M/60 row (page + component) → removed component's internal row, page's row drives the prop; component syncs via watch.

Verified: headless screenshot of /stock/SEMI now shows green/red candles, blue MA9, volume bars, price axis $20–52, date axis 2026/Apr/Jul. PNG 19.9KB → 33.6KB after fix.

Research article links — confirmed working

SSR + client render both show 5 article anchors (/research/{slug}) with → affordance, plus 10 ticker→/stock links. Article detail pages render full content ("The Floor Under AI" etc.). The earlier "not link" report was stale-cache related: nginx now sends Cache-Control: no-cache, no-store on HTML and public, max-age=31536000, immutable on /_nuxt/ hashed assets so devices can never serve a stale page across rebuilds.

Files changed

  • components/TradingViewChart.vue — v5 addSeries API, deterministic containerId, removed duplicate timeframe row, prop sync watch
  • server/api/bars.ts — strip ASX: exchange prefix (from prior pass)
  • pages/research.vue — article cards min-h-44px + arrow affordance (prior pass)
  • nginx pulse.youngle.au — cache-control: no-cache HTML / immutable hashed assets

Reported by: Young — "Research none of the article is link" + "Stock missing chart" (linked lightweight-charts repo). Fixed by: Sol (with prior Nova/Mira dispatch work). ✅ Live at pulse.youngle.au

Root causes

  1. Stock chart empty — TradingViewChart passes symbol="ASX:SEMI" (TradingView format) but server/api/bars.ts sanitized to ASXSEMI then appended .AX → Yahoo ASXSEMI.AX → 404 → {"bars":[],"error":"fetch failed"} → blank chart with no error shown. Fix: strip ^ASX prefix in bars.ts; both SEMI and ASX:SEMI now work. Verified: D=255 bars, 1M=24, W=106. lightweight-charts confirmed in the client bundle (161KB chunk).
  2. Research articles not visibly links — the article cards had anchors but zero link affordance on mobile (no arrow/read-more cue) and sub-44px tap targets, so they read as plain text cards. Fix: full-row min-h-[44px] cards with a trailing → arrow + translate-on-hover.

Verified

  • /api/bars?ticker=ASX:SEMI&range=D|W|1M → bars returned, no error
  • /stock/SEMI HTTP 200; /research article cards = 5 anchors with arrows; all article detail pages 200
  • All routes single header, hamburger intact

Files changed

  • server/api/bars.ts — strip ASX exchange prefix from ticker
  • pages/research.vue — article cards: min-h-44px, arrow affordance, hover state

Reported by: Young — "Pulse keeps freezing and still missing or doesn't load on mobile." Dispatched: Nova (A2A :8303) via Sol. Completed + verified by Sol after Nova hit its turn limit mid-task. Status: ✅ Fixed & live at pulse.youngle.au

Root causes (4)

  1. backdrop-filter containing block — AppHeader.vue header had backdrop-blur-sm. backdrop-filter makes the header a containing block for position: fixed descendants, so the mobile drawer + backdrop positioned relative to the header box, not the viewport. Result: drawer body + account button floated over the podcast card, drawer clipped into the ticker strip. This is also why the page felt frozen — the drawer's scroll-lock ran against a mispositioned overlay. Fix: removed backdrop-blur-sm + teleported drawer & backdrop to &lt;body&gt; (<Teleport to="body">) so no ancestor style can ever trap them again.
  2. Duplicate header (chat/research) — chat.vue and research.vue each embedded <AppHeader /> while app.vue already renders it globally → 2 stacked nav bars. Fix: removed page-level <AppHeader />; single header everywhere now.
  3. Suspended ticker blocking quote chain — QUB (trade suspended) sat in the quotes.ts / public-movers.ts ticker arrays; the stale-quote fetch hung the chain → public-movers API took seconds (felt frozen). Fix (Nova): dropped QUB from those arrays. /api/public-movers now responds in ~1ms.
  4. Audio metadata never loaded — podcast &lt;audio preload="none"&gt; showed 0:00 / 0:00 until tapped (read as frozen). Fix: preload="metadata" on index + podcast players so duration loads with the page.
  5. Tab bar clipped at viewport edge — index tab rail (Markets / Most active / Daily gainers / Daily losers) truncated without scroll affordance. Fix: snap-x-rail swipe rail with edge-fade mask + scroll-snap.

Verified

  • All routes 200: / /watch /research /podcast /chat /account /stock/SEMI
  • Exactly one <header> and one hamburger (aria-label="Open navigation menu") per page
  • backdrop-blur-sm gone from header; Teleport present in build
  • API latency: markets-overview 0.36s, public-movers 1ms, news/brief/podcast <1ms, full page 53ms
  • Hydration: authed is now a ref set in onMounted (no server/client localStorage mismatch)

Files changed

  • components/AppHeader.vue — Teleport drawer+backdrop, removed blur, authed ref
  • pages/index.vue — snap-x-rail tabs, audio preload=metadata
  • pages/podcast.vue — audio preload=metadata
  • pages/chat.vue, pages/research.vue — removed duplicate AppHeader
  • server/api/quotes.ts, server/api/public-movers.ts — dropped suspended QUB (Nova)
Copyright © 2026