diff --git a/src/app/event-handlers.ts b/src/app/event-handlers.ts
index 4930c01a7..feb398b1e 100644
--- a/src/app/event-handlers.ts
+++ b/src/app/event-handlers.ts
@@ -262,6 +262,9 @@ export class EventHandlerManager implements AppModule {
(panel as unknown as { refreshChannelsFromStorage: () => void }).refreshChannelsFromStorage();
}
}
+ if (e.key === STORAGE_KEYS.mapMode) {
+ this.syncMapModeUI();
+ }
};
window.addEventListener('storage', this.boundStorageHandler);
@@ -311,6 +314,7 @@ export class EventHandlerManager implements AppModule {
this.setupMapResize();
this.setupMapPin();
+ this.setupMap3D();
this.boundVisibilityHandler = () => {
document.body?.classList.toggle('animations-paused', document.hidden);
@@ -648,14 +652,7 @@ export class EventHandlerManager implements AppModule {
isDesktopApp: this.ctx.isDesktopApp,
statusPanel: this.ctx.statusPanel,
isGlobeMode: () => this.ctx.map?.isGlobeMode() ?? false,
- onMapModeChange: (useGlobe: boolean) => {
- saveToStorage(STORAGE_KEYS.mapMode, useGlobe ? 'globe' : 'flat');
- if (useGlobe) {
- this.ctx.map?.switchToGlobe();
- } else {
- this.ctx.map?.switchToFlat();
- }
- },
+ onMapModeChange: (useGlobe: boolean) => this.setMapMode(useGlobe),
});
if (this.ctx.statusPanel) {
@@ -950,6 +947,34 @@ export class EventHandlerManager implements AppModule {
this.setupMapFullscreen(mapSection);
}
+ setupMap3D(): void {
+ const btn = document.getElementById('map3dBtn');
+ if (!btn) return;
+
+ this.syncMapModeUI();
+
+ btn.addEventListener('click', () => {
+ const isGlobe = this.ctx.map?.isGlobeMode();
+ this.setMapMode(!isGlobe);
+ });
+ }
+
+ private setMapMode(useGlobe: boolean): void {
+ if (useGlobe) {
+ this.ctx.map?.switchToGlobe();
+ } else {
+ this.ctx.map?.switchToFlat();
+ }
+ saveToStorage(STORAGE_KEYS.mapMode, useGlobe ? 'globe' : 'flat');
+ this.syncMapModeUI();
+ }
+
+ private syncMapModeUI(): void {
+ const isGlobe = this.ctx.map?.isGlobeMode();
+ document.getElementById('map3dBtn')?.classList.toggle('active', isGlobe);
+ this.ctx.unifiedSettings?.refreshMapMode();
+ }
+
private setupMapFullscreen(mapSection: HTMLElement): void {
const btn = document.getElementById('mapFullscreenBtn');
if (!btn) return;
diff --git a/src/app/panel-layout.ts b/src/app/panel-layout.ts
index fdbfa404d..7248d05be 100644
--- a/src/app/panel-layout.ts
+++ b/src/app/panel-layout.ts
@@ -214,6 +214,9 @@ export class PanelLayoutManager implements AppModule {
+
diff --git a/src/components/UnifiedSettings.ts b/src/components/UnifiedSettings.ts
index 62e7ad9fd..d8dc76183 100644
--- a/src/components/UnifiedSettings.ts
+++ b/src/components/UnifiedSettings.ts
@@ -217,6 +217,13 @@ export class UnifiedSettings {
if (this.activeTab === 'panels') this.renderPanelsTab();
}
+ public refreshMapMode(): void {
+ if (this.activeTab === 'general') {
+ const cb = this.overlay.querySelector('#us-globe-mode') as HTMLInputElement;
+ if (cb) cb.checked = this.config.isGlobeMode?.() ?? false;
+ }
+ }
+
public getButton(): HTMLButtonElement {
const btn = document.createElement('button');
btn.className = 'unified-settings-btn';
diff --git a/src/locales/en.json b/src/locales/en.json
index 1f75d61d7..680b5d378 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -80,14 +80,7 @@
"noSignals": "No recent high-severity signals.",
"assessmentUnavailable": "Assessment unavailable.",
"noNews": "No recent country-specific coverage.",
- "noMarkets": "No active markets for this country.",
"noIndicators": "No country-specific indicators available.",
- "components": {
- "unrest": "Unrest",
- "conflict": "Conflict",
- "security": "Security",
- "information": "Information"
- },
"nearbyPorts": "Nearby Ports",
"detected": "Detected",
"notDetected": "No",
@@ -139,6 +132,7 @@
"downloadApp": "Download App",
"fullscreen": "Fullscreen",
"pinMap": "Pin map to top",
+ "globeMode": "3D Globe Mode",
"viewOnGitHub": "View on GitHub",
"filterSources": "Filter sources...",
"sourcesEnabled": "{{enabled}}/{{total}} enabled",
@@ -2305,4 +2299,4 @@
"refresh": "Refresh",
"all": "All"
}
-}
+}
\ No newline at end of file
diff --git a/src/styles/main.css b/src/styles/main.css
index f2df3b582..b7d1bd547 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -952,13 +952,21 @@ canvas,
background: transparent;
border: 1px solid var(--border);
color: var(--text-dim);
- padding: 4px 8px;
+ width: 26px;
+ height: 26px;
+ padding: 0;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
+ flex-shrink: 0;
+}
+
+#map3dBtn {
+ font-size: 9px;
+ font-weight: bold;
}
.map-pin-btn:hover {
@@ -2212,7 +2220,7 @@ body.live-news-fullscreen-active .map-legend {
display: none !important;
}
-body.live-news-fullscreen-active .panels-grid > *:not(.live-news-fullscreen) {
+body.live-news-fullscreen-active .panels-grid>*:not(.live-news-fullscreen) {
visibility: hidden !important;
}
@@ -12511,8 +12519,15 @@ a.prediction-link:hover {
}
@keyframes globe-beta-pulse {
- 0%, 100% { box-shadow: 0 0 6px rgba(0, 229, 255, 0.3), 0 0 20px rgba(0, 229, 255, 0.15), inset 0 0 8px rgba(0, 229, 255, 0.05); }
- 50% { box-shadow: 0 0 10px rgba(0, 229, 255, 0.5), 0 0 30px rgba(0, 229, 255, 0.25), inset 0 0 12px rgba(0, 229, 255, 0.1); }
+
+ 0%,
+ 100% {
+ box-shadow: 0 0 6px rgba(0, 229, 255, 0.3), 0 0 20px rgba(0, 229, 255, 0.15), inset 0 0 8px rgba(0, 229, 255, 0.05);
+ }
+
+ 50% {
+ box-shadow: 0 0 10px rgba(0, 229, 255, 0.5), 0 0 30px rgba(0, 229, 255, 0.25), inset 0 0 12px rgba(0, 229, 255, 0.1);
+ }
}
/* deck.gl Controls */
@@ -16561,7 +16576,18 @@ body.has-breaking-alert .panels-grid {
}
@keyframes globe-pulse {
- 0% { transform: scale(1); opacity: 0.6; }
- 70% { transform: scale(2.5); opacity: 0; }
- 100% { transform: scale(2.5); opacity: 0; }
-}
+ 0% {
+ transform: scale(1);
+ opacity: 0.6;
+ }
+
+ 70% {
+ transform: scale(2.5);
+ opacity: 0;
+ }
+
+ 100% {
+ transform: scale(2.5);
+ opacity: 0;
+ }
+}
\ No newline at end of file