40 Commits
Author SHA1 Message Date
Claude c430197f6a fix(world/finance): mount the terminal on the in-place variant switch too
The finance terminal only mounted on the initial ?variant=finance load; the new
in-place variant switch (setSiteVariant, no reload) ran ensureVariantPanels but
never mounted the terminal — so clicking the Finance tab showed the panel
dashboard, not the Bloomberg terminal. Replace mountFinanceTerminal with
syncFinanceTerminal(variant): mounts the terminal overlay when entering finance,
removes it (destroy) when leaving. Wired into both init() and the switch. Now the
tab works both ways — verified: in-place switch mounts 27 chart cards.
2026-07-21 21:10:01 -07:00
Claudeandzeekay 036859c334 feat(world/globe): real-time global flow layer — animated comms + trade arcs
A toggleable "Global flows" map layer draws the real backbone of world comms +
trade as animated great-circle arcs on the globe ("where everything is going"):
the principal submarine-cable / internet-backbone routes (cyan) and the major
maritime trade + energy lanes (amber), between real global hubs. Direction is
source → target with a travelling pulse (the existing AnimatedArcLayer + the
shared cloud-pulse clock).

- New src/config/global-flows.ts: 27 real corridors (NYC↔London transatlantic,
  Tokyo↔LA transpacific, Shanghai↔Rotterdam via Suez, Gulf oil → Asia, etc.),
  weighted by representative magnitude — no synthetic data.
- createGlobalFlowsLayer() in DeckGLMap: great-circle on the globe, alpha + width
  scale with corridor weight, comms/trade colour-coded.
- Low-end degradation (device tier): draws only the heaviest maxFlowArcs()
  corridors (40 low / 120 mid / 300 high) so a weak GPU never stalls.
- Toggle: "Global flows" in the layer panel (world + tech variants) + URL state;
  default OFF (overlay). Animated only while visible (cloud-pulse gate).
2026-07-21 20:58:49 -07:00
Claude e62cdddb85 feat(world/finance): Bloomberg-style finance terminal (charts ≫ news), lazy-loaded
The finance variant now renders a dense, dark terminal of LIVE TradingView
charts instead of the news-heavy map dashboard — global indices (S&P 500,
Nasdaq 100, Dow, VIX, Nikkei, Hang Seng, China A50, DAX, FTSE, CAC, Euro Stoxx,
ASX), commodities (gold, silver, WTI, Brent, nat gas, copper), forex (DXY +
majors) and crypto (BTC/ETH) — plus a live ticker tape, a market-overview
widget, a Lux DEX trade card and alt-asset feeds (auctions + luxury real estate).

- Code-split: App dynamic-imports FinanceTerminal only for the finance variant,
  so the TradingView embed logic + terminal CSS never ship in the entry bundle.
- Low-end degradation (device tier): fewer charts + lighter widget types
  (mini-symbol-overview instead of advanced-chart), and every widget lazy-mounts
  on viewport (IntersectionObserver) so a weak laptop never mounts dozens of
  live embeds at once. The map is skipped entirely for this variant (−2.7 MB).
- CAPITALCOM CFD index symbols (free-embed compatible); exchange-native SP:SPX /
  DJ:DJI etc. are account-gated and render an error card.
- Lux DEX: exchange.lux.network sets X-Frame-Options: DENY, so the SwapWidget
  can't be iframed — surfaced as a prominent launch card to the real DEX.
- Auctions (Sotheby's) + luxury RE (JamesEdition) fetch /v1/world/{auctions,
  luxury-realestate}; until that backend feed exists they show an honest
  "live feed pending" state — never fabricated listings.
- CSP (index.html): allow s3/s.tradingview.com (script) + the tradingview-widget
  frames. The production HANZO_STATIC_CSP is widened in the universe world CR.
2026-07-21 20:45:22 -07:00
Claudeandzeekay 04257213b8 perf(world): defer the map off the first-paint critical path (eager JS 4.1MB→1.4MB)
On a low-end laptop the globe (mapbox-gl + deck.gl ≈ 2.7 MB) was eagerly
modulepreloaded and constructed synchronously during renderLayout, so nothing
painted until ~4.1 MB of JS downloaded + parsed + WebGL initialised (≈17.7 s to
app-shell under 6× CPU throttle on the live build). This code-splits the map and
degrades gracefully for weak hardware.

- Map loads via dynamic import() in App.mountMap(); init awaits it AFTER the
  shell paints, before map-dependent wiring. Every this.map access was already
  null-guarded, and mountMap replays the initEscalation / onTimeRangeChanged /
  initial-URL-state wiring that used to run inline in createPanels.
- Isolate Vite's __vitePreload helper into its own chunk (manualChunks): it had
  been co-located INTO the 'map' chunk, so the entry statically imported 'map'
  just for that helper and Vite modulepreloaded the whole 2.7 MB before paint —
  THE reason the map stayed eager despite the dynamic import.
- @/components barrel re-exports the map classes as TYPES only (their modules
  have top-level side effects; value re-exports also dragged 'map' eager).
- Defer @sentry/browser (~460 KB raw / ~130 KB gzip) out of the entry to
  requestIdleCallback, with a synchronous early-error buffer replayed on init so
  no boot error is lost.
- New device-tier util (hardwareConcurrency + deviceMemory + reduced-motion,
  ?tier= override): low-end drops the deck.gl overlay DPR 2→1, skips the idle
  globe auto-spin, and scales real-time refresh intervals up to 2×.

Eager JS before first paint: 4136 KB → 1440 KB (raw). Map (2.7 MB) + Sentry now
load after first paint.
2026-07-21 20:30:04 -07:00
ClaudeandRau1CS 0f6c7df6f9 feat: add finance/trading variant with market-focused dashboard
Add a new 'finance' site variant (finance.worldmonitor.app) following the
same pattern as the existing tech variant. Includes:

- Finance-specific RSS feeds: markets, forex, bonds, commodities, crypto,
  central banks, economic data, IPOs/M&A, derivatives, fintech, regulation,
  institutional investors, and market analysis (all free/open RSS sources)
- Finance-focused panels with trading-themed labels (Market Headlines,
  Live Markets, Forex & Currencies, Fixed Income, etc.)
- Geographic data for stock exchanges (30+), financial centers (20+),
  central banks (14), and commodity hubs (10) worldwide
- Four new map layers: stockExchanges, financialCenters, centralBanks,
  commodityHubs with tier-based icons and zoom-dependent labels
- Map popup rendering for all finance marker types
- Variant switcher updated with FINANCE tab in header
- Search modal with finance-specific sources and icons
- Vite HTML variant plugin metadata for SEO
- Build scripts (dev:finance, build:finance, test:e2e:finance)
- Tauri desktop config for Finance Monitor app

https://claude.ai/code/session_01CCmkws2EYuUHjYDonzXEtY
2026-02-16 11:22:17 +01:00
Claude 538bc956e3 Add missing layers to DeckGLMap for feature parity with D3 Map
Major additions:
- AIS Disruptions layer (spoofing/jamming events)
- Cable Advisories layer (fault/maintenance markers)
- Repair Ships layer
- Country Labels layer
- Flight Delays toggle in layer panel
- Countries toggle in layer panel

Data storage fixes:
- setAisData now stores disruptions (was ignoring them)
- setCableActivity now stores advisories and repair ships

Also:
- Import COUNTRY_LABELS from config
- Add tooltips and click handlers for all new layers
- Temporarily disable ML worker (dependency unavailable)

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 07:21:33 +00:00
Claude c017b2c723 Add missing Irradiators and Spaceports toggles to layer panel
These layers existed in buildLayers() with mapLayers.X checks but had
no UI toggles, making them inaccessible. Added toggles for both in the
geopolitical variant layer panel.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:59:09 +00:00
Claude a8c86f96da Add APT Groups and Critical Minerals layers to DeckGLMap
- Import APT_GROUPS and CRITICAL_MINERALS from config
- Add createAPTGroupsLayer() with red markers and yellow outline for cyber threat actors
- Add createMineralsLayer() with color-coded markers by mineral type (Lithium, Cobalt, Rare Earths, Nickel)
- APT Groups always visible in geopolitical variant (no toggle)
- Add minerals toggle to layer panel for geopolitical variant
- Add tooltips and click handlers for both layers

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:43:13 +00:00
Claude 453ecd25d4 Add missing layers: irradiators, spaceports, ports, flight delays
- Add gamma irradiators layer (IAEA DIIF facilities)
- Add spaceports layer (launch sites)
- Add strategic ports layer (61 ports, shown with AIS)
- Add flight delays layer (FAA airport delays/ground stops)
- Fix setFlightDelays to store data properly
- Add tooltips and click handlers for all new layers

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:36:44 +00:00
Claude 5d147f9f43 Add clustering support for Tech HQs, Tech Events, and Protests
- Add clusterMarkers method for grouping nearby markers
- Create HTML overlay container for cluster badges
- Render Tech HQs with emoji icons and cluster count badges
- Render Tech Events with calendar icons and clustering
- Render Protests with severity icons and clustering
- Clusters expand on click to show popup with all items
- Single items show individual popup on click
- Clustering radius adjusts based on zoom level

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:19:52 +00:00
Claude 3278e4af42 Remove unused MapView import
https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:09:37 +00:00
Claude 4595b75356 Remove duplicate FOCUS dropdown and improve map legend
- Remove FOCUS region selector from header (keep only map's view selector)
- Redesign legend as compact horizontal bar at bottom center
- Add proper shapes: triangles for bases, squares for datacenters, hexagons for nuclear
- Reduce whitespace in legend layout

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 06:06:59 +00:00
Claude 88971ad298 Increase marker sizes for better visibility while keeping transparency
- Datacenters: size 10-14px, opacity ~55% (was 6-10px, ~30%)
- Bases: size 11-16px, opacity ~63%
- Nuclear: size 11-15px, opacity ~78%
- Better balance between visibility and layering

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 05:59:31 +00:00
Claude 69d4145d16 Reduce datacenter and base marker opacity for better layering
- Datacenter squares: size 6-10px (was 10-14), opacity ~30% (was ~63%)
- Planned datacenters: opacity ~20% for subtle indication
- Military bases: size 8-14px (was 12-20), opacity ~63% (was 100%)
- Improves map readability when multiple layers are overlaid

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 05:46:52 +00:00
Claude 8a04f748db Use IconLayer for distinct marker shapes
Replaced ScatterplotLayer circles with IconLayer for distinct shapes:
- Military bases: TRIANGULAR icons (color-coded by operator)
- Nuclear facilities: HEXAGONAL icons (yellow/orange)
- Datacenters: SQUARE icons (purple)
- Hotspots: Remain as circles (appropriate for threat indicators)

Each marker type now has a unique geometric shape for easy identification.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 05:24:19 +00:00
Claude e8a00e93ca Improve visual distinction for map markers
- Nuclear: Yellow/orange hollow RING with inner dot (radiation symbol look)
- Datacenters: Purple filled circles with light border
- Bases: Color-coded by operator (US-NATO blue, Russia red, China orange, etc.)
- Removed non-working TextLayer emoji approach

Each marker type now has a distinct visual style beyond just color.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-25 05:20:34 +00:00
Claude 24903e6aa1 Add emoji icons to datacenter and nuclear markers
Restored visual distinction for markers that had icons in the
original D3/SVG implementation:
- Datacenters: 🖥️ icon
- Nuclear facilities: ☢️ icon

Uses TextLayer overlaid on ScatterplotLayer to render emoji icons.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 15:27:52 +00:00
Claude fd1fb278f3 Add @deck.gl/mapbox dependency
Required for MapboxOverlay integration with MapLibre.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 14:10:53 +00:00
Claude 3cbe2cd021 Fix deck.gl/MapLibre sync using MapboxOverlay
Replaced separate Deck instance with MapboxOverlay which properly
integrates deck.gl into MapLibre's WebGL context. This fixes:
- Points moving when zooming/panning (view state sync)
- Coordinate offset issues (London appearing in France)
- Cursor issues (MapLibre now handles all interactions)

MapboxOverlay renders deck.gl layers directly into MapLibre's canvas,
eliminating all view state synchronization issues.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 14:10:25 +00:00
Claude bf6615cd1f Fix deck.gl/MapLibre view state sync causing offset
The circular sync between deck.gl and MapLibre was causing the
deck.gl layer to be offset from the base map. Fixed by:
- Making deck.gl the single source of truth (has controller enabled)
- Removing MapLibre->deck.gl sync that caused circular updates
- Adding initial sync on map load to ensure alignment

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 14:04:30 +00:00
Claude 92ea2f4093 Fix incorrect datacenter coordinates in US
The source dataset had placeholder coordinates clustering all US
datacenters around Kansas/Oklahoma. Fixed actual locations for:
- Mt Pleasant, Wisconsin (42.7°N, -87.9°W)
- Meta New Albany, Ohio (40.1°N, -82.8°W)
- OpenAI/Microsoft Atlanta (33.7°N, -84.4°W)
- Applied Digital Ellendale, ND (46.0°N, -98.5°W)
- Nebius New Jersey (40.1°N, -74.4°W)
- CoreWeave Denton, TX (33.2°N, -97.1°W)

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 13:59:58 +00:00
Claude 719f274e69 Remove auto-panning from map interactions
The map now stays fixed on the user's selected region when:
- Clicking items from lists (conflicts, bases, pipelines, etc.)
- Flash location animations
- New data flowing in

Popups now appear at the projected screen position of items
rather than panning the map to center on them. If an item is
off-screen, the popup still appears at its projected position.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 13:15:11 +00:00
Claude 0b7f9de52d Fix cursor style on deck.gl canvas
Add CSS to override deck.gl's default grab cursor on its canvas,
using the default pointer cursor and only grabbing when dragging.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 13:03:42 +00:00
Claude 66f562c79c Enable deck.gl controller for mouse panning/zooming
The deck.gl canvas was blocking mouse events from reaching MapLibre
because it had pointer-events: auto for picking but controller: false.
Enable deck.gl's controller and sync view state changes back to MapLibre.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 13:00:53 +00:00
Claude 6c24164855 Fix cursor style on deck.gl map to use default pointer
Override MapLibre GL's default grab cursor to use the standard
pointer cursor, only switching to grabbing when actively dragging.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:59:31 +00:00
Claude f2c7109e85 Fix dark overlay caused by timestamp element stretching
Root cause: The timestamp element had both classes 'map-timestamp' and
'deckgl-timestamp'. These classes set conflicting positioning:
- .map-timestamp: bottom: 8px; right: 10px;
- .deckgl-timestamp: top: 10px; left: 50%;

When all four positioning values (top/bottom/left/right) are set on an
absolutely positioned element without explicit width/height, it stretches
to fill the space between those edges - creating a 724x315px dark overlay.

Fixes:
- Remove 'map-timestamp' class from timestamp element in DeckGLMap.ts
- Add explicit bottom: auto; right: auto; width: auto; height: auto;
  to .deckgl-timestamp CSS to prevent any accidental stretching

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:56:02 +00:00
Claude 4fbb5aad21 Attempt fix for dark overlay with z-index and CSS transparency
Changes:
- Add explicit z-index to MapLibre (1) and deck.gl overlay (2)
- Add overflow: hidden to wrapper
- Reverted to letting deck.gl create its own canvas
- Added effects: [] to disable any default deck.gl effects
- Set canvas background to transparent after deck initializes
- Added comprehensive CSS rules to ensure all canvas elements
  and containers have transparent backgrounds

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:54:07 +00:00
Claude 6b304e18e1 Fix deck.gl canvas transparency by creating WebGL context with alpha
Instead of relying on deck.gl's default canvas/context creation,
manually create canvas and WebGL context with explicit alpha support:

- Create canvas element with transparent background style
- Get WebGL2/WebGL context with alpha: true, premultipliedAlpha: true
- Set gl.clearColor(0, 0, 0, 0) for transparent clear
- Pass pre-created canvas and gl context to Deck constructor

This ensures the WebGL context is properly configured for alpha
transparency from the start, allowing the MapLibre base map to
show through the deck.gl overlay.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:47:53 +00:00
Claude 1cd4677aaa Fix dark overlay with WebGL transparency settings
Two-pronged approach to fix the dark semi-transparent overlay:

1. Add MapLibre background layer:
   - Added 'background' layer with dark color (#0a0f0c) to MapLibre style
   - This ensures no transparent gaps while tiles are loading

2. Set WebGL clear color to transparent:
   - Added onWebGLInitialized callback to Deck initialization
   - Explicitly set gl.clearColor(0, 0, 0, 0) for transparent background
   - Enable proper alpha blending with gl.blendFunc

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:39:52 +00:00
Claude 85b16037ed Fix dark overlay issue in DeckGLMap
Root cause: CONFLICT_ZONES config had inconsistent coordinate formats.
Red Sea Crisis and South Lebanon entries were in [lat, lon] format
while other entries were in [lon, lat] format (GeoJSON standard).
This caused polygons to render in completely wrong locations.

Fixes:
- Fix Red Sea Crisis coords: [[12,42]...] → [[42,12]...] (swap to [lon,lat])
- Fix Red Sea Crisis center: [14,43] → [43,14]
- Fix South Lebanon coords: [[33.0,35.1]...] → [[35.1,33.0]...]
- Fix South Lebanon center: [33.2,35.4] → [35.4,33.2]
- Add explicit transparent background to #deckgl-overlay CSS

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:32:15 +00:00
Claude e379bd502a Fix popup system in DeckGLMap to use proper PopupData format
The MapPopup.show() method expects a PopupData object with:
- type: PopupType string identifying the popup renderer
- data: the actual data object to display
- x, y: click coordinates for popup positioning

Previously was passing raw objects which resulted in broken/missing popups.

Changes:
- Import PopupType from MapPopup
- Update handleClick to map layer IDs to PopupType and create proper PopupData
- Update all trigger methods (triggerConflictClick, triggerBaseClick, etc.)
  to create proper PopupData with centered x/y coordinates
- Add getContainerCenter() helper for programmatic popup positioning

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:25:49 +00:00
Claude eeea18062e Fix coordinate bugs and add missing tooltips in DeckGLMap
- Fix coordinate swap bugs in createCablesLayer, createPipelinesLayer,
  and createConflictZonesLayer - data is already [lon, lat] format
- Fix coordinate order in triggerConflictClick, triggerPipelineClick,
  and triggerCableClick to pass (lat, lon) to setCenter correctly
- Fix handleClick to not show popup for hotspots (they have their own handler)
- Add missing tooltips for all layer types (cables, pipelines, conflicts,
  natural events, weather, outages, AIS density, military flights,
  waterways, economic centers, tech HQs, accelerators, cloud regions,
  tech events)

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 12:06:17 +00:00
Claude b50c6a445a Add deck.gl WebGL visualization for desktop
Implement Palantir-like interactive map experience using deck.gl with
MapLibre GL as the base map. Key changes:

- Add deck.gl (@deck.gl/core, @deck.gl/layers, @deck.gl/geo-layers) and
  maplibre-gl dependencies for GPU-accelerated rendering
- Create DeckGLMap component with WebGL layers for:
  - Undersea cables and pipelines (PathLayer)
  - Military bases, nuclear facilities, datacenters (ScatterplotLayer)
  - Conflict zones (GeoJsonLayer with polygons)
  - Hotspots, earthquakes, weather, outages, protests
  - AIS density, military vessels and flights
  - Strategic waterways, economic centers
  - Tech variant: startup hubs, tech HQs, accelerators, cloud regions
- Create MapContainer wrapper that conditionally renders:
  - DeckGLMap (WebGL) on desktop with WebGL support
  - Existing D3/SVG MapComponent on mobile for graceful degradation
- Add dark theme CSS styles for deck.gl controls, legend, layer toggles
- Import maplibre-gl CSS in main.ts

Desktop users now get smooth 60fps interactions with large datasets
while mobile users retain the optimized SVG experience.

https://claude.ai/code/session_01GTanC7R6aSQNsnijqJRUFz
2026-01-23 11:48:20 +00:00
Claude a430337476 Improve mobile usability with touch-optimized defaults and targets
- Add mobile-specific default layers (hotspots, conflicts, outages only)
  to reduce visual clutter on small screens
- Default to MENA view on mobile devices for better regional focus
- Add 44x44px touch targets using ::before pseudo-elements for all
  map markers (hotspots, bases, earthquakes, etc.)
- Update MapPopup to center horizontally on mobile for better visibility
- Add comprehensive mobile CSS with responsive controls and buttons
- Update mobile warning modal messaging to reflect new mobile experience
- Add extra-small screen (480px) specific layout adjustments

Fixes touch target accuracy issues where clicking one marker would
open a different marker's popup.
2026-01-11 08:33:24 +00:00
Claude 3236159fc8 Consolidate military layers into single 'Military' toggle
User feedback: "it's already crowded" - combine Mil Aircraft and Mil Vessels
into a single layer toggle for cleaner UI.
2026-01-11 03:38:46 +00:00
Claude d4c7888549 Add military flight and vessel tracking feature
- Add types for military flights, vessels, and clusters (MilitaryFlight,
  MilitaryVessel, MilitaryFlightCluster, MilitaryVesselCluster)
- Create military-flights.ts service with OpenSky Network API integration
  for tracking military aircraft (fighters, bombers, tankers, AWACS, etc.)
- Create military-vessels.ts service leveraging AIS stream to detect
  military/government vessels (carriers, destroyers, submarines, etc.)
- Add comprehensive military callsign patterns and aircraft type database
  in config/military.ts (US, NATO, Russia, China callsigns)
- Add known naval vessel database with carrier strike groups
- Implement military hotspot detection for strategic regions (Taiwan Strait,
  South China Sea, Persian Gulf, Black Sea, etc.)
- Add map rendering for military flights and vessels with operator-specific
  colors, aircraft type icons, and flight track trails
- Add CSS styles for military markers with pulsing animations for
  interesting activity
- Integrate into App.ts with 2-minute refresh for flights, 5-minute for vessels
- Add OpenSky and ADS-B Exchange API proxies in vite.config.ts
2026-01-11 03:31:17 +00:00
Claude a694d57c17 Add virtual scrolling with windowed rendering for news panels
Implements efficient rendering for large news lists by only rendering
visible chunks of items. This reduces DOM nodes from 200+ to ~24
(3 chunks × 8 items) for panels with many articles.

Implementation:
- Add VirtualList component with two strategies:
  - VirtualList: Fixed-height items with DOM recycling
  - WindowedList: Variable-height items with chunk-based rendering
- Update NewsPanel to use WindowedList for lists > 15 items
- Extract renderClusterHtml() method for per-item rendering
- Add CSS containment hints for scroll performance

Performance benefits:
- Reduced initial render time (fewer DOM nodes)
- Smoother scrolling (CSS contain: strict)
- Lower memory usage (chunks rendered on-demand)
- Event handlers bound only to visible chunks
2026-01-10 15:40:06 +00:00
Claude 715d909667 Add real-time activity indicators with "new" badges
Adds visual feedback when panels receive new data:

- Panel header badges showing count of new items (e.g., "3 new")
- Pulsing animation to draw attention to updated panels
- Green "NEW" tag on recently added news items (fades after 2 min)
- Highlight glow effect on new items (first 30 seconds)
- Auto-clear badges when user scrolls or clicks panel

Implementation:
- Add ActivityTracker service to track seen items per panel
- Extend Panel base class with badge element and setNewBadge()
- Update NewsPanel to integrate with activity tracking
- Add CSS animations for badges, highlights, and panel glow
2026-01-10 15:14:39 +00:00
Claude a24e3f5059 Move clustering and correlation analysis to Web Worker
Moves O(n²) Jaccard clustering and correlation signal detection off the
main thread to prevent UI jank during data refresh cycles. The worker
maintains its own state for correlation analysis (previousSnapshot,
recentSignalKeys) and returns serialized results back to the main thread.

Changes:
- Add src/workers/analysis.worker.ts with self-contained clustering and
  correlation logic
- Add src/services/analysis-worker.ts as typed async interface to worker
- Update App.ts to use worker for clusterNews and analyzeCorrelations
- Worker is lazily initialized on first use
2026-01-10 15:07:29 +00:00
Claude 592d1fc54e Add mobile warning popup for desktop-optimized experience
Show informational popup on mobile devices notifying users that the
dashboard is optimized for desktop viewing. Users can dismiss the
popup and optionally choose not to see it again (persisted to
localStorage). Detection uses both screen width and pointer type.
2026-01-10 14:03:49 +00:00