Add scout constellation mode for APK venue persona packs.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Cluster 3+ scout_report hits on the same SSID within 10 minutes; server infers airport/campus/retail venue class and pushes persona spread_policy. Emberwake weather-map merges active scout biomes. Includes agent, server API, and Vitest coverage.
This commit is contained in:
@@ -12,6 +12,7 @@ import { SacredMotif } from '../Visual/sacredGeometry/motifs';
|
||||
import SetupBanner from '../SetupBanner';
|
||||
import { getSetupStatus } from '../../help/setupStatus';
|
||||
import { resolvePageWeather } from '../../help/pageWeather';
|
||||
import { mergeScoutBiomeWeather, type ScoutConstellationSnapshot } from '../../help/scoutBiomeWeather';
|
||||
import { isDashboardRoute } from '../../help/routeEffects';
|
||||
import { api } from '../../api/client';
|
||||
import { usePresence } from '../../context/PresenceContext';
|
||||
@@ -40,10 +41,11 @@ function operatorDeckId(pathname: string): string {
|
||||
if (path.startsWith('/lotl-timeline') || path.startsWith('/onion')) return 'lotl-timeline';
|
||||
if (path.startsWith('/roi')) return 'roi';
|
||||
if (path.startsWith('/activity')) return 'activity';
|
||||
if (path.startsWith('/seer')) return 'seer';
|
||||
return 'dashboard';
|
||||
}
|
||||
|
||||
const NAV = [
|
||||
const NAV_BASE = [
|
||||
{ to: '/dashboard', label: 'Command Deck', icon: 'deck' },
|
||||
{ to: '/crucible', label: 'Crucible', icon: 'crucible' },
|
||||
{ to: '/activity', label: 'Activity Feed', icon: 'activity' },
|
||||
@@ -57,6 +59,20 @@ const NAV = [
|
||||
{ to: '/settings', label: 'Calibrate', icon: 'gear' },
|
||||
] as const;
|
||||
|
||||
const SEER_NAV = { to: '/seer', label: 'Seer', icon: 'seer' } as const;
|
||||
|
||||
function buildNav(aiControlEnabled: boolean) {
|
||||
if (!aiControlEnabled) {
|
||||
return [...NAV_BASE];
|
||||
}
|
||||
const items = [...NAV_BASE];
|
||||
const calibrateIdx = items.findIndex((i) => i.to === '/settings');
|
||||
items.splice(calibrateIdx, 0, SEER_NAV);
|
||||
return items;
|
||||
}
|
||||
|
||||
const NAV = NAV_BASE;
|
||||
|
||||
const DOCS_HREF = '/docs/';
|
||||
|
||||
/** Primary tabs on mobile bottom bar — Deck, Crucible, Activity, ROI, Onion */
|
||||
@@ -150,6 +166,14 @@ function NavIcon({ type }: { type: string }) {
|
||||
<path d="M12 8v4" />
|
||||
</svg>
|
||||
);
|
||||
case 'seer':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
<ellipse cx="12" cy="12" rx="9" ry="5" />
|
||||
<circle cx="12" cy="12" r="2.5" fill="currentColor" strokeWidth="0" />
|
||||
<path d="M4 12c2-3 5-4.5 8-4.5s6 1.5 8 4.5" strokeOpacity="0.45" />
|
||||
</svg>
|
||||
);
|
||||
case 'docs':
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||
@@ -281,7 +305,19 @@ export default function Layout({ children }: LayoutProps) {
|
||||
}, [moreOpen]);
|
||||
|
||||
const setupStatus = getSetupStatus(serverConfig, serverInfo);
|
||||
const pageWeather = resolvePageWeather(location.pathname);
|
||||
const { latestMessage } = useWebSocket();
|
||||
const scoutBiome = useMemo(() => {
|
||||
if (latestMessage?.type !== 'scout_constellations') return null;
|
||||
return latestMessage.payload as ScoutConstellationSnapshot;
|
||||
}, [latestMessage]);
|
||||
const pageWeather = useMemo(() => {
|
||||
const base = resolvePageWeather(location.pathname);
|
||||
const path = location.pathname.split('?')[0].replace(/\/$/, '') || '/';
|
||||
if (path === '/emberwake' || path === '/spread' || path === '/dashboard' || path === '/agents') {
|
||||
return mergeScoutBiomeWeather(base, scoutBiome);
|
||||
}
|
||||
return base;
|
||||
}, [location.pathname, scoutBiome]);
|
||||
const showDeckEffects = isDashboardRoute(location.pathname);
|
||||
const moreActive = MOBILE_MORE.some((item) => location.pathname === item.to);
|
||||
const mobileShortLabel: Record<string, string> = {
|
||||
|
||||
Reference in New Issue
Block a user