feat: alive UI wave, galaxy presence, spread and fleet enhancements
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Dashboard ambient layer, comrade presence, Mission Deck and War Room, Emberwake supply chain, spread/docs publishing, fleet policy and modules API, CI docker mining, and refreshed USB pack.
This commit is contained in:
AetherForge
2026-06-04 22:36:17 -07:00
parent 1551bd5dad
commit a32860b0d9
154 changed files with 17383 additions and 601 deletions

View File

@@ -137,6 +137,24 @@
text-decoration: none;
}
.nav-item--docs {
margin-top: 0.35rem;
border: 1px solid rgba(0, 232, 245, 0.22);
background: linear-gradient(90deg, rgba(0, 232, 245, 0.06), transparent);
text-decoration: none;
}
.nav-item--docs:hover {
background: linear-gradient(90deg, rgba(0, 232, 245, 0.14), rgba(168, 62, 240, 0.06));
border-color: rgba(0, 232, 245, 0.45);
box-shadow: 0 0 16px rgba(0, 232, 245, 0.12);
}
.mobile-more-link--docs {
border: 1px solid rgba(0, 232, 245, 0.25);
background: rgba(0, 232, 245, 0.06);
}
.nav-item.active {
background: linear-gradient(90deg, rgba(0, 245, 255, 0.12), transparent);
color: var(--neon-cyan);
@@ -169,6 +187,23 @@
border-radius: 0 2px 2px 0;
}
.nav-item--mission.active {
background: linear-gradient(90deg, rgba(255, 140, 58, 0.18), transparent);
color: #ff8c3a;
border-color: rgba(255, 140, 58, 0.45);
box-shadow: inset 0 0 24px rgba(255, 140, 58, 0.12);
}
.nav-item--mission:hover {
color: #ffb366;
border-color: rgba(255, 140, 58, 0.35);
}
.nav-glow--mission {
background: #ff8c3a;
box-shadow: 0 0 14px #ff8c3a, 0 0 28px rgba(255, 140, 58, 0.45);
}
/* ── Matrix rain ──────────────────────────────── */
.matrix-rain-wrap {
/* Flex-grow to fill all space between nav and footer */

View File

@@ -11,30 +11,50 @@ import SacredGeometryLayer from '../Visual/sacredGeometry/SacredGeometryLayer';
import { SacredMotif } from '../Visual/sacredGeometry/motifs';
import SetupBanner from '../SetupBanner';
import { getSetupStatus } from '../../help/setupStatus';
import { resolvePageWeather } from '../../help/pageWeather';
import { api } from '../../api/client';
import { usePresence } from '../../context/PresenceContext';
import ComradeAvatar from '../Presence/ComradeAvatar';
import type { ServerConfig } from '../../types';
import '../Presence/Presence.css';
import './Layout.css';
import './MobileNav.css';
import '../../styles/operatorDeck.css';
interface LayoutProps {
children: ReactNode;
}
function operatorDeckId(pathname: string): string {
const path = pathname.split('?')[0].replace(/\/$/, '') || '/';
if (path.startsWith('/mission-deck')) return 'mission-deck';
if (path.startsWith('/forge') || path.startsWith('/builder')) return 'forge';
if (path.startsWith('/crucible')) return 'crucible';
if (path.startsWith('/emberwake') || path.startsWith('/spread')) return 'emberwake';
if (path.startsWith('/agents')) return 'fleet';
if (path.startsWith('/builds')) return 'builds';
if (path.startsWith('/settings')) return 'settings';
if (path.startsWith('/pathtracer')) return 'pathtracer';
return 'dashboard';
}
const NAV = [
{ to: '/dashboard', label: 'Command Deck', icon: 'deck' },
{ to: '/agents', label: 'Fleet Roster', icon: 'fleet' },
{ to: '/crucible', label: 'Crucible', icon: 'crucible' },
{ to: '/forge', label: 'Forge', icon: 'forge' },
{ to: '/mission-deck', label: 'Mission Deck', icon: 'mission', glow: true },
{ to: '/builds', label: 'Builds', icon: 'builds' },
{ to: '/emberwake', label: 'Emberwake', icon: 'ember' },
{ to: '/guide', label: 'Field Guide', icon: 'guide' },
{ to: '/settings', label: 'Calibrate', icon: 'gear' },
{ to: '/pathtracer', label: 'Path Tracer', icon: 'trace' },
] as const;
const DOCS_HREF = '/docs/';
/** Primary tabs on iPhone bottom bar */
const MOBILE_PRIMARY = NAV.slice(0, 4);
/** Builds, Guide, Calibrate, Path Tracer — “More” sheet */
/** Builds, Calibrate, Path Tracer — “More” sheet */
const MOBILE_MORE = NAV.slice(4);
function NavIcon({ type }: { type: string }) {
@@ -60,6 +80,12 @@ function NavIcon({ type }: { type: string }) {
<path d="M8 16l-2 4 4-2" />
</svg>
);
case 'mission':
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M13 2L3 14h8l-1 8 10-12h-8l1-8z" />
</svg>
);
case 'builds':
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
@@ -69,14 +95,6 @@ function NavIcon({ type }: { type: string }) {
<path d="M7 4.5h10M7 10.5h10M7 16.5h10" strokeOpacity="0.35" />
</svg>
);
case 'guide':
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M4 19.5A2.5 2.5 0 016.5 17H20" />
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" />
<path d="M8 7h8M8 11h6" />
</svg>
);
case 'crucible':
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
@@ -103,6 +121,14 @@ function NavIcon({ type }: { type: string }) {
<path d="M12 8v4" />
</svg>
);
case 'docs':
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M4 19.5A2.5 2.5 0 016.5 17H20" />
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z" />
<path d="M8 7h8M8 11h6" strokeOpacity="0.55" />
</svg>
);
default:
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
@@ -185,6 +211,7 @@ function MobileTopStats() {
export default function Layout({ children }: LayoutProps) {
const location = useLocation();
const isMobile = useIsMobileLayout();
const { othersOnline, comrades } = usePresence();
const [serverConfig, setServerConfig] = useState<ServerConfig | null>(null);
const [moreOpen, setMoreOpen] = useState(false);
@@ -206,6 +233,7 @@ export default function Layout({ children }: LayoutProps) {
}, [moreOpen]);
const setupStatus = getSetupStatus(serverConfig);
const pageWeather = resolvePageWeather(location.pathname);
const moreActive = MOBILE_MORE.some((item) => location.pathname === item.to);
const mobileShortLabel: Record<string, string> = {
'/dashboard': 'Deck',
@@ -215,9 +243,12 @@ export default function Layout({ children }: LayoutProps) {
};
return (
<div className={`layout${isMobile ? ' layout--mobile' : ''}`}>
<div
className={`layout${isMobile ? ' layout--mobile' : ''}${othersOnline ? ' layout--comrades-online' : ''}`}
data-operator-deck={operatorDeckId(location.pathname)}
>
{!isMobile && <CursorFire />}
<AmbientBackground />
<AmbientBackground weather={pageWeather} />
<SacredGeometryLayer />
<nav className="sidebar sidebar--desktop desktop-only">
<div className="sidebar-header">
@@ -238,15 +269,30 @@ export default function Layout({ children }: LayoutProps) {
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
className={({ isActive }) =>
`nav-item${'glow' in item && item.glow ? ' nav-item--mission' : ''} ${isActive ? 'active' : ''}`
}
>
<span className="nav-icon">
<NavIcon type={item.icon} />
</span>
<span className="nav-label">{item.label}</span>
{location.pathname === item.to && <span className="nav-glow" />}
{location.pathname === item.to && (
<span className={`nav-glow${'glow' in item && item.glow ? ' nav-glow--mission' : ''}`} />
)}
</NavLink>
))}
<a
href={DOCS_HREF}
target="_blank"
rel="noopener noreferrer"
className="nav-item nav-item--docs"
>
<span className="nav-icon">
<NavIcon type="docs" />
</span>
<span className="nav-label">Documentation</span>
</a>
</div>
<div className="sidebar-sacred-sigil" aria-hidden>
@@ -258,6 +304,18 @@ export default function Layout({ children }: LayoutProps) {
<div className="sidebar-footer">
<FleetReadout />
{othersOnline && (
<div className="sidebar-comrades">
<div className="sidebar-comrades-avatars">
{comrades.slice(0, 4).map((c) => (
<ComradeAvatar key={c.user} user={c.user} size="sm" />
))}
</div>
<span className="sidebar-comrades-label">
{comrades.length} comrade{comrades.length === 1 ? '' : 's'} online
</span>
</div>
)}
<div className="sidebar-sig font-tech">
<span className="sig-love">made with <span className="sig-heart"></span> drjones</span>
<span className="sig-ver">v0.0.1</span>
@@ -300,6 +358,16 @@ export default function Layout({ children }: LayoutProps) {
{item.label}
</NavLink>
))}
<a
href={DOCS_HREF}
target="_blank"
rel="noopener noreferrer"
className="mobile-more-link mobile-more-link--docs"
onClick={() => setMoreOpen(false)}
>
<NavIcon type="docs" />
Documentation
</a>
</div>
<nav className="mobile-bottom-nav" aria-label="Main navigation">
{MOBILE_PRIMARY.map((item) => (