Close DV-01–04,07,09,13,14: unify operator-deck UX in server/web.
Standardize neon cyan on #00e8f5, wire SacredPageHeader across Builds/Path Tracer/LOTL, dedupe Pages.css, align Path Tracer chrome, drop dead wealth-deck CSS, add prefers-color-scheme shell + HelpTip, sidebar version from package.json build inject, and Vitest CSS regression guards.
This commit is contained in:
@@ -12,10 +12,12 @@ import { SacredMotif } from '../Visual/sacredGeometry/motifs';
|
||||
import SetupBanner from '../SetupBanner';
|
||||
import { getSetupStatus } from '../../help/setupStatus';
|
||||
import { resolvePageWeather } from '../../help/pageWeather';
|
||||
import { isDashboardRoute } from '../../help/routeEffects';
|
||||
import { api } from '../../api/client';
|
||||
import { usePresence } from '../../context/PresenceContext';
|
||||
import { useVisualEffects } from '../../context/VisualEffectsContext';
|
||||
import ComradeAvatar from '../Presence/ComradeAvatar';
|
||||
import { formatAppVersion } from '../../help/appVersion';
|
||||
import type { ServerConfig, ServerInfo } from '../../types';
|
||||
import '../Presence/Presence.css';
|
||||
import './Layout.css';
|
||||
@@ -280,6 +282,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
|
||||
const setupStatus = getSetupStatus(serverConfig, serverInfo);
|
||||
const pageWeather = resolvePageWeather(location.pathname);
|
||||
const showDeckEffects = isDashboardRoute(location.pathname);
|
||||
const moreActive = MOBILE_MORE.some((item) => location.pathname === item.to);
|
||||
const mobileShortLabel: Record<string, string> = {
|
||||
'/dashboard': 'Deck',
|
||||
@@ -296,7 +299,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
className={`layout${isMobile ? ' layout--mobile' : ''}${othersOnline ? ' layout--comrades-online' : ''}`}
|
||||
data-operator-deck={operatorDeckId(location.pathname)}
|
||||
>
|
||||
{!isMobile && glowParticles && <CursorFire />}
|
||||
{!isMobile && glowParticles && showDeckEffects && <CursorFire />}
|
||||
<AmbientBackground weather={pageWeather} />
|
||||
{glowParticles && <SacredGeometryLayer />}
|
||||
<nav className="sidebar sidebar--desktop desktop-only">
|
||||
@@ -348,8 +351,8 @@ export default function Layout({ children }: LayoutProps) {
|
||||
<SacredMotif name="flower" opacity={0.6} />
|
||||
</div>
|
||||
|
||||
{/* Matrix rain log — fills the lower sidebar between nav and footer */}
|
||||
<MatrixRain />
|
||||
{/* Matrix rain log — Command Deck only; fills lower sidebar between nav and footer */}
|
||||
{showDeckEffects && <MatrixRain />}
|
||||
|
||||
<div className="sidebar-footer">
|
||||
<FleetReadout />
|
||||
@@ -367,7 +370,7 @@ export default function Layout({ children }: LayoutProps) {
|
||||
)}
|
||||
<div className="sidebar-sig font-tech">
|
||||
<span className="sig-love">made with <span className="sig-heart">♥</span> drjones</span>
|
||||
<span className="sig-ver">{serverInfo?.version ?? 'v1.0.0'}</span>
|
||||
<span className="sig-ver">{formatAppVersion(serverInfo?.version)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -837,6 +837,19 @@ describe('SystemStatusBar', () => {
|
||||
expect(screen.getByText(/1 BUILD/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('derives fleet counts from WebSocket agents, not REST listAgents', async () => {
|
||||
const listAgentsSpy = vi.spyOn(api, 'listAgents');
|
||||
render(
|
||||
<MemoryRouter future={routerFuture}>
|
||||
<SystemStatusBar />
|
||||
</MemoryRouter>
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/FLEET 1\/2 ONLINE/i)).toBeInTheDocument();
|
||||
});
|
||||
expect(listAgentsSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('FleetTopologyMap', () => {
|
||||
@@ -925,4 +938,32 @@ describe('Layout', () => {
|
||||
expect(screen.getByRole('link', { name: /Crucible/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /Onion/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('mounts MatrixRain and CursorFire on Command Deck only', async () => {
|
||||
const { container: deck } = render(
|
||||
<MemoryRouter initialEntries={['/dashboard']} future={routerFuture}>
|
||||
<Layout>
|
||||
<div>deck</div>
|
||||
</Layout>
|
||||
</MemoryRouter>
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(deck.querySelector('.matrix-rain-canvas')).toBeTruthy();
|
||||
expect(deck.querySelector('.cursor-fire-fx')).toBeTruthy();
|
||||
});
|
||||
cleanup();
|
||||
|
||||
const { container: crucible } = render(
|
||||
<MemoryRouter initialEntries={['/crucible']} future={routerFuture}>
|
||||
<Layout>
|
||||
<div>crucible</div>
|
||||
</Layout>
|
||||
</MemoryRouter>
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('crucible')).toBeInTheDocument();
|
||||
});
|
||||
expect(crucible.querySelector('.matrix-rain-canvas')).toBeNull();
|
||||
expect(crucible.querySelector('.cursor-fire-fx')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user