Back to Worldmonitor

Panel System Documentation

docs/Docs_To_Review/PANELS.md

2.5.2344.3 KB
Original Source

Panel System Documentation

World Monitor — Config-driven panel architecture powering three site variants.

Source of truth: src/config/panels.ts · Panel base class: src/components/Panel.ts · App wiring: src/App.ts


Table of Contents

  1. Overview
  2. Panel Configuration
  3. Panel Base Class
  4. Full Variant Panels
  5. Tech Variant Panels
  6. Finance Variant Panels
  7. Variant Comparison Matrix
  8. Map Layers
  9. Panel Persistence
  10. Panel Lifecycle
  11. Adding a New Panel
  12. Diagrams

1. Overview

World Monitor uses a config-driven panel system where every dashboard tile — from live news feeds to AI insights to market data — is declared as a PanelConfig entry inside a variant-specific configuration object. The system is designed around three principles:

  1. Variant isolation — Each site variant (full, tech, finance) declares its own panel set with variant-appropriate display names and priorities. The build-time environment variable VITE_VARIANT selects which set is exported.
  2. User customization — Users can toggle panel visibility, reorder panels via drag-and-drop, and resize panels via a drag handle. All preferences persist to localStorage.
  3. No framework — Panels are vanilla TypeScript classes extending a shared Panel base class. There is no React/Vue/Angular; DOM construction and updates are imperative.

Variant Domains

VariantDomainFocusPanel Count
fullworldmonitor.ioGeopolitical intelligence, OSINT, defense37
techtech.worldmonitor.ioTechnology, AI/ML, startups, VC34
financefinance.worldmonitor.ioMarkets, trading, macro, commodities29

Key Files

FilePurpose
src/config/panels.tsCentral panel & map-layer definitions for all three variants
src/config/variants/base.tsShared VariantConfig interface, STORAGE_KEYS, MONITOR_COLORS, API URLs
src/config/variants/full.tsFull variant config with panels, layers, and feeds
src/config/variants/tech.tsTech variant config
src/config/variants/finance.tsFinance variant config
src/components/Panel.tsBase Panel class (440 lines) — DOM, resize, badges, lifecycle
src/components/NewsPanel.tsNewsPanel extending Panel — RSS-driven news tiles
src/App.tsApplication shell — panel instantiation, data loading, settings modal
src/types/index.tsPanelConfig, MapLayers, AppState type definitions

2. Panel Configuration

2.1 PanelConfig Type

Every panel is described by a PanelConfig object defined in src/types/index.ts:

typescript
export interface PanelConfig {
  name: string;       // Display name shown in panel header and settings modal
  enabled: boolean;   // Whether the panel is visible (toggled by user)
  priority?: number;  // 1 = core (shown early), 2 = supplementary (shown later)
}
  • name — Variant-specific. The same panel ID (e.g. map) can have different display names across variants: "Global Map" (full), "Global Tech Map" (tech), "Global Markets Map" (finance).
  • enabled — Defaults to true for all shipped panels. Users can disable panels via the Settings modal; the toggled state is persisted.
  • priority — Informational grouping. Priority 1 panels are considered core to the variant's mission; priority 2 panels are supplementary. This does not affect rendering order—order is determined by declaration order in the config object and user drag-and-drop overrides.

2.2 Variant Selection

Panel sets are selected at build time via the SITE_VARIANT constant (derived from VITE_VARIANT env var):

typescript
// src/config/panels.ts — variant-aware exports
export const DEFAULT_PANELS =
  SITE_VARIANT === 'tech'    ? TECH_PANELS :
  SITE_VARIANT === 'finance' ? FINANCE_PANELS :
                                FULL_PANELS;

export const DEFAULT_MAP_LAYERS =
  SITE_VARIANT === 'tech'    ? TECH_MAP_LAYERS :
  SITE_VARIANT === 'finance' ? FINANCE_MAP_LAYERS :
                                FULL_MAP_LAYERS;

export const MOBILE_DEFAULT_MAP_LAYERS =
  SITE_VARIANT === 'tech'    ? TECH_MOBILE_MAP_LAYERS :
  SITE_VARIANT === 'finance' ? FINANCE_MOBILE_MAP_LAYERS :
                                FULL_MOBILE_MAP_LAYERS;

Vite tree-shakes the unused variant objects from the production bundle.

2.3 VariantConfig Interface

Each variant file exports a full VariantConfig object:

typescript
// src/config/variants/base.ts
export interface VariantConfig {
  name: string;                              // 'full' | 'tech' | 'finance'
  description: string;                       // Human-readable variant description
  panels: Record<string, PanelConfig>;       // Panel ID → config
  mapLayers: MapLayers;                      // Desktop default layer toggles
  mobileMapLayers: MapLayers;                // Mobile default layer toggles
}

The variant config also defines its own FEEDS object (Record<string, Feed[]>) mapping panel IDs to their RSS feed sources. Feeds that don't have a registered panel ID result in auto-generated NewsPanel instances (see Panel Lifecycle).

2.4 Storage Keys

All persistence keys are centralized in STORAGE_KEYS:

typescript
// src/config/variants/base.ts (also re-exported from src/config/panels.ts)
export const STORAGE_KEYS = {
  panels:        'worldmonitor-panels',          // Panel visibility toggles
  monitors:      'worldmonitor-monitors',         // Monitor keyword configs
  mapLayers:     'worldmonitor-layers',           // Map layer toggles
  disabledFeeds: 'worldmonitor-disabled-feeds',   // Per-source feed disabling
} as const;

Additional keys used outside STORAGE_KEYS:

KeyPurposeManaged By
worldmonitor-panel-spansPanel height/span sizes (1–4)Panel.ts
panel-orderDrag-and-drop panel orderingApp.ts
worldmonitor-variantLast-active variant (triggers reset on change)App.ts
worldmonitor-panel-order-v1.9Migration flag for v1.9 panel layoutApp.ts

2.5 Monitor Colors

The monitor palette provides 10 fixed category colors used for user-defined keyword monitors:

typescript
export const MONITOR_COLORS = [
  '#44ff88', '#ff8844', '#4488ff', '#ff44ff', '#ffff44',
  '#ff4444', '#44ffff', '#88ff44', '#ff88ff', '#88ffff',
];

These colors are theme-independent and persist alongside monitor definitions in localStorage.


3. Panel Base Class

All panels extend the Panel class defined in src/components/Panel.ts (440 lines). This base class provides the shared DOM structure, interaction patterns, and lifecycle methods.

3.1 Constructor Options

typescript
export interface PanelOptions {
  id: string;             // Unique panel identifier (matches config key)
  title: string;          // Display name rendered in header
  showCount?: boolean;    // Show item count badge in header
  className?: string;     // Additional CSS class on root element
  trackActivity?: boolean; // Enable "new items" badge (default: true)
  infoTooltip?: string;   // HTML content for methodology tooltip (ℹ️ button)
}

3.2 DOM Structure

Every panel renders the following DOM tree:

div.panel[data-panel="{id}"]
├── div.panel-header
│   ├── div.panel-header-left
│   │   ├── span.panel-title          ← Display name
│   │   ├── div.panel-info-wrapper    ← (optional) ℹ️ tooltip
│   │   └── span.panel-new-badge      ← (optional) "N new" badge
│   ├── span.panel-data-badge         ← live/cached/unavailable indicator
│   └── span.panel-count              ← (optional) item count
├── div.panel-content#${id}Content    ← Main content area
└── div.panel-resize-handle           ← Drag-to-resize handle

3.3 Features

FeatureDescription
Drag-to-resizeBottom handle supports mouse + touch. Height maps to span classes (span-1 through span-4). Double-click resets to default.
CollapsibleClick header to toggle hidden class on content (handled by CSS).
Loading stateshowLoading(message?) renders a radar sweep animation with text. Shown by default on construction.
Error stateshowError(message?) renders error text. showConfigError(message) adds a "Open Settings" button (Tauri desktop).
Data badgesetDataBadge(state, detail?) shows live, cached, or unavailable with optional detail text.
New badgesetNewBadge(count, pulse?) shows a blue dot with count in the header. Pulses for important updates.
Count badgesetCount(n) updates the numeric count in the header (when showCount is enabled).
Info tooltipHover/click on ℹ️ icon shows methodology explanation. Dismissed on outside click.
Throttled contentsetContentThrottled(html) buffers DOM writes to one per animation frame (PERF-009).
Header error statesetErrorState(hasError, tooltip?) toggles a red header accent for degraded panels.

3.4 Span System (Sizing)

Panel height is quantized into 4 span levels:

SpanMin HeightCSS ClassDescription
1defaultspan-1Standard single-row height
2250pxspan-2Medium — 50px drag triggers
3350pxspan-3Large — 150px drag triggers
4500pxspan-4Extra-large — 300px drag triggers

Span values are persisted per-panel in the worldmonitor-panel-spans localStorage key as a JSON object { [panelId]: spanNumber }.

3.5 Public Methods

MethodSignatureDescription
getElement()(): HTMLElementReturns the root DOM element
show()(): voidRemove hidden class
hide()(): voidAdd hidden class
toggle(visible)(boolean): voidShow or hide
showLoading(msg?)(string?): voidRender loading spinner
showError(msg?)(string?): voidRender error message
showConfigError(msg)(string): voidRender config error with settings button
setContent(html)(string): voidSet content innerHTML directly
setContentThrottled(html)(string): voidBuffered content update (rAF)
setCount(n)(number): voidUpdate count badge
setNewBadge(count, pulse?)(number, boolean?): voidUpdate new-items badge
clearNewBadge()(): voidHide new badge
setDataBadge(state, detail?)(string, string?): voidUpdate data freshness badge
clearDataBadge()(): voidHide data badge
setErrorState(err, tip?)(boolean, string?): voidToggle header error styling
getId()(): stringReturn panel ID
resetHeight()(): voidClear saved span, remove span classes
destroy()(): voidRemove all event listeners

4. Full Variant Panels (worldmonitor.io)

The full (geopolitical) variant ships 37 panels focused on OSINT, defense intelligence, geopolitical risk, and global situational awareness.

#Panel IDDisplay NamePriorityComponent ClassData Source
1mapGlobal Map1MapContainerMapLibre + deck.gl
2live-newsLive News1LiveNewsPanelMulti-source RSS aggregation
3live-webcamsLive Webcams1LiveWebcamsPanelCurated webcam streams
4insightsAI Insights1InsightsPanelGroq/OpenRouter LLM summarization
5strategic-postureAI Strategic Posture1StrategicPosturePanelTheater posture API
6ciiCountry Instability1CIIPanelComposite instability index
7strategic-riskStrategic Risk Overview1StrategicRiskPanelRisk scores API
8intelIntel Feed1NewsPanelIntelligence RSS feeds
9gdelt-intelLive Intelligence1GdeltIntelPanelGDELT event database
10cascadeInfrastructure Cascade1CascadePanelMulti-domain cascade analysis
11politicsWorld News1NewsPanelPolitical RSS feeds
12middleeastMiddle East1NewsPanelRegional RSS feeds
13africaAfrica1NewsPanelRegional RSS feeds
14latamLatin America1NewsPanelRegional RSS feeds
15asiaAsia-Pacific1NewsPanelRegional RSS feeds
16energyEnergy & Resources1NewsPanelEnergy RSS feeds
17govGovernment1NewsPanelGovernment RSS feeds
18thinktanksThink Tanks1NewsPanelThink tank RSS feeds
19polymarketPredictions1PredictionPanelPolymarket API
20commoditiesCommodities1CommoditiesPanelYahoo Finance / commodity APIs
21marketsMarkets1MarketPanelFinnhub / Yahoo Finance
22economicEconomic Indicators1EconomicPanelFRED API
23financeFinancial1NewsPanelFinancial RSS feeds
24techTechnology2NewsPanelTechnology RSS feeds
25cryptoCrypto2CryptoPanelCoinGecko API
26heatmapSector Heatmap2HeatmapPanelMarket sector data
27aiAI/ML2NewsPanelAI/ML RSS feeds
28layoffsLayoffs Tracker2NewsPanelLayoffs RSS feeds
29monitorsMy Monitors2MonitorPanelUser-defined keyword monitors
30satellite-firesFires2SatelliteFiresPanelNASA FIRMS API
31macro-signalsMarket Radar2MacroSignalsPanelMacro signals API
32etf-flowsBTC ETF Tracker2ETFFlowsPanelETF flows API
33stablecoinsStablecoins2StablecoinPanelStablecoin markets API
34ucdp-eventsUCDP Conflict Events2UcdpEventsPanelUCDP API
35displacementUNHCR Displacement2DisplacementPanelUNHCR population API
36climateClimate Anomalies2ClimateAnomalyPanelClimate anomalies API
37population-exposurePopulation Exposure2PopulationExposurePanelWorldPop exposure API

Full variant exclusive panels: strategic-posture, cii, strategic-risk, gdelt-intel, cascade, satellite-fires, ucdp-events, displacement, climate, population-exposure, and the regional panels (middleeast, africa, latam, asia).


5. Tech Variant Panels (tech.worldmonitor.io)

The tech variant ships 34 panels focused on technology news, AI/ML, startup ecosystems, and developer tooling.

#Panel IDDisplay NamePriorityComponent ClassData Source
1mapGlobal Tech Map1MapContainerMapLibre + deck.gl
2live-newsTech Headlines1LiveNewsPanelTech RSS aggregation
3live-webcamsLive Webcams2LiveWebcamsPanelCurated webcam streams
4insightsAI Insights1InsightsPanelGroq/OpenRouter LLM summarization
5aiAI/ML News1NewsPanelAI/ML RSS feeds
6techTechnology1NewsPanelTechnology RSS feeds
7startupsStartups & VC1NewsPanelStartup RSS feeds
8vcblogsVC Insights & Essays1NewsPanelVC blog RSS feeds
9regionalStartupsGlobal Startup News1NewsPanelRegional startup RSS feeds
10unicornsUnicorn Tracker1NewsPanelUnicorn RSS feeds
11acceleratorsAccelerators & Demo Days1NewsPanelAccelerator RSS feeds
12securityCybersecurity1NewsPanelCybersecurity RSS feeds
13policyAI Policy & Regulation1NewsPanelAI policy RSS feeds
14regulationAI Regulation Dashboard1RegulationPanelRegulation data
15layoffsLayoffs Tracker1NewsPanelLayoffs RSS feeds
16marketsTech Stocks2MarketPanelFinnhub / Yahoo Finance
17financeFinancial News2NewsPanelFinancial RSS feeds
18cryptoCrypto2CryptoPanelCoinGecko API
19hardwareSemiconductors & Hardware2NewsPanelHardware RSS feeds
20cloudCloud & Infrastructure2NewsPanelCloud RSS feeds
21devDeveloper Community2NewsPanelDeveloper RSS feeds
22githubGitHub Trending1NewsPanelGitHub trending API
23ipoIPO & SPAC2NewsPanelIPO RSS feeds
24polymarketTech Predictions2PredictionPanelPolymarket API
25fundingFunding & VC1NewsPanelFunding RSS feeds
26producthuntProduct Hunt1NewsPanelProduct Hunt RSS
27eventsTech Events1TechEventsPanelTech events API
28service-statusService Status2ServiceStatusPanelService status API
29economicEconomic Indicators2EconomicPanelFRED API
30tech-readinessTech Readiness Index1TechReadinessPanelWorld Bank API
31macro-signalsMarket Radar2MacroSignalsPanelMacro signals API
32etf-flowsBTC ETF Tracker2ETFFlowsPanelETF flows API
33stablecoinsStablecoins2StablecoinPanelStablecoin markets API
34monitorsMy Monitors2MonitorPanelUser-defined keyword monitors

Tech variant exclusive panels: startups, vcblogs, regionalStartups, unicorns, accelerators, security, policy, regulation, hardware, cloud, dev, github, funding, producthunt, events, service-status, tech-readiness.


6. Finance Variant Panels (finance.worldmonitor.io)

The finance variant ships 29 panels focused on markets, trading, macro indicators, and financial data.

#Panel IDDisplay NamePriorityComponent ClassData Source
1mapGlobal Markets Map1MapContainerMapLibre + deck.gl
2live-newsMarket Headlines1LiveNewsPanelFinancial RSS aggregation
3live-webcamsLive Webcams2LiveWebcamsPanelCurated webcam streams
4insightsAI Market Insights1InsightsPanelGroq/OpenRouter LLM summarization
5marketsLive Markets1MarketPanelFinnhub / Yahoo Finance
6markets-newsMarkets News2NewsPanelMarkets RSS feeds
7forexForex & Currencies1NewsPanelForex RSS feeds
8bondsFixed Income1NewsPanelFixed income RSS feeds
9commoditiesCommodities & Futures1CommoditiesPanelYahoo Finance / commodity APIs
10commodities-newsCommodities News2NewsPanelCommodities RSS feeds
11cryptoCrypto & Digital Assets1CryptoPanelCoinGecko API
12crypto-newsCrypto News2NewsPanelCrypto RSS feeds
13centralbanksCentral Bank Watch1NewsPanelCentral bank RSS feeds
14economicEconomic Data1EconomicPanelFRED API
15economic-newsEconomic News2NewsPanelEconomic RSS feeds
16ipoIPOs, Earnings & M&A1NewsPanelIPO/M&A RSS feeds
17heatmapSector Heatmap1HeatmapPanelMarket sector data
18macro-signalsMarket Radar1MacroSignalsPanelMacro signals API
19derivativesDerivatives & Options2NewsPanelDerivatives RSS feeds
20fintechFintech & Trading Tech2NewsPanelFintech RSS feeds
21regulationFinancial Regulation2NewsPanelRegulation RSS feeds
22institutionalHedge Funds & PE2NewsPanelInstitutional RSS feeds
23analysisMarket Analysis2NewsPanelAnalysis RSS feeds
24etf-flowsBTC ETF Tracker2ETFFlowsPanelETF flows API
25stablecoinsStablecoins2StablecoinPanelStablecoin markets API
26gcc-investmentsGCC Investments2InvestmentsPanelGCC investment data
27gccNewsGCC Business News2NewsPanelGCC news RSS feeds
28polymarketPredictions2PredictionPanelPolymarket API
29monitorsMy Monitors2MonitorPanelUser-defined keyword monitors

Finance variant exclusive panels: markets-news, forex, bonds, commodities-news, crypto-news, centralbanks, economic-news, derivatives, fintech, institutional, analysis, gcc-investments, gccNews.


7. Variant Comparison Matrix

This matrix shows which panel IDs are available in each variant. Panels that appear in multiple variants may have different display names (see individual variant sections above).

Panel IDFullTechFinanceNotes
mapDifferent names per variant
live-newsDifferent names per variant
live-webcamsPriority 1 in full, priority 2 in tech/finance
insightsDifferent names per variant
markets"Markets" / "Tech Stocks" / "Live Markets"
economic"Economic Indicators" / "Economic Indicators" / "Economic Data"
crypto"Crypto" / "Crypto" / "Crypto & Digital Assets"
polymarket"Predictions" / "Tech Predictions" / "Predictions"
monitorsIdentical across all variants
macro-signalsP2 in full/tech, P1 in finance
etf-flowsP2 in all variants
stablecoinsP2 in all variants
layoffsP2 in full, P1 in tech
finance"Financial" / "Financial News"
techP2 in full, P1 in tech
ai"AI/ML" / "AI/ML News"
heatmapP2 in full, P1 in finance
commodities"Commodities" / "Commodities & Futures"
ipo"IPO & SPAC" / "IPOs, Earnings & M&A"
regulation"AI Regulation Dashboard" / "Financial Regulation"
politicsFull only — World News
middleeastFull only
africaFull only
latamFull only
asiaFull only
energyFull only
govFull only
thinktanksFull only
intelFull only
gdelt-intelFull only
cascadeFull only
strategic-postureFull only
ciiFull only
strategic-riskFull only
satellite-firesFull only
ucdp-eventsFull only
displacementFull only
climateFull only
population-exposureFull only
startupsTech only
vcblogsTech only
regionalStartupsTech only
unicornsTech only
acceleratorsTech only
securityTech only
policyTech only
hardwareTech only
cloudTech only
devTech only
githubTech only
fundingTech only
producthuntTech only
eventsTech only
service-statusTech only
tech-readinessTech only
markets-newsFinance only
forexFinance only
bondsFinance only
commodities-newsFinance only
crypto-newsFinance only
centralbanksFinance only
economic-newsFinance only
derivativesFinance only
fintechFinance only
institutionalFinance only
analysisFinance only
gcc-investmentsFinance only
gccNewsFinance only

8. Map Layers

The map is a specialized panel (map ID) rendered in its own #mapSection container rather than the #panelsGrid. Each variant defines both desktop and mobile default layer sets.

8.1 MapLayers Interface

typescript
// src/types/index.ts
export interface MapLayers {
  // Geopolitical layers
  conflicts: boolean;     bases: boolean;         hotspots: boolean;
  nuclear: boolean;       sanctions: boolean;     military: boolean;

  // Infrastructure layers
  cables: boolean;        pipelines: boolean;     waterways: boolean;
  outages: boolean;       datacenters: boolean;   spaceports: boolean;

  // Threat layers
  cyberThreats: boolean;  protests: boolean;      fires: boolean;

  // Environmental layers
  weather: boolean;       economic: boolean;      natural: boolean;
  minerals: boolean;      irradiators: boolean;

  // Transport layers
  ais: boolean;           flights: boolean;

  // Data source layers
  ucdpEvents: boolean;    displacement: boolean;  climate: boolean;

  // Tech variant layers
  startupHubs: boolean;   cloudRegions: boolean;  accelerators: boolean;
  techHQs: boolean;       techEvents: boolean;

  // Finance variant layers
  stockExchanges: boolean; financialCenters: boolean; centralBanks: boolean;
  commodityHubs: boolean;  gulfInvestments: boolean;
}

8.2 Full Variant Layers

LayerDesktop DefaultMobile DefaultDescription
conflicts✅ ON✅ ONArmed conflict zones
bases✅ ONOFFMilitary bases
hotspots✅ ON✅ ONGeopolitical hotspots
nuclear✅ ONOFFNuclear facilities
sanctions✅ ON✅ ONSanctioned entities/regions
weather✅ ON✅ ONWeather alerts
economic✅ ONOFFEconomic indicators overlay
waterways✅ ONOFFStrategic waterways
outages✅ ON✅ ONInternet/infrastructure outages
military✅ ONOFFMilitary deployments
natural✅ ON✅ ONNatural disasters
cablesOFFOFFUndersea fiber cables
pipelinesOFFOFFOil/gas pipelines
aisOFFOFFAIS vessel tracking
irradiatorsOFFOFFGamma irradiators
cyberThreatsOFFOFFCyber threat indicators
datacentersOFFOFFAI data centers
protestsOFFOFFSocial unrest events
flightsOFFOFFMilitary flights
spaceportsOFFOFFSpace launch facilities
mineralsOFFOFFCritical mineral deposits
firesOFFOFFActive fires (FIRMS)
ucdpEventsOFFOFFUCDP conflict data
displacementOFFOFFUNHCR displacement data
climateOFFOFFClimate anomalies
All tech layersOFFOFF
All finance layersOFFOFF

Desktop default ON: 11 layers · Mobile default ON: 5 layers

8.3 Tech Variant Layers

LayerDesktop DefaultMobile DefaultDescription
cables✅ ONOFFUndersea fiber cables
weather✅ ONOFFWeather alerts
economic✅ ONOFFEconomic indicators overlay
outages✅ ON✅ ONInternet outages
datacenters✅ ON✅ ONAI data centers
natural✅ ON✅ ONNatural disasters
startupHubs✅ ON✅ ONStartup ecosystem hubs
cloudRegions✅ ONOFFCloud provider regions
techHQs✅ ONOFFMajor tech company HQs
techEvents✅ ON✅ ONTech conferences and events
All geopoliticalOFFOFF
All militaryOFFOFF
All finance layersOFFOFF

Desktop default ON: 10 layers · Mobile default ON: 5 layers

8.4 Finance Variant Layers

LayerDesktop DefaultMobile DefaultDescription
cables✅ ONOFFUndersea fiber cables
pipelines✅ ONOFFOil/gas pipelines
sanctions✅ ONOFFSanctioned entities
weather✅ ONOFFWeather alerts
economic✅ ON✅ ONEconomic indicators overlay
waterways✅ ONOFFStrategic waterways
outages✅ ON✅ ONInternet outages
natural✅ ON✅ ONNatural disasters
stockExchanges✅ ON✅ ONGlobal stock exchanges
financialCenters✅ ONOFFFinancial centers
centralBanks✅ ON✅ ONCentral bank locations
All geopoliticalOFFOFF
All militaryOFFOFF
All tech layersOFFOFF

Desktop default ON: 11 layers · Mobile default ON: 5 layers

8.5 Mobile Layer Strategy

Mobile defaults enable fewer layers to preserve performance on constrained devices. The pattern:

  • Each variant enables ~5 layers on mobile (vs 10–11 on desktop)
  • Environmental critical layers (natural, outages) are always on
  • Variant-signature layers stay on (e.g. startupHubs for tech, stockExchanges for finance)
  • Heavy overlay layers (cables, pipelines, weather) are off by default on mobile

9. Panel Persistence

All user preferences survive page reload via localStorage. The following table enumerates every persisted setting:

9.1 Persistence Map

SettinglocalStorage KeyFormatDefault SourceSurvives Reload
Panel visibilityworldmonitor-panelsRecord<string, PanelConfig> JSONDEFAULT_PANELS
Panel orderingpanel-orderstring[] JSONConfig declaration order
Panel sizes/spansworldmonitor-panel-spansRecord<string, number> JSONAll span-1
Map layer togglesworldmonitor-layersMapLayers JSONDEFAULT_MAP_LAYERS
Monitor keywordsworldmonitor-monitorsMonitor[] JSON[]
Disabled sourcesworldmonitor-disabled-feedsstring[] JSON[]
Active variantworldmonitor-variantPlain stringSITE_VARIANT
Banner dismissalbanner-dismissed (sessionStorage)Timestamp stringSession only

9.2 Variant Change Reset

When the stored variant (worldmonitor-variant) differs from the current SITE_VARIANT, the App constructor performs a full reset:

typescript
if (storedVariant !== currentVariant) {
  localStorage.setItem('worldmonitor-variant', currentVariant);
  localStorage.removeItem(STORAGE_KEYS.mapLayers);
  localStorage.removeItem(STORAGE_KEYS.panels);
  localStorage.removeItem(this.PANEL_ORDER_KEY);
  localStorage.removeItem(this.PANEL_SPANS_KEY);
  this.mapLayers = { ...defaultLayers };
  this.panelSettings = { ...DEFAULT_PANELS };
}

This ensures users switching between variant domains (e.g. from worldmonitor.io to tech.worldmonitor.io) get a clean default experience for the new variant.

9.3 Full Reset

Users can clear all panel preferences by clearing localStorage for the domain. There is no in-app "reset to defaults" button — the variant change mechanism serves as an implicit reset.

9.4 Merge Strategy for New Panels

When the application adds new panels (in a code update), the saved panel order is merged with the current defaults:

  1. Start with the saved order (from panel-order key)
  2. Remove any panels that no longer exist in DEFAULT_PANELS
  3. Find panels present in DEFAULT_PANELS but missing from saved order
  4. Insert missing panels after the politics panel position (or at position 0)
  5. Always place monitors panel last
  6. Always place live-news panel first (CSS grid constraint — it spans 2 columns)
  7. Always place live-webcams immediately after live-news

10. Panel Lifecycle

10.1 Initialization Flow

The panel lifecycle begins in App.ts constructor and flows through these phases:

  1. Config LoadingpanelSettings loaded from localStorage (or DEFAULT_PANELS on first visit / variant change)
  2. DOM Scaffoldingrender() builds the page shell including #panelsGrid container and settings modal
  3. Panel InstantiationcreatePanels() constructs all panel class instances and registers them in this.panels and this.newsPanels
  4. Grid Insertion — Panels are appended to #panelsGrid in the resolved order (saved + merge)
  5. Drag-and-drop Binding — Each panel element gets makeDraggable() handlers for reordering
  6. Visibility ApplicationapplyPanelSettings() calls panel.toggle(config.enabled) for every panel
  7. Data LoadingloadAllData() fires parallel API calls; each handler updates its panel via setContent() / setContentThrottled()
  8. Refresh Scheduling — Periodic refresh timers are set up per data source (2–60 min intervals)

10.2 Panel Type Hierarchy

Panel (base)
├── NewsPanel          ← RSS-driven news feeds (most panels)
├── MarketPanel        ← Live market tickers
├── HeatmapPanel       ← Sector heatmap grid
├── CryptoPanel        ← Cryptocurrency prices
├── CommoditiesPanel   ← Commodity prices
├── PredictionPanel    ← Polymarket predictions
├── EconomicPanel      ← FRED economic indicators
├── MonitorPanel       ← User keyword monitors
├── CIIPanel           ← Country Instability Index
├── CascadePanel       ← Infrastructure cascade analysis
├── GdeltIntelPanel    ← GDELT intelligence feed
├── SatelliteFiresPanel← NASA FIRMS fire data
├── StrategicRiskPanel ← Risk score overview
├── StrategicPosturePanel ← Theater posture
├── UcdpEventsPanel    ← UCDP conflict events
├── DisplacementPanel  ← UNHCR displacement
├── ClimateAnomalyPanel← Climate anomaly data
├── PopulationExposurePanel ← Population exposure
├── InvestmentsPanel   ← GCC investment tracker
├── LiveNewsPanel      ← Breaking news aggregation
├── LiveWebcamsPanel   ← Multi-stream webcam view
├── TechEventsPanel    ← Technology events
├── ServiceStatusPanel ← Service uptime monitor
├── TechReadinessPanel ← World Bank tech index
├── MacroSignalsPanel  ← Macro market signals
├── ETFFlowsPanel      ← BTC ETF flow tracker
├── StablecoinPanel    ← Stablecoin market data
├── InsightsPanel      ← AI-generated insights
├── RegulationPanel    ← Regulation dashboard
├── RuntimeConfigPanel ← Desktop runtime config (Tauri only)
├── OrefSirensPanel    ← Israel alert sirens (full only)
└── StatusPanel        ← System status (not in panel grid)

10.3 Auto-generated News Panels

App.ts iterates over all keys in the variant's FEEDS object. For any feed category that does not already have a manually instantiated NewsPanel, a new NewsPanel is created automatically:

typescript
for (const key of Object.keys(FEEDS)) {
  if (this.newsPanels[key]) continue;                    // Skip if already created
  if (!Array.isArray((FEEDS as Record<string, unknown>)[key])) continue;
  // If a data panel exists with this key, create a separate news panel with "-news" suffix
  const panelKey = this.panels[key] && !this.newsPanels[key] ? `${key}-news` : key;
  if (this.panels[panelKey]) continue;
  const panelConfig = DEFAULT_PANELS[panelKey] ?? DEFAULT_PANELS[key];
  const label = panelConfig?.name ?? key.charAt(0).toUpperCase() + key.slice(1);
  const panel = new NewsPanel(panelKey, label);
  this.attachRelatedAssetHandlers(panel);
  this.newsPanels[key] = panel;
  this.panels[panelKey] = panel;
}

This allows the finance variant to have paired panels like markets (data panel) + markets-news (RSS panel) without manual duplication.

10.4 Panel Toggle Flow

When a user clicks a panel toggle in the Settings modal:

  1. config.enabled is flipped on the in-memory panelSettings
  2. saveToStorage(STORAGE_KEYS.panels, this.panelSettings) persists to localStorage
  3. renderPanelToggles() re-renders the toggle UI with updated checkmarks
  4. applyPanelSettings() iterates all panels, calling panel.toggle(config.enabled)
  5. The Panel.toggle() method adds/removes the hidden CSS class

10.5 Panel Destruction

Panels are destroyed when the App instance is torn down. The Panel.destroy() method removes global event listeners (tooltip close handlers, touch/mouse handlers for resize). Panel DOM elements are removed when their parent grid is cleared.


11. Adding a New Panel

Step-by-step guide to adding a new panel to World Monitor.

Step 1: Define the Panel Config

Add the panel entry to the appropriate variant config in src/config/panels.ts:

typescript
// In the FULL_PANELS (or TECH_PANELS, FINANCE_PANELS) object:
'my-panel': { name: 'My Panel', enabled: true, priority: 2 },

Panel order matters — panels are rendered in declaration order by default.

Step 2: Create the Component Class

Create a new file in src/components/:

typescript
// src/components/MyPanel.ts
import { Panel } from './Panel';

export class MyPanel extends Panel {
  constructor() {
    super({
      id: 'my-panel',
      title: 'My Panel',
      showCount: true,
      infoTooltip: 'Methodology: ...',
    });
  }

  public async refresh(): Promise<void> {
    this.showLoading();
    try {
      const data = await fetch('/api/my-data').then(r => r.json());
      this.setContent(this.renderData(data));
      this.setCount(data.length);
      this.setDataBadge('live');
    } catch (e) {
      this.showError('Failed to load data');
      this.setDataBadge('unavailable');
    }
  }

  private renderData(data: unknown[]): string {
    return `<div class="my-panel-content">...</div>`;
  }
}

For RSS-based panels, use NewsPanel directly instead of creating a new class — just add feeds to the variant's FEEDS object and the panel will be auto-generated.

Step 3: Register in App.ts

Import and instantiate the panel in the createPanels() method of src/App.ts:

typescript
import { MyPanel } from '@/components/MyPanel';

// Inside createPanels():
const myPanel = new MyPanel();
this.panels['my-panel'] = myPanel;

Step 4: Wire Data Loading

Add a data loading task in loadAllData():

typescript
tasks.push({
  name: 'myPanel',
  task: runGuarded('myPanel', () => (this.panels['my-panel'] as MyPanel).refresh()),
});

Step 5: Add Refresh Interval (Optional)

If the panel needs periodic refresh, add a timer in setupRefreshTimers() referencing REFRESH_INTERVALS.

Step 6: Add Map Layer (Optional)

If the panel has an associated map layer:

  1. Add the boolean field to MapLayers in src/types/index.ts
  2. Set default values in all variant layer objects in src/config/panels.ts
  3. Handle the layer in MapContainer

Step 7: Add i18n Key

Add a translation key in src/locales/en.json (and other locale files):

json
{
  "panels": {
    "myPanel": "My Panel"
  }
}

Step 8: Register in Variant Configs

If using the variant config system (src/config/variants/), add the panel to the appropriate variant file(s) alongside feeds if applicable.


12. Diagrams

12.1 Panel Registration Flow

mermaid
flowchart TD
    A[App Constructor] --> B{Variant Changed?}
    B -->|Yes| C[Reset localStorage
Use DEFAULT_PANELS]
    B -->|No| D[Load panelSettings
from localStorage]
    C --> E[render: Build DOM Shell]
    D --> E
    E --> F[createPanels]
    F --> G[Instantiate Panel Classes
Register in this.panels]
    G --> H[Auto-generate NewsPanels
from FEEDS keys]
    H --> I[Resolve Panel Order
saved + merge missing]
    I --> J[Append to #panelsGrid
in resolved order]
    J --> K[makeDraggable
on each panel element]
    K --> L[applyPanelSettings
show/hide per config]
    L --> M[renderPanelToggles
build settings modal UI]
    M --> N[loadAllData
parallel API calls]

12.2 Variant Selection Flow

mermaid
flowchart LR
    ENV["VITE_VARIANT
env variable"] --> VAR["SITE_VARIANT
constant"]
    VAR --> SW{Switch}
    SW -->|"'tech'"| TP["TECH_PANELS
TECH_MAP_LAYERS
TECH_MOBILE_MAP_LAYERS"]
    SW -->|"'finance'"| FP["FINANCE_PANELS
FINANCE_MAP_LAYERS
FINANCE_MOBILE_MAP_LAYERS"]
    SW -->|default| GP["FULL_PANELS
FULL_MAP_LAYERS
FULL_MOBILE_MAP_LAYERS"]
    TP --> EX["DEFAULT_PANELS
DEFAULT_MAP_LAYERS
MOBILE_DEFAULT_MAP_LAYERS"]
    FP --> EX
    GP --> EX
    EX --> APP["App.ts
imports DEFAULT_PANELS"]
    APP --> TREE["Vite tree-shakes
unused variants"]

12.3 Panel Toggle Persistence Flow

mermaid
sequenceDiagram
    participant User
    participant SettingsModal
    participant App
    participant localStorage
    participant Panel

    User->>SettingsModal: Click panel toggle
    SettingsModal->>App: panelKey identified
    App->>App: config.enabled = !config.enabled
    App->>localStorage: saveToStorage('worldmonitor-panels', panelSettings)
    App->>SettingsModal: renderPanelToggles() — update checkmarks
    App->>App: applyPanelSettings()
    loop For each panel
        App->>Panel: panel.toggle(config.enabled)
        Panel->>Panel: add/remove 'hidden' CSS class
    end
    Note over localStorage: Survives page reload
    User->>User: Refreshes page
    App->>localStorage: loadFromStorage('worldmonitor-panels')
    App->>App: applyPanelSettings() with restored state

Appendix: Panel Component Files

Quick reference for locating panel component source files:

ComponentFile
Panel (base)src/components/Panel.ts
NewsPanelsrc/components/NewsPanel.ts
MarketPanel / HeatmapPanelsrc/components/MarketPanel.ts
CryptoPanelsrc/components/CryptoPanel.ts
CommoditiesPanelsrc/components/CommoditiesPanel.ts
PredictionPanelsrc/components/PredictionPanel.ts
EconomicPanelsrc/components/EconomicPanel.ts
MonitorPanelsrc/components/MonitorPanel.ts
CIIPanelsrc/components/CIIPanel.ts
CascadePanelsrc/components/CascadePanel.ts
GdeltIntelPanelsrc/components/GdeltIntelPanel.ts
SatelliteFiresPanelsrc/components/SatelliteFiresPanel.ts
StrategicRiskPanelsrc/components/StrategicRiskPanel.ts
StrategicPosturePanelsrc/components/StrategicPosturePanel.ts
UcdpEventsPanelsrc/components/UcdpEventsPanel.ts
DisplacementPanelsrc/components/DisplacementPanel.ts
ClimateAnomalyPanelsrc/components/ClimateAnomalyPanel.ts
PopulationExposurePanelsrc/components/PopulationExposurePanel.ts
InvestmentsPanelsrc/components/InvestmentsPanel.ts
LiveNewsPanelsrc/components/LiveNewsPanel.ts
LiveWebcamsPanelsrc/components/LiveWebcamsPanel.ts
TechEventsPanelsrc/components/TechEventsPanel.ts
ServiceStatusPanelsrc/components/ServiceStatusPanel.ts
TechReadinessPanelsrc/components/TechReadinessPanel.ts
MacroSignalsPanelsrc/components/MacroSignalsPanel.ts
ETFFlowsPanelsrc/components/ETFFlowsPanel.ts
StablecoinPanelsrc/components/StablecoinPanel.ts
InsightsPanelsrc/components/InsightsPanel.ts
RegulationPanelsrc/components/RegulationPanel.ts
RuntimeConfigPanelsrc/components/RuntimeConfigPanel.ts
OrefSirensPanelsrc/components/OrefSirensPanel.ts
StatusPanelsrc/components/StatusPanel.ts