Commit Graph

1307 Commits

Author SHA1 Message Date
Elie Habib
caeb3ead2c chore: bump v2.5.14 (#428)
Changes since v2.5.13:
- feat: add ARM64 Linux build target and download detection (#427)
- fix(live-channels): tolerate YouTube API failures when adding custom channels (#425)
- fix(linux): append host GStreamer plugins to AppImage search path (#424)
- fix(linux): enable keyring persistence via Secret Service + keyutils (#419)
v2.5.14
2026-02-26 20:58:26 +04:00
Elie Habib
ab502f6062 feat: add ARM64 Linux build target and download detection (#427)
- CI: add ubuntu-24.04-arm matrix entry with aarch64-unknown-linux-gnu
- Node sidecar: download linux-arm64 Node.js bundle for ARM runners
- Download API: add linux-appimage-arm64 pattern for aarch64 AppImage
- Download banner: show both x64 and ARM64 Linux options (UA can't distinguish)
- Desktop updater: map Linux aarch64 to linux-appimage-arm64 for in-app updates
- Smoke test: fix AppImage search path for cross-target builds
2026-02-26 20:52:44 +04:00
Elie Habib
bbe814c985 fix(live-channels): tolerate YouTube API failures when adding custom channels (#425)
The /api/youtube/live validation endpoint may return 429 or non-JSON
responses (Vercel WAF, YouTube rate limiting). Previously this caused
res.json() to parse HTML → either throw (caught, channel added) or
return channelExists:false (blocked add with red border).

Now only blocks when the API explicitly returns 200 OK with
channelExists:false — any non-OK status or error allows the add.

Also bumps version to 2.5.13.
v2.5.13
2026-02-26 20:33:11 +04:00
Elie Habib
30bd84abb2 fix(linux): append host GStreamer plugins to AppImage search path (#424)
* chore: bump v2.5.12

## Changelog

- fix(linux): enable keyring persistence via Secret Service + keyutils (#419)
- fix(ci): use weston+XWayland for Linux smoke test (#417)
- ci: add standalone Test Linux App workflow (#414)
- ci: skip Typecheck and Lint on fork PRs (#415)
- perf: optimize Wingbits API usage and reduce unnecessary polling (#416)

* fix(linux): append host GStreamer plugins to AppImage search path

The linuxdeploy GStreamer hook force-overrides GST_PLUGIN_PATH_1_0 and
GST_PLUGIN_SYSTEM_PATH_1_0 to only contain bundled plugins from the CI
build system (Ubuntu 24.04, GStreamer 1.24).  On hosts with newer
GStreamer (e.g. Arch 1.28), codec plugins like gst-libav and
fakevideosink from gst-plugins-bad are invisible — WebKit can't play
video.

Append common host GStreamer plugin directories as fallback so the
system's codec plugins are discoverable while bundled plugins retain
priority.

Also fixes:
- tauri.conf.json devUrl port mismatch (5173 → 3000) breaking desktop:dev
- live-channels-window YouTube validation allowing add on non-OK responses
2026-02-26 20:24:50 +04:00
Elie Habib
dd4f4ad786 fix(linux): enable keyring persistence via Secret Service + keyutils (#419)
Linux had no keyring backend feature enabled — keyring v3 fell back to
in-memory mock store. Secrets appeared to save but were lost on restart.

Added `linux-native-sync-persistent` (kernel keyutils + D-Bus Secret
Service combo) and `crypto-rust` for Secret Service encryption. This
uses GNOME Keyring or KDE Wallet on desktop Linux for persistent storage.
2026-02-26 18:47:29 +04:00
Elie Habib
9c9323732e Optimize Wingbits API usage and reduce unnecessary polling (#416)
* ci(linux): add AppImage smoke test to desktop build

Launch the built AppImage under Xvfb after the Linux build to catch
startup crashes and render failures automatically. Uploads a screenshot
artifact for visual inspection.

* Optimize Wingbits API usage and panel polling
2026-02-26 17:59:15 +04:00
Elie Habib
1b719701bf ci: skip Typecheck and Lint on fork PRs (#415)
Only run CI on PRs from branches within the repo, not from external
forks. Prevents unnecessary action minutes from contributor PRs.
2026-02-26 17:57:33 +04:00
Elie Habib
6d1821229e fix(ci): use weston+XWayland for Linux smoke test (#417)
* fix(ci): use weston+XWayland for Linux smoke test instead of pure Wayland

Previous attempt used GDK_BACKEND=wayland which caused GTK init panic
(tao requires X11). Now: weston headless with XWayland provides X11
through a real compositor. Falls back to Xvfb if weston fails.
Also uploads weston/app logs as artifacts for debugging.

* refactor(ci): use xwfb-run for Linux smoke test display

xwfb-run (from xwayland-run package) is purpose-built for this:
Xwayland on a headless Wayland compositor, replaces xvfb-run.
Falls back to plain Xvfb if xwfb-run is unavailable.
Uploads display-server and app logs as artifacts.
2026-02-26 17:57:10 +04:00
Elie Habib
ba903aa02a ci: add standalone Test Linux App workflow, revert build-desktop.yml (#414)
The squash merge of #413 put smoke test steps into build-desktop.yml
instead of the intended standalone workflow. This commit:
- Reverts build-desktop.yml to its original state
- Adds test-linux-app.yml as a separate Linux-only smoke test workflow
2026-02-26 17:21:02 +04:00
Elie Habib
af99606cf9 ci(linux): add AppImage smoke test to desktop build (#413)
Launch the built AppImage under Xvfb after the Linux build to catch
startup crashes and render failures automatically. Uploads a screenshot
artifact for visual inspection.
2026-02-26 16:57:57 +04:00
Elie Habib
5fa5a73e30 chore: bump v2.5.11 (#412)
Fixes:
- Linux AppImage black screen on WebKit/GStreamer (#411)
- Destroy live news player before showing offline/error message (#410)
v2.5.11
2026-02-26 16:45:35 +04:00
Elie Habib
d2e00bc581 fix(linux): address AppImage black screen on WebKit/GStreamer (#411) 2026-02-26 16:41:00 +04:00
Elie Habib
e40fe1aeb6 fix(live-news): destroy player before showing offline/error message (#347) (#410)
showOfflineMessage() and showEmbedError() replaced innerHTML without
cleaning up this.player, so initializePlayer() bailed early on the
next channel switch — leaving a black screen. destroyPlayer() is
idempotent so the onError path won't double-destroy.

Co-authored-by: N Cho-chin <Niboshi-Wasabi@users.noreply.github.com>
2026-02-26 16:09:35 +04:00
Elie Habib
012907ae84 chore: bump v2.5.10 and update README for recent fixes (#408)
* chore: bump v2.5.10 and update README for recent fixes

Version 2.5.9 → 2.5.10. Roadmap entries for:
- Yahoo Finance rate-limit UX across all market panels
- Sidecar auth resilience (401-retry, settings diagFetch)
- Verbose toggle persistence to writable data directory
- Finnhub-to-Yahoo fallback routing

* chore: add v2.5.10 changelog entry
v2.5.10
2026-02-26 15:23:21 +04:00
Elie Habib
2db217211e fix: surface Yahoo rate-limit status to user, auth retry, verbose persistence (#407)
- Show rate-limited message instead of generic "Failed to load" on Markets,
  ETF, Commodities, and Sector panels when Yahoo returns 429
- fetchYahooQuotesBatch returns rateLimited flag; early-exit after 3 misses
- ETF panel skips retry loop when rate-limited, shows specific i18n message
- Fallback Finnhub symbols through Yahoo when API key missing
- 401-retry in runtime fetch patch for stale sidecar token after restart
- diagFetch auth helper for settings window diagnostic endpoints
- Verbose toggle writes to writable dataDir instead of read-only app bundle
2026-02-26 14:49:25 +04:00
Elie Habib
1cc961b491 fix: sequential Yahoo calls, sector fallback, and missing try-catch guards (#406)
- list-commodity-quotes: replace Promise.all with fetchYahooQuotesBatch to prevent Yahoo 429
- get-sector-summary: add Yahoo Finance fallback when FINNHUB_API_KEY is missing
- list-etf-flows: sequential fetch loop + add missing try-catch around cachedFetchJson
- get-macro-signals: replace unnecessary Promise.allSettled([single]) with direct await
2026-02-26 13:12:37 +04:00
Elie Habib
fc7b91fa9d fix: tighten AI summary prompts to 2 sentences / 60 words max (#404)
* fix: sequential Yahoo calls, sector fallback, and missing try-catch guards

- list-commodity-quotes: replace Promise.all with fetchYahooQuotesBatch to prevent Yahoo 429
- get-sector-summary: add Yahoo Finance fallback when FINNHUB_API_KEY is missing
- list-etf-flows: sequential fetch loop + add missing try-catch around cachedFetchJson
- get-macro-signals: replace unnecessary Promise.allSettled([single]) with direct await

* fix: tighten AI summary prompts to 2 sentences / 60 words max

Summaries were often verbose walls of text despite "2-3 sentences" in
the prompt. Changed to "2 concise sentences MAX (under 60 words total)"
across all brief, analysis, and fallback prompts. Reduced max_tokens
from 150 to 100 to hard-cap output length.
2026-02-26 13:11:41 +04:00
Elie Habib
e5fc09d1fc fix: rename settings modal title from "PANELS" to "SETTINGS" in all 17 locales (#403)
The header.settings i18n key was incorrectly set to "PANELS" (and its
translations) for the settings modal title. The modal contains General,
Panels, and Sources tabs — the overall title should be "SETTINGS".
2026-02-26 12:54:45 +04:00
Elie Habib
812c6d0562 fix: Sentry triage — escape CSS selector, guard player.destroy, add noise filters (#402)
- CSS.escape() on data-news-id querySelector to prevent SyntaxError
  when news item IDs contain special characters (WORLDMONITOR-5J)
- typeof guard on this.player.destroy() for partially-initialized
  YouTube players (WORLDMONITOR-5C/5B)
- 11 new ignoreErrors patterns for IndexedDB races, browser vendor
  internals, and extension-injected errors
- beforeSend filter for blob: URL extension frames
2026-02-26 12:36:41 +04:00
Elie Habib
a560efff49 chore: bump v2.5.9 and update README for recent features (#398)
* fix: sort tariff datapoints newest-first in trade policy panel

* fix: update tests broken by cachedFetchJson migration

- Restore "Strip unterminated" comment in summarize-article.ts that
  tests use to locate the unterminated tag stripping section
- Update ACLED tests to check for cachedFetchJson instead of removed
  getCachedJson/setCachedJson patterns

* chore: bump version to 2.5.9 and make pre-push hook executable

* docs: update README with supply chain intel, universal CII, Happy Monitor, security hardening, and recent features
v2.5.9
2026-02-26 10:26:08 +04:00
Elie Habib
2839d884db fix: sort supply chain chokepoints by disruption score descending (#399)
* fix: sort tariff datapoints newest-first in trade policy panel

* fix: update tests broken by cachedFetchJson migration

- Restore "Strip unterminated" comment in summarize-article.ts that
  tests use to locate the unterminated tag stripping section
- Update ACLED tests to check for cachedFetchJson instead of removed
  getCachedJson/setCachedJson patterns

* fix: sort supply chain chokepoints by disruption score descending
2026-02-26 10:25:55 +04:00
Elie Habib
0060a71a66 fix: sort tariff datapoints newest-first and fix cachedFetchJson test (#400)
* fix: sort tariff datapoints newest-first in trade policy panel

* fix: update tests broken by cachedFetchJson migration

- Restore "Strip unterminated" comment in summarize-article.ts that
  tests use to locate the unterminated tag stripping section
- Update ACLED tests to check for cachedFetchJson instead of removed
  getCachedJson/setCachedJson patterns
2026-02-26 10:25:41 +04:00
Elie Habib
f912c5f15a fix: restore Linux AppImage updater routing and fallback port reporting (#397) 2026-02-26 10:07:59 +04:00
Elie Habib
384cc78daa feat: add global streaming video quality setting (#365) (#396)
Add a "Streaming" section to the GENERAL settings tab with a quality
dropdown (Auto / Low 360p / Medium 480p / High / HD 720p). The setting
persists to localStorage and applies to all live streams:

- LiveWebcamsPanel: appends vq= to direct embeds, passes through proxy
- LiveNewsPanel: sets quality via YT.Player API onReady + desktop proxy
- YouTube embed proxy: accepts vq param, calls setPlaybackQuality()

Closes #365
2026-02-26 09:57:43 +04:00
Elie Habib
5221486c88 fix: Linux AppImage crashes, RSS proxy 403s, and console noise (#395)
Linux AppImage (#370, #257):
- Upgrade CI from Ubuntu 22.04 to 24.04 (GLib 2.80 fixes g_task_set_static_name symbol mismatch)
- Set GDK_BACKEND=wayland,x11 when WAYLAND_DISPLAY detected (fixes GTK init on niri/river)
- Disable WebKit bubblewrap sandbox in AppImage context (FUSE mount breaks it → blank screen)
- Set GTK_IM_MODULE to built-in simple context in AppImage (prevents host module conflicts)

RSS proxy:
- Add 32 missing domains to allowlist (EuroNews lang variants, international news feeds)
- Normalize www prefix in domain validation (prevents entire class of www/non-www mismatch 403s)

Console cleanup:
- TrendingKeywords: console.log → console.debug for suppressed terms (30+ noisy log lines)
- PostHog: add ui_host for reverse proxy setup (fixes /ingest/ 404s)
2026-02-26 09:37:26 +04:00
Elie Habib
e5b30ba28c ci: add TypeScript type-check workflow on PRs (#394)
Runs npm run typecheck (tsc --noEmit) on every PR to catch type
errors before merge. Would have prevented the #382 regression.
2026-02-26 09:37:16 +04:00
Elie Habib
8ac824ebb4 feat: add Ransomware.live RSS feed to cyber/security sources (#392)
- Add to tech variant security feed group
- Add to main variant global feeds as type: 'cyber'
- Add www.ransomware.live to RSS proxy allowlist
- Set source tier to 3 (specialty)
2026-02-26 09:30:19 +04:00
Elie Habib
be82576037 fix: restore FilterSpecification type annotation on country highlight filters (#393)
PR #382 accidentally removed the maplibregl.FilterSpecification type
cast, causing tsc to infer (string | string[])[] which doesn't satisfy
setFilter's parameter type. This broke the Vercel build.
2026-02-26 09:29:20 +04:00
Fayez Bast
66e9f818d4 fix: keep flat news on cluster failure and fix deckgl filter typing (#382) 2026-02-26 09:09:27 +04:00
Elie Habib
75dc6418ea fix: supply chain panel bugs — invalid FRED series, global banner, feature gate (#391)
- Replace non-existent BDIY (Baltic Dry Index) FRED series with real
  public series: PCU483111483111 (Deep Sea Freight PPI) and TSIFRGHT
  (Freight Transportation Index). BDIY is proprietary Baltic Exchange
  data not available on FRED — all shipping requests were failing.
- Make "upstream unavailable" banner per-tab instead of global — shipping
  failure no longer shows the warning on chokepoints and minerals tabs
  that have valid data.
- Remove inconsistent isFeatureAvailable('supplyChain') gate from
  frontend fetchShippingRates() — server handler already returns empty
  when FRED_API_KEY is missing, and chokepoints/minerals had no gate.
2026-02-26 09:05:03 +04:00
Elie Habib
e0d0cf9790 fix: respect Browser Local Model toggle — skip ML worker init when disabled (#390)
mlWorker.init() was called unconditionally in App.init(), ignoring the
browserModel setting. This caused ONNX model downloads, 120s embed
timeouts, and clustering warnings even with the toggle OFF.

- Gate mlWorker.init() on browserModel setting (or desktop runtime)
- Subscribe to setting changes for dynamic init/terminate on toggle
- Replace mlWorker.init() in sentiment-gate with isAvailable check
- Clean up subscription in App.destroy() to prevent HMR listener leaks
2026-02-26 08:53:43 +04:00
Elie Habib
3983278f53 feat: dynamic sidecar port with EADDRINUSE fallback + let scoping bug (#375)
* feat: dynamic sidecar port with EADDRINUSE fallback

Rust probes port 46123 via TcpListener::bind; if busy, binds port 0 for
an OS-assigned ephemeral port. The actual port is stored in LocalApiState,
passed to sidecar via LOCAL_API_PORT env, and exposed to frontend via
get_local_api_port IPC command.

Frontend resolves the port lazily on first API call (with retry-on-failure
semantics) and caches it. All hardcoded 46123 references replaced with
dynamic getApiBaseUrl()/getLocalApiPort() accessors. CSP connect-src
broadened to http://127.0.0.1:* (frame-src unchanged).

* fix: scope CSP to desktop builds and eliminate port TOCTOU race

P1: Remove http://127.0.0.1:* from index.html (web build CSP). The
wildcard allowed web app JS to probe arbitrary localhost services.
Vite's htmlVariantPlugin now injects localhost CSP only when
VITE_DESKTOP_RUNTIME=1 (desktop builds).

P2: Replace Rust probe_available_port() (bind→release→spawn race)
with a confirmed port handshake. Sidecar now handles EADDRINUSE
fallback internally and writes the actual bound port to a file.
Rust polls the port file (up to 5s) to store only the confirmed port.

* fix: isSafeUrl ReferenceError — addresses scoped inside try block

`let addresses = []` was declared inside the outer `try` block but
referenced after the `catch` on line 200. `let` is block-scoped so
every request through isSafeUrl crashed with:
  ReferenceError: addresses is not defined

Move the declaration before the `try` so it's in scope for the return.
2026-02-26 08:51:59 +04:00
Elie Habib
dc30693135 feat: add Supply Chain Disruption Intelligence service (#387)
* feat: add Supply Chain Disruption Intelligence service

Add new supply_chain domain with 3 RPCs:
- GetShippingRates: FRED Baltic Dry Index with spike detection
- GetChokepointStatus: NGA nav warnings + AIS vessel aggregation
  for 6 global chokepoints with composite disruption scoring
- GetCriticalMinerals: HHI concentration analysis for 7 strategic
  minerals from USGS 2024 production data

Frontend: 3-tab SupplyChainPanel with event delegation, sparkline
SVG, escapeHtml XSS hardening, and circuit breakers per RPC.

13 unit tests for pure scoring functions (_scoring.mjs).

* fix: chokepoint upstream detection, FRED weekly frequency, missing CSS

P1: Chokepoint handler propagates upstreamUnavailable when maritime
sources fail or return empty, instead of always reporting false.

P1: FRED BDI uses frequency=w for true 52-week window.

P2: Add CSS for sc-status-dot, sc-dot-*, sc-risk-* classes.

* fix: allow partial chokepoint data when one upstream source succeeds
2026-02-26 08:20:33 +04:00
Elie Habib
b0071d1a14 fix: eliminate cache stampede across all server handlers (#389)
* fix: use cachedFetchJson for theater posture to prevent Wingbits API stampede

The theater posture handler used manual getCachedJson/setCachedJson which
has no request coalescing. During the window between a cache miss and the
cache write completing (~15s Wingbits timeout), every concurrent request
bypassed the cache and fired its own POST to Wingbits with 9 bounding
boxes — causing ~1500 requests/min.

Switch to cachedFetchJson which deduplicates concurrent in-flight
requests via a shared promise, so only one upstream fetch runs at a time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: migrate all handlers to cachedFetchJson, fix metadata labeling with cachedFetchJsonWithMeta

- Migrate 48 server handlers from manual getCachedJson/setCachedJson to
  cachedFetchJson, eliminating cache stampede vulnerability across the
  entire codebase (concurrent cache misses now coalesce into one fetch)
- Add cachedFetchJsonWithMeta to redis.ts — returns { data, source }
  atomically, fixing TOCTOU race between separate cache check and fetch
- Wire summarize-article.ts and get-usni-fleet-report.ts to use
  cachedFetchJsonWithMeta for correct provider/cached metadata
- Add try/catch to 7 handlers that lost graceful degradation
- Add 4 tests for WithMeta source labeling: cache hit, fresh miss,
  coalesced followers, and TOCTOU regression protection

* chore: trigger PR sync

---------

Co-authored-by: Mikael Sundberg <mikael.sundberg82@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 07:54:08 +04:00
Elie Habib
1844276017 fix: trade policy panel visual overhaul and false unavailable banner (#385)
- Suppress "WTO data temporarily unavailable" banner when cached data
  exists on the active tab — only show when truly empty + upstream down
- Add all missing CSS for trade policy panel (228 lines) — cards with
  hover states, color-coded status badges, structured tariff table,
  trade flow metrics layout, source links, and warning banner styling
2026-02-26 03:09:38 +04:00
Elie Habib
24d42f70f8 fix: economic panel showing wrong values and unresponsive tabs (#384)
Two bugs:
1. Single shared circuit breaker cached one FRED series response for all 7 series,
   causing identical wrong values across indicators. Now uses per-series breakers.
2. Tab click listeners were destroyed by setContent() debounce. Replaced with
   event delegation on the stable content element.
2026-02-26 02:58:08 +04:00
Elie Habib
95228647b4 fix: correct trade policy panel data display issues (#383)
- Remove "Bound Rate" column from tariffs table — WTO TP_B_0090
  bound rate data unavailable on our subscription tier (always 0.0%)
- Fix restrictions status labels: backend returns high/moderate/low
  but panel expected IN_FORCE/TERMINATED — now matches actual values
- Update all 17 locale files with new tariff level labels
2026-02-26 01:39:16 +04:00
Elie Habib
bf4e216efd fix: add diagnostic logging to WTO API fetch for silent failures (#376)
wtoFetch() silently returned null on missing key, HTTP errors, and
exceptions — making it impossible to diagnose why trade data shows
"temporarily unavailable". Now logs the specific failure reason.
2026-02-25 13:14:45 +00:00
Elie Habib
6ac39d5166 fix: use cachedFetchJson for WTO endpoints + add 10-min refresh schedule (#373)
Switch all 4 WTO trade endpoints from manual getCachedJson/setCachedJson
to cachedFetchJson, which coalesces concurrent cache misses into a single
upstream WTO API call. This prevents hammering the WTO API when multiple
requests arrive during a cache miss window.

Also register tradePolicy with the RefreshScheduler at a 10-minute
interval (full/finance variants) — previously it was only fetched once
at startup with no periodic refresh.

https://claude.ai/code/session_01FdvFhpLALL9iJaF8iXMjtu

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 12:44:27 +00:00
Elie Habib
5c4b1ffc6b fix: WTO API indicator codes, disable giving panel, update README (#371) 2026-02-25 11:19:04 +00:00
Elie Habib
07d0803014 Add WTO trade policy intelligence service with tariffs, flows, and barriers (#364)
* feat: add WTO trade policy service with 4 RPC endpoints and TradePolicyPanel

Adds a new `trade` RPC domain backed by the WTO API (apiportal.wto.org) for
trade policy intelligence: quantitative restrictions, tariff timeseries,
bilateral trade flows, and SPS/TBT barrier notifications.

New files: 6 protos, generated server/client, 4 server handlers + shared WTO
fetch utility, client service with circuit breakers, TradePolicyPanel (4 tabs),
and full API key infrastructure (Rust keychain, sidecar, runtime config).

Panel registered for FULL and FINANCE variants with data loader integration,
command palette entry, status panel tracking, data freshness monitoring, and
i18n across all 17 locale files.

https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye

* chore: update package-lock.json

https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye

* fix: move tab click listener to constructor to prevent leak

The delegated click handler was added inside render(), which runs
on every data update (4× per load cycle). Since the listener targets
this.content (a persistent container), each call stacked a duplicate
handler. Moving it to the constructor binds it exactly once.

https://claude.ai/code/session_01HZXyoQp6xK3TX8obDzv6Ye

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 10:50:12 +00:00
Elie Habib
b238618e13 Add BIS central bank data integration (policy rates, exchange rates, credit) (#363)
* feat: add BIS Central Bank Policy Tracker to EconomicPanel

Add a new "Central Banks" tab to the Economic panel powered by the BIS
(Bank for International Settlements) free SDMX REST API. Displays policy
rates, effective exchange rates, and credit-to-GDP ratios for 12 major
central banks (Fed, ECB, BOE, BOJ, SNB, MAS, RBI, RBA, PBOC, BOC, BOK,
BCB).

- Proto: 4 new files (bis_data, get_bis_policy_rates, get_bis_exchange_rates,
  get_bis_credit) + 3 RPCs added to EconomicService
- Server: shared CSV fetch/parse helper using papaparse, 3 handlers with
  Redis caching (6h/12h TTL), batched single-request per dataset
- Frontend: 3 separate circuit breakers, fetchBisData() with Promise.all
- UI: Central Banks tab with policy rates (sorted by rate, colored by
  cut/hike/hold), real EER indices, and credit-to-GDP ratios
- Integration: 'bis' DataSourceId, BIS in StatusPanel WORLD_APIS, 60min
  refresh interval, data loader wiring
- i18n: BIS keys added to all 17 locale files

No API key required — BIS is free/public. All BIS-derived text rendered
via escapeHtml() to prevent XSS.

https://claude.ai/code/session_01N73WokR4NPuSg8JpPz7nYZ

* chore: update package-lock.json

https://claude.ai/code/session_01N73WokR4NPuSg8JpPz7nYZ

* fix: BIS policy tracker - URL bug, error logging, UX, and data cleanup

- Fix double '?' in fetchBisCSV URL construction (format=csv was silently ignored)
- Add error logging to all 3 BIS server handlers (previously silent catch blocks)
- Fix misleading "BIS data loading..." empty state to "temporarily unavailable - will retry"
- Remove unused nominal EER fetch to save bandwidth (only real EER is displayed)
- Fix previousRatio rounding inconsistency in credit-to-GDP fallback path

https://claude.ai/code/session_01N73WokR4NPuSg8JpPz7nYZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-25 14:46:27 +04:00
Elie Habib
5206027573 fix: filter deck.gl Uint8Array WebGL pick noise in Sentry (#362)
Add /^Uint8Array$/ to ignoreErrors — deck.gl readPixelsToArrayWebGL
crash on Windows Edge when GPU context is stressed. Not actionable.
2026-02-25 10:12:24 +00:00
Elie Habib
e7260be2f2 fix: improve API key signup link discoverability in settings (#361)
Move the barely-visible ↗ arrow into an in-input "Get key" button
that only appears when the key is missing. Much clearer affordance.
2026-02-25 10:06:27 +00:00
Sebastien Melki
cc8f5f3f01 fix: RSS proxy allowlist + add 9 new positive news feeds (#360)
* fix: add redirect target domains to RSS proxy allowlist

HuffPost and Nature feeds redirect to different hostnames
(chaski.huffpost.com, www.nature.com) that weren't in ALLOWED_DOMAINS,
causing the proxy to reject the redirect and fall through to the
Railway relay (which is currently down).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove dead RSS feeds and add 9 new positive news sources

Remove Sunny Skyz (old URL dead) and HuffPost Good News (feed discontinued).
Replace with verified working feeds:
- Positive: Upworthy, DailyGood, Good Good Good, GOOD Magazine,
  Sunny Skyz (new URL), The Better India
- Science: Singularity Hub, Human Progress, Greater Good (Berkeley)

Also add redirect target domains (www.nature.com) to proxy allowlist
and remove dead HuffPost domains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add fallback data for Progress/Renewable panels + expand Breakthroughs sources

World Bank API is intermittent (some indicators time out with 0 bytes).
Both Human Progress and Renewable Energy panels silently returned empty
when API was unavailable and no IndexedDB cache existed.

- Add hardcoded fallback datasets (verified from World Bank, Feb 2026)
  for all 4 progress indicators and renewable energy global/regional data
- Use fallback instead of empty on any API failure or empty response
- Add Singularity Hub, Human Progress, Greater Good (Berkeley) to
  Breakthroughs panel source filter so new science feeds show up

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:51:08 +02:00
Elie Habib
e3bdde0d92 chore: bump version to 2.5.8 and align README with recent features (#359)
Update data layer count to 36+, add Happy Monitor variant to Live Demos,
expand Cmd+K command palette description, and add trade routes to
Infrastructure section.
2026-02-25 08:12:39 +00:00
Elie Habib
cb0235ca6b feat: add trade route & chokepoint visualization layer (#358)
Static layer showing 19 major global shipping lanes as multi-segment
arcs through strategic chokepoints, with amber dots highlighting
active waterway passages. Routes are waypointed through STRATEGIC_WATERWAYS
coordinates so arcs visually pass through Suez, Malacca, Hormuz, etc.
rather than taking misleading great-circle shortcuts.

- New src/config/trade-routes.ts with 19 routes, segment resolver
- ArcLayer (greatCircle per segment) + ScatterplotLayer for chokepoints
- Status-based coloring (active/disrupted/high_risk) with light theme support
- Finance variant ON by default, all others OFF
- Toggle UI, layer help, command palette, URL state persistence
- Translated layer labels across all 17 locales
2026-02-25 08:03:30 +00:00
Sebastien Melki
797d127ce1 fix: add redirect target domains to RSS proxy allowlist (#357)
HuffPost and Nature feeds redirect to different hostnames
(chaski.huffpost.com, www.nature.com) that weren't in ALLOWED_DOMAINS,
causing the proxy to reject the redirect and fall through to the
Railway relay (which is currently down).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:58:06 +04:00
Elie Habib
2f92dbee8b chore: remove .claudedocs + tighten Sentry noise filters (#356)
* chore: remove .claudedocs from repo and add to gitignore

Sentry triage log was accidentally committed in v2.4.0. These are
ephemeral Claude session artifacts, not project documentation.

* fix: tighten Sentry noise filters for maplibre/deck.gl and third-party errors

The beforeSend filename regex missed `maplibre-*` chunks and hashes
containing `-`, letting ~3700 map-internal crashes through. Also widen
imageManager filter for Safari wording and add filters for YouTube
widget API, Sentry SDK logs.js, Facebook WebView, and TDZ errors.
2026-02-25 07:26:19 +00:00
Elie Habib
1d069229c0 feat: country commands + command palette UX improvements (#355)
* chore: remove .claudedocs from repo and add to gitignore

Sentry triage log was accidentally committed in v2.4.0. These are
ephemeral Claude session artifacts, not project documentation.

* fix: tighten Sentry noise filters for maplibre/deck.gl and third-party errors

The beforeSend filename regex missed `maplibre-*` chunks and hashes
containing `-`, letting ~3700 map-internal crashes through. Also widen
imageManager filter for Safari wording and add filters for YouTube
widget API, Sentry SDK logs.js, Facebook WebView, and TDZ errors.

* feat: add country brief commands for all ISO countries to command palette

Generates country commands from all ~200 ISO 3166-1 codes using
Intl.DisplayNames for name resolution. Curated countries (30) retain
rich searchAliases for keyword matching (e.g. "kremlin" → Russia).

* feat: update command palette empty state and translate all 16 locales

Update placeholder, hint, and empty state text to reflect command
palette capabilities (countries, layers, panels, navigation, settings).
Add example commands row with styled kbd tags. Translate all strings
across ar, de, el, es, fr, it, ja, nl, pl, pt, ru, sv, th, tr, vi, zh.
2026-02-25 07:26:08 +00:00