diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx
index a4d53e7..2abc22e 100644
--- a/frontend/src/app/page.tsx
+++ b/frontend/src/app/page.tsx
@@ -4,8 +4,9 @@ import { API_BASE } from "@/lib/api";
import { useEffect, useState, useRef, useCallback } from "react";
import dynamic from 'next/dynamic';
import { motion } from "framer-motion";
+import { ChevronLeft, ChevronRight } from "lucide-react";
import WorldviewLeftPanel from "@/components/WorldviewLeftPanel";
-import WorldviewRightPanel from "@/components/WorldviewRightPanel";
+
import NewsFeed from "@/components/NewsFeed";
import MarketsPanel from "@/components/MarketsPanel";
import FilterPanel from "@/components/FilterPanel";
@@ -123,6 +124,8 @@ export default function Dashboard() {
// Stable reference for child components — only changes when dataVersion increments
const data = dataRef.current;
const [uiVisible, setUiVisible] = useState(true);
+ const [leftOpen, setLeftOpen] = useState(true);
+ const [rightOpen, setRightOpen] = useState(true);
const [settingsOpen, setSettingsOpen] = useState(false);
const [legendOpen, setLegendOpen] = useState(false);
const [mapView, setMapView] = useState({ zoom: 2, latitude: 20 });
@@ -435,21 +438,54 @@ export default function Dashboard() {
VSR
- {/* LEFT HUD CONTAINER */}
-
+ {/* LEFT HUD CONTAINER — slides off left edge when hidden */}
+
{/* LEFT PANEL - DATA LAYERS */}
setSettingsOpen(true)} onLegendClick={() => setLegendOpen(true)} gibsDate={gibsDate} setGibsDate={setGibsDate} gibsOpacity={gibsOpacity} setGibsOpacity={setGibsOpacity} onEntityClick={setSelectedEntity} onFlyTo={(lat, lng) => setFlyToLocation({ lat, lng, ts: Date.now() })} />
+
- {/* LEFT BOTTOM - DISPLAY CONFIG */}
-
-
-
-
+ {/* LEFT SIDEBAR TOGGLE TAB */}
+
+
+
- {/* RIGHT HUD CONTAINER */}
-
+ {/* RIGHT SIDEBAR TOGGLE TAB */}
+
+
+
+
+ {/* RIGHT HUD CONTAINER — slides off right edge when hidden */}
+
{/* FIND / LOCATE */}
@@ -505,7 +541,7 @@ export default function Dashboard() {
-
+
{/* BOTTOM CENTER COORDINATE / LOCATION BAR — hidden when Sentinel-2 imagery overlay is open */}
{!(selectedEntity?.type === 'region_dossier' && regionDossier?.sentinel2) && {
+ const interpFlight = useCallback((f: any): [number, number] => {
if (!f.speed_knots || f.speed_knots <= 0 || dtSeconds <= 0) return [f.lng, f.lat];
if (f.alt != null && f.alt <= 100) return [f.lng, f.lat];
if (dtSeconds < 1) return [f.lng, f.lat];
const heading = f.true_track || f.heading || 0;
const [newLat, newLng] = interpolatePosition(f.lat, f.lng, heading, f.speed_knots, dtSeconds);
return [newLng, newLat];
- };
+ }, [dtSeconds]);
// Helper: interpolate a ship's position using SOG + heading
- const interpShip = (s: any): [number, number] => {
+ const interpShip = useCallback((s: any): [number, number] => {
if (typeof s.sog !== 'number' || !s.sog || s.sog <= 0 || dtSeconds <= 0) return [s.lng, s.lat];
const heading = (typeof s.cog === 'number' ? s.cog : 0) || s.heading || 0;
const [newLat, newLng] = interpolatePosition(s.lat, s.lng, heading, s.sog, dtSeconds);
return [newLng, newLat];
- };
+ }, [dtSeconds]);
// Helper: interpolate a satellite's position between API updates
- const interpSat = (s: any): [number, number] => {
+ const interpSat = useCallback((s: any): [number, number] => {
if (!s.speed_knots || s.speed_knots <= 0 || dtSeconds < 1) return [s.lng, s.lat];
const [newLat, newLng] = interpolatePosition(s.lat, s.lng, s.heading || 0, s.speed_knots, dtSeconds, 0, 65);
return [newLng, newLat];
- };
+ }, [dtSeconds]);
// Satellite GeoJSON with interpolated positions
const satellitesGeoJSON = useMemo(() => {