* feat: enhance support for HLS streams and update font styles
* chore: add .vercelignore to exclude large local build artifacts from Vercel deploys
* chore: include node types in tsconfig to fix server type errors on Vercel build
* fix(middleware): guard optional variant OG lookup to satisfy strict TS
* fix: desktop build and live channels handle null safety
- scripts/build-sidecar-sebuf.mjs: Skip building removed [domain]/v1/[rpc].ts (removed in #785)
- src/live-channels-window.ts: Add optional chaining for handle property to prevent null errors
- src-tauri/Cargo.lock: Bump version to 2.5.24
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix: address review issues on PR #1020
- Remove AGENTS.md (project guidelines belong to repo owner)
- Restore tracking script in index.html (accidentally removed)
- Revert tsconfig.json "node" types (leaks Node globals to frontend)
- Add protocol validation to isHlsUrl() (security: block non-http URIs)
- Revert Cargo.lock version bump (release management concern)
* fix: address P2/P3 review findings
- Preserve hlsUrl for HLS-only channels in refreshChannelInfo (was
incorrectly clearing the stream URL on every refresh cycle)
- Replace deprecated .substr() with .substring()
- Extract duplicated HLS display name logic into getChannelDisplayName()
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Elie Habib <elie.habib@gmail.com>
The settings and signal modal overlays had z-index: 1000, which was
lower than the stacking context created by the globe's WebGL canvas
container. Bump both to z-index: 9999 to match other fullscreen overlays.
- Add 56px accent-blue floating action button (bottom-right)
- Always visible regardless of scroll or horizontal overflow
- Hide subtitles and type badges on mobile results for cleaner look
- Wire FAB to same openSearch handler as header icon
* feat: add mobile bottom sheet search with trigger bar
Replace the desktop-only Cmd+K search modal with a native-feeling
bottom sheet on mobile (<768px). Adds a fixed pill-shaped trigger
bar at the bottom of the screen, suggestion chips for quick access
to countries/commands, 48px touch targets, and slide-up animation.
Also removes the deprecated MobileWarningModal, adds a community
discussion link in settings, and improves mobile header layout.
* fix: improve mobile search bottom sheet UX
- Remove redundant trigger pill (header search icon is primary entry)
- Reduce sheet height from 75vh to 50vh for half-sheet feel
- Cap mobile results to 5 (2 per type) to avoid overwhelming small screens
- Add visualViewport listener for iOS keyboard-aware sheet resizing
- Replace clipped "Cancel" text with × icon + aria-label
- Hide chips on first keystroke to preserve vertical space
- Show 2 tips instead of 4 on mobile
- Revert panels-grid padding-bottom (no longer needed without pill)
* enhance supply chain panel
* fix(supply-chain): resolve P1 threat zeroing and P2 geo-first misclassification
P1: threat baseline is now always applied regardless of config
staleness — stale config only adds a review-recommended note,
never zeros the score.
P2: resolveChokepointId now checks text evidence first and only
falls back to proximity when text has no confident match.
Adds regression test: text "Bab el-Mandeb" with location near
Suez correctly resolves to bab_el_mandeb.
* fix(webcams): recover from blocked/stuck youtube embeds
---------
Co-authored-by: fayez bast <fayezbast15@gmail.com>
Replace the desktop-only Cmd+K search modal with a native-feeling
bottom sheet on mobile (<768px). Adds a fixed pill-shaped trigger
bar at the bottom of the screen, suggestion chips for quick access
to countries/commands, 48px touch targets, and slide-up animation.
Also removes the deprecated MobileWarningModal, adds a community
discussion link in settings, and improves mobile header layout.
Add real-time search/filter for available channels in the manage channels
window, with debounced input, per-region match counts, and empty state.
Also adds AlJazeera Arabic to the Middle East region with HLS stream.
* feat: enhance map resizing with automatic container observation, top-edge pinning, and responsive height adjustments.
* fix: Correct map resizing and prevent duplicate transition end events during height adjustments.
* feat: enable country clicks in the CII panel to open country briefs and add hover styling for clickable countries on maps.
* feat: Add globe map resize support and refactor map resize event handling logic.
* refactor: Streamline map destruction logic, remove redundant map resizing calls, and format CSS keyframe rules.
* fix: revert unintended destroy() behavior change and CSS formatting noise
- destroy(): restore unconditional cleanup of all map instances (safer
for edge cases where stale refs might exist after mode switches)
- Revert cosmetic CSS changes: combinator spacing, keyframe
reformatting, missing newline at EOF
---------
Co-authored-by: Elie Habib <elie.habib@gmail.com>
- destroy(): restore unconditional cleanup of all map instances (safer
for edge cases where stale refs might exist after mode switches)
- Revert cosmetic CSS changes: combinator spacing, keyframe
reformatting, missing newline at EOF
* feat(globe): add glowing Beta badge to 3D globe controls
Cyan pulsing badge appears top-right above zoom controls, only when
the globe view is active.
* feat(mobile): replace GitHub icon with settings gear on mobile header
On screens ≤768px, hide the GitHub link and show a gear icon that
opens UnifiedSettings — giving mobile users quick access to settings
from the compact header row.
* feat(globe): add 3D globe view powered by globe.gl
Replicate the Sentinel.axonia.us globe locally and expose it via Settings.
- Add GlobeMap.ts: new globe.gl v2 component with night-sky starfield,
earth topobathy texture, specular water map, atmosphere glow, auto-rotate
(pauses on interaction, resumes after 60 s), and HTML marker layer for
conflict zones, intel hotspots, and other data categories
- Update MapContainer with switchToGlobe() / switchToFlat() runtime
methods and isGlobeMode() query; constructor accepts preferGlobe param
- Wire globe toggle in UnifiedSettings General tab (MAP section);
persisted to worldmonitor-map-mode via loadFromStorage/saveToStorage
- Add mapMode storage key to STORAGE_KEYS
- Download earth textures to public/textures/ (topo-bathy, night-sky,
water specular, day)
- Add globe.gl ^2.45.0 and @types/three dependencies
- Add globe CSS + @keyframes globe-pulse for pulsing conflict markers
* feat(globe): wire region selector & CMD+K navigation to 3D globe
* feat(globe): add zoom controls, layer panel, marker tooltips; fix Vercel build
* feat(globe): expand to all 28 world-variant layers with live data rendering
* refactor(globe): use proper keyof MapLayers types
* fix(globe): route AIS/flight data to globe, implement ship traffic markers, hide dayNight toggle
- MapContainer: add globe guard to setAisData and setFlightDelays (data was silently dropped)
- GlobeMap: implement setAisData with AisDisruptionMarker (typed, no any casts); renders
disruption events with severity-colored ship icons and full tooltip (name/type/severity)
- GlobeMap: three-point dayNight suppression — disabled in initGlobe(), overridden in
setLayers(), ignored in enableLayer(); toggle removed from layer panel UI
- MapContainer: add globe guards to 5 happy-variant setters (P3: keep no-op stubs in globe)
- Add tests/globe-2d-3d-parity.test.mjs: 13 static-analysis tests covering routing,
AIS marker fields, and dayNight suppression (all passing)
Add `touch-action: none` to .map-container, .deckgl-map-wrapper, and
#deckgl-basemap so the browser does not intercept pinch/pan gestures on
Android. MapLibre (and deck.gl via MapboxOverlay) already handle these
touch interactions internally; the missing CSS property caused the
browser to claim the gestures for native page zoom instead.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add widget picker & layout presets design for #882
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add widget picker implementation plan (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add layout presets config (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add i18n keys for layout presets (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add LayoutTabs component (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add WidgetPicker popover component (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add close button to panel headers (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: wire LayoutTabs and WidgetPicker into header (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: remove Settings > Panels tab, replaced by widget picker (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add layout presets validation tests (#882)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add missing supercluster, preact, @types/geojson, and @types/supercluster dependencies. Remove DOM reparenting in LiveNewsPanel fullscreen toggle that caused iframe reloads and channel switching. Hide sibling panels and map overlays when a panel is in fullscreen mode.
Add Web Share API button to both CountryDeepDivePanel (small brief) and
CountryBriefPage (large brief). Falls back to clipboard copy with a
checkmark flash when navigator.share is unavailable. Shares the deep
link URL (?c=CODE) with the country name as title.
Adds a "Reset Layout" button in Settings > Panels tab that clears all
saved panel heights, widths, map height, and ordering from localStorage,
then reloads to apply defaults. Includes i18n translations for all 19 locales.
Closes#797
Co-Authored-By: Vittorio Halfon <vittoriohalfon01@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Implement dynamic map resizing with a bottom panel grid for ultra-wide layouts and double-click resize.
* feat: Dynamically adjust panel layout based on map visibility and screen width, including new CSS for map-hidden state.
Make the Available Channels grid the primary way to toggle channels on/off:
- Add 10 default channels (Bloomberg, SkyNews, DW, etc.) to the grid
- Add "All" region tab showing every channel (with i18n for 19 locales)
- Grid cards are now toggleable: click ✓ to remove, click + to add
- Hover on added cards shows ✕ with red tint to signal removal
- Top pills get × close button on hover for quick removal
- Click-to-edit gated to custom channels only (builtins use grid toggle)
- Fix France24 default to use French channel (@FRANCE24)
UX polish:
- Tighten header/content whitespace (padding, gaps, margins reduced)
- Align settings gear icon to top of pill row (flex-start)
- Add ESC dismiss to all modals (channels, story, signal, findings)
- SignalModal ESC listener properly added on show, removed on hide
The header download button now uses the same green color scheme as the
old "DESKTOP AVAILABLE" popup, making it more visible. The redundant
sliding popup is removed since the header dropdown serves the same purpose.
Adds a download button in the header (web-only) that detects the user's
platform via navigator.userAgentData (with navigator.platform fallback)
and opens the correct installer URL. Hidden on mobile viewports.
Co-authored-by: AaronCorkyj <AaronCorkyj@users.noreply.github.com>
* fix: suppress map renders during resize drag for smoother UX
- Add isResizing flag to DeckGLMap and MapComponent
- Suppress ResizeObserver callbacks during active drag
- Set trackResize: false on maplibre to prevent redundant resizes
- Move render() call from mousemove to mouseup (single clean render)
- Add overflow: hidden to .map-section to prevent visual overflow
- Lower minimum map height from 400px to 350px
Co-authored-by: danielimad <daniel_t_air@hotmail.com>
* fix: prevent stuck isResizing via centralized endResize
Extract endResize() and trigger from mouseup, window blur, and
visibilitychange to prevent permanently suppressed ResizeObserver
if mouseup is missed (e.g. alt-tab during drag).
---------
Co-authored-by: danielimad <daniel_t_air@hotmail.com>
Move panel element to document.body when entering fullscreen to escape
the panels-grid stacking context (z-index: 1) that trapped it below
the map legend, DEFCON badge, time selector, and layers panel.
* fix(web): improve mobile responsiveness — collapsible map, panel sizing, font bump (#354)
- Add Show/Hide Map toggle on mobile (collapsed by default, persisted in localStorage)
- Increase panel max-height from 400px to 70vh and add iOS smooth scrolling
- Bump mobile font sizes to 13-14px for readability
- All changes scoped to ≤768px media query or isMobile guard
Closes#354
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(mobile): i18n map toggle, cap panel height, reduce !important
- Replace hardcoded "Show Map"/"Hide Map" with t() i18n calls
and ▶/▼ chevron prefix for universal clarity
- Add components.map.showMap/hideMap keys to all 18 locales
(ar, de, el, en, es, fr, it, ja, ko, nl, pl, pt, ru, sv, th, tr, vi, zh)
- Clarify localStorage default: explicit null check instead of
double-negative !== 'false'
- Cap panel max-height at min(70vh, 500px) to prevent single
panel from consuming full viewport on tall phones
- Reduce !important in collapsed map styles by raising specificity
via .main-content ancestor selector
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Elie Habib <elie.habib@gmail.com>
* feat(live-news): add Fox News HLS stream & fullscreen toggle for panels/map
- Add Fox News HLS direct stream (247preview.foxnews.com) to bypass
YouTube embedding restrictions (error 150)
- Remove isDesktopRuntime() gate from resolveChannelVideo so direct HLS
streams work in browser, not just desktop app
- Remove useFallbackOnly from Fox News channel config
- Add fullscreen maximize button to Live News, Live Webcams, and Global
Situation map panels — click to expand, ESC to exit
* fix(map): use map-pin-btn style for map fullscreen button visibility
* fix(map): group fullscreen and pin buttons in flex container
Wrap the fullscreen and pin buttons in a flex div so they stay
together on the right side of the map header instead of being
spread apart by justify-content: space-between.
* feat: animate panel count badge on new data arrival
* feat(settings): gate badge pulse animation behind toggle (off by default)
PR #671 adds CSS pulse on panel count badges. This commit gates it
behind a new `badgeAnimation` setting in UnifiedSettings so users
opt-in. Adds i18n keys for all 18 locales.
---------
Co-authored-by: jeronlxj <jeronliaw@u.nus.edu>
* feat(market): add Gulf Economies panel with GCC indices, currencies & oil
Add a new panel tracking 15 Gulf/GCC financial symbols via Yahoo Finance:
- 7 stock indices (TASI, DFMGI, ADX, QSI, Bahrain, Kuwait, MSM)
- 6 currencies (SAR, AED, QAR, KWD, BHD, OMR vs USD)
- 2 oil benchmarks (WTI, Brent)
Server: new ListGulfQuotes RPC with 3-tier caching (in-memory + Redis
via cachedFetchJson + stale fallback). Uses fetchYahooQuotesBatch for
rate-limit-safe sequential fetching.
Frontend: GulfEconomiesPanel with 3 collapsible sections, sparklines,
8s delayed start, 60s polling. Registered in both full and finance
variants.
Inspired by #641 (credit: @aa5064, commit 684a73c).
* refactor: hoist MarketServiceClient to module scope, clarify memCache closure
* fix(market): register list-gulf-quotes in RPC_CACHE_TIER map
Explicitly set 'medium' cache tier for the new Gulf quotes endpoint,
matching other market RPCs instead of relying on the implicit default.
* refactor: extract shared miniSparkline to @/utils/sparkline
Deduplicates the identical function from MarketPanel and GulfEconomiesPanel
into a single shared utility.
* fix(panels): disable gulf-economies by default on full variant
Panel remains always-on for finance variant. Full variant users can
manually enable it via panel settings.