Add AV/Defender tests and clear PROBLEMS.md antivirus rows.
Cover mining diagnostics JSON blockers, AV-Safe preset fields, defender_off error paths, and Calibrate exclusion .ps1 generation with Go + Vitest; honest AV limits already documented in settingHelp.
This commit is contained in:
@@ -17,8 +17,8 @@
|
||||
background-image:
|
||||
linear-gradient(rgba(201, 162, 39, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(201, 162, 39, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(rgba(0, 245, 255, 0.02) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 245, 255, 0.02) 1px, transparent 1px);
|
||||
linear-gradient(rgba(0, 232, 245, 0.02) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 232, 245, 0.02) 1px, transparent 1px);
|
||||
background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
|
||||
animation: grid-drift var(--weather-grid-drift) linear infinite;
|
||||
transform: perspective(500px) rotateX(60deg) scale(2);
|
||||
@@ -43,7 +43,7 @@
|
||||
.ambient-orb-cyan {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: rgba(0, 245, 255, 0.12);
|
||||
background: rgba(0, 232, 245, 0.12);
|
||||
top: 10%;
|
||||
right: 5%;
|
||||
animation-delay: 0s;
|
||||
@@ -72,7 +72,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 245, 255, 0.15), transparent);
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 232, 245, 0.15), transparent);
|
||||
animation: scanline 8s linear infinite;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
animation-delay: 0s;
|
||||
}
|
||||
.ambient-sparkle--2 {
|
||||
color: rgba(0, 245, 255, 0.45);
|
||||
color: rgba(0, 232, 245, 0.45);
|
||||
top: 72%;
|
||||
left: 68%;
|
||||
animation-delay: -1.2s;
|
||||
@@ -57,7 +57,7 @@
|
||||
.ambient-sparkle:nth-child(5) {
|
||||
top: 8%;
|
||||
left: 55%;
|
||||
color: rgba(0, 245, 255, 0.35);
|
||||
color: rgba(0, 232, 245, 0.35);
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.ambient-sparkle:nth-child(6) {
|
||||
@@ -81,7 +81,7 @@
|
||||
.ambient-sparkle:nth-child(9) {
|
||||
top: 35%;
|
||||
left: 8%;
|
||||
color: rgba(0, 245, 255, 0.38);
|
||||
color: rgba(0, 232, 245, 0.38);
|
||||
animation-delay: -2.8s;
|
||||
}
|
||||
.ambient-sparkle:nth-child(10) {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--neon-cyan);
|
||||
text-shadow: 0 0 12px rgba(0, 245, 255, 0.5);
|
||||
text-shadow: 0 0 12px rgba(0, 232, 245, 0.5);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
border: 1px solid var(--border-brass);
|
||||
border-radius: 2px;
|
||||
padding: 0.75rem 1rem;
|
||||
box-shadow: 0 0 20px rgba(0, 245, 255, 0.15), var(--shadow-panel);
|
||||
box-shadow: 0 0 20px rgba(0, 232, 245, 0.15), var(--shadow-panel);
|
||||
}
|
||||
|
||||
.neon-tooltip-time {
|
||||
|
||||
@@ -33,7 +33,7 @@ interface HashrateChartProps {
|
||||
const GRAD_IDS = ['cyan', 'magenta', 'amber', 'green', 'purple'] as const;
|
||||
|
||||
function colorToId(color: string): string {
|
||||
if (color.includes('e8f5') || color.includes('f5ff') || color.includes('06b6d4') || color === '#00e8f5' || color === '#00f5ff' || color.includes('neon-cyan')) return 'cyan';
|
||||
if (color.includes('e8f5') || color.includes('f5ff') || color.includes('06b6d4') || color === '#00e8f5' || color === '#00e8f5' || color.includes('neon-cyan')) return 'cyan';
|
||||
if (color.includes('2da6') || color.includes('8b5cf6')) return 'magenta';
|
||||
if (color.includes('b020') || color.includes('eab308')) return 'amber';
|
||||
if (color.includes('39ff') || color.includes('22c55e')) return 'green';
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
|
||||
.access-depth-tag--active {
|
||||
color: var(--neon-cyan);
|
||||
border: 1px solid rgba(0, 245, 255, 0.35);
|
||||
border: 1px solid rgba(0, 232, 245, 0.35);
|
||||
}
|
||||
|
||||
.access-depth-tag--skip {
|
||||
@@ -248,7 +248,7 @@
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--neon-cyan);
|
||||
border: 1px solid rgba(0, 245, 255, 0.45);
|
||||
border: 1px solid rgba(0, 232, 245, 0.45);
|
||||
border-radius: 3px;
|
||||
padding: 1px 6px;
|
||||
cursor: help;
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.upgrade-select:focus { border-color: var(--neon-cyan, #00e5ff); }
|
||||
.upgrade-select:focus { border-color: var(--neon-cyan, #00e8f5); }
|
||||
|
||||
.upgrade-btn {
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
aspect-ratio: 1;
|
||||
min-height: 160px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(0, 245, 255, 0.18);
|
||||
border: 1px solid rgba(0, 232, 245, 0.18);
|
||||
background:
|
||||
radial-gradient(ellipse at 50% 45%, rgba(0, 245, 255, 0.06) 0%, transparent 65%),
|
||||
radial-gradient(ellipse at 50% 45%, rgba(0, 232, 245, 0.06) 0%, transparent 65%),
|
||||
rgba(0, 0, 0, 0.45);
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -38,8 +38,8 @@
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 245, 255, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 245, 255, 0.04) 1px, transparent 1px);
|
||||
linear-gradient(rgba(0, 232, 245, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 232, 245, 0.04) 1px, transparent 1px);
|
||||
background-size: 20% 20%;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -94,7 +94,7 @@
|
||||
box-shadow:
|
||||
0 0 0 3px rgba(255, 255, 255, 0.25),
|
||||
0 0 22px var(--dot-color, var(--neon-cyan)),
|
||||
0 0 36px rgba(0, 245, 255, 0.55);
|
||||
0 0 36px rgba(0, 232, 245, 0.55);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.6rem 0.75rem;
|
||||
border: 1px solid rgba(0, 245, 255, 0.15);
|
||||
border: 1px solid rgba(0, 232, 245, 0.15);
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
@@ -142,15 +142,15 @@
|
||||
.agent-action-btn {
|
||||
padding: 0.4rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
border: 1px solid rgba(0, 245, 255, 0.35);
|
||||
background: rgba(0, 245, 255, 0.08);
|
||||
border: 1px solid rgba(0, 232, 245, 0.35);
|
||||
background: rgba(0, 232, 245, 0.08);
|
||||
color: var(--text-primary);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.agent-action-btn:hover {
|
||||
background: rgba(0, 245, 255, 0.18);
|
||||
background: rgba(0, 232, 245, 0.18);
|
||||
}
|
||||
|
||||
.agent-action-btn.danger {
|
||||
@@ -164,7 +164,7 @@
|
||||
overflow: auto;
|
||||
padding: 0.75rem;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border: 1px solid rgba(0, 245, 255, 0.2);
|
||||
border: 1px solid rgba(0, 232, 245, 0.2);
|
||||
border-radius: 6px;
|
||||
font-family: var(--font-mono, monospace);
|
||||
font-size: 0.72rem;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
.lotl-tier-badge--active,
|
||||
.cn-lotl.lotl-active {
|
||||
color: var(--neon-cyan);
|
||||
background: rgba(0, 245, 255, 0.12);
|
||||
border: 1px solid rgba(0, 245, 255, 0.28);
|
||||
background: rgba(0, 232, 245, 0.12);
|
||||
border: 1px solid rgba(0, 232, 245, 0.28);
|
||||
}
|
||||
|
||||
.lotl-tier-badge--fail,
|
||||
@@ -71,7 +71,7 @@
|
||||
.lotl-attempts-block {
|
||||
margin: 0.35rem 0 0.5rem;
|
||||
padding: 0.45rem 0.65rem;
|
||||
border-left: 2px solid rgba(0, 245, 255, 0.35);
|
||||
border-left: 2px solid rgba(0, 232, 245, 0.35);
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border-radius: 0 4px 4px 0;
|
||||
font-family: var(--font-tech);
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
|
||||
.protocol-tunnel-card {
|
||||
border: 1px solid rgba(0, 245, 255, 0.15);
|
||||
border: 1px solid rgba(0, 232, 245, 0.15);
|
||||
border-radius: 6px;
|
||||
padding: 0.65rem 0.75rem;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
|
||||
@@ -18,20 +18,20 @@
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 245, 255, 0.1);
|
||||
border: 1px solid rgba(0, 245, 255, 0.35);
|
||||
background: rgba(0, 232, 245, 0.1);
|
||||
border: 1px solid rgba(0, 232, 245, 0.35);
|
||||
color: var(--neon-cyan);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 0 8px rgba(0, 245, 255, 0.2);
|
||||
box-shadow: 0 0 8px rgba(0, 232, 245, 0.2);
|
||||
transition: background 0.15s, box-shadow 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.help-tip-trigger:hover .help-tip-icon,
|
||||
.help-tip-trigger:focus-visible .help-tip-icon {
|
||||
background: rgba(0, 245, 255, 0.22);
|
||||
border-color: rgba(0, 245, 255, 0.65);
|
||||
box-shadow: 0 0 12px rgba(0, 245, 255, 0.45);
|
||||
background: rgba(0, 232, 245, 0.22);
|
||||
border-color: rgba(0, 232, 245, 0.65);
|
||||
box-shadow: 0 0 12px rgba(0, 232, 245, 0.45);
|
||||
}
|
||||
|
||||
.help-tip-label {
|
||||
@@ -48,11 +48,11 @@
|
||||
line-height: 1.45;
|
||||
color: #e8f4f8;
|
||||
background: rgba(6, 14, 18, 0.97);
|
||||
border: 1px solid rgba(0, 245, 255, 0.45);
|
||||
border: 1px solid rgba(0, 232, 245, 0.45);
|
||||
border-radius: 4px;
|
||||
box-shadow:
|
||||
0 4px 24px rgba(0, 0, 0, 0.55),
|
||||
0 0 16px rgba(0, 245, 255, 0.12);
|
||||
0 0 16px rgba(0, 232, 245, 0.12);
|
||||
pointer-events: auto;
|
||||
animation: help-tip-in 0.12s ease-out;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
width: 260px;
|
||||
background: linear-gradient(180deg, rgba(16, 12, 8, 0.97) 0%, rgba(8, 6, 4, 0.98) 100%);
|
||||
border-right: 1px solid var(--border-brass);
|
||||
box-shadow: 4px 0 40px rgba(0, 0, 0, 0.5), inset -1px 0 0 rgba(0, 245, 255, 0.08);
|
||||
box-shadow: 4px 0 40px rgba(0, 0, 0, 0.5), inset -1px 0 0 rgba(0, 232, 245, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
@@ -156,10 +156,10 @@
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: linear-gradient(90deg, rgba(0, 245, 255, 0.12), transparent);
|
||||
background: linear-gradient(90deg, rgba(0, 232, 245, 0.12), transparent);
|
||||
color: var(--neon-cyan);
|
||||
border-color: rgba(0, 245, 255, 0.35);
|
||||
box-shadow: inset 0 0 20px rgba(0, 245, 255, 0.08);
|
||||
border-color: rgba(0, 232, 245, 0.35);
|
||||
box-shadow: inset 0 0 20px rgba(0, 232, 245, 0.08);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
@@ -402,7 +402,7 @@
|
||||
|
||||
.readout-bar-fill.readout-bar-active {
|
||||
background: linear-gradient(90deg, var(--neon-green), var(--neon-cyan));
|
||||
box-shadow: 0 0 6px rgba(0, 245, 255, 0.35);
|
||||
box-shadow: 0 0 6px rgba(0, 232, 245, 0.35);
|
||||
animation: bar-shimmer 2.5s ease-in-out infinite;
|
||||
background-size: 200% 100%;
|
||||
}
|
||||
@@ -451,7 +451,7 @@
|
||||
|
||||
.sig-ver {
|
||||
font-size: 0.55rem;
|
||||
color: rgba(0, 245, 255, 0.35);
|
||||
color: rgba(0, 232, 245, 0.35);
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
}
|
||||
|
||||
.mobile-bottom-nav-item.active {
|
||||
background: rgba(0, 245, 255, 0.08);
|
||||
background: rgba(0, 232, 245, 0.08);
|
||||
}
|
||||
|
||||
.mobile-menu-backdrop {
|
||||
@@ -171,7 +171,7 @@
|
||||
}
|
||||
|
||||
.mobile-more-link.active {
|
||||
background: rgba(0, 245, 255, 0.1);
|
||||
background: rgba(0, 232, 245, 0.1);
|
||||
color: var(--neon-cyan);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
.sacred-page-header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sacred-page-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(201, 162, 39, 0.5) 15%,
|
||||
rgba(0, 232, 245, 0.35) 50%,
|
||||
rgba(201, 162, 39, 0.5) 85%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.sacred-page-header-motif {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.deck-hero-status + .sacred-page-header-motif,
|
||||
.deck-hero-actions + .sacred-page-header-motif {
|
||||
right: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import SacredPageHeader from './SacredPageHeader';
|
||||
|
||||
vi.mock('../../HelpTip', () => ({
|
||||
HelpTip: ({ field }: { field: string }) => <span data-testid={`help-${field}`}>?</span>,
|
||||
}));
|
||||
|
||||
vi.mock('./motifs', () => ({
|
||||
SacredMotif: () => <svg data-testid="sacred-motif" />,
|
||||
}));
|
||||
|
||||
describe('SacredPageHeader', () => {
|
||||
it('renders deck-hero chrome with eyebrow, title, subtitle, and motif', () => {
|
||||
const { container } = render(
|
||||
<SacredPageHeader
|
||||
eyebrow="NETWORK OPS"
|
||||
title="Path Tracer"
|
||||
subtitle="Multi-hop WireGuard chain."
|
||||
helpField="pt_path_tracer"
|
||||
actions={<button type="button">Go</button>}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.deck-hero.sacred-page-header')).toBeTruthy();
|
||||
expect(screen.getByText('NETWORK OPS')).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('Path Tracer');
|
||||
expect(screen.getByText('Multi-hop WireGuard chain.')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('help-pt_path_tracer')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('sacred-motif')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Go' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { HelpTip } from '../../HelpTip';
|
||||
import { SacredMotif } from './motifs';
|
||||
import './SacredPageHeader.css';
|
||||
|
||||
export interface SacredPageHeaderProps {
|
||||
eyebrow?: string;
|
||||
title: ReactNode;
|
||||
subtitle?: ReactNode;
|
||||
helpField?: string;
|
||||
actions?: ReactNode;
|
||||
status?: ReactNode;
|
||||
className?: string;
|
||||
heroClassName?: string;
|
||||
}
|
||||
|
||||
/** Unified operator-deck page hero — eyebrow, title, subtitle, optional sacred motif. */
|
||||
export default function SacredPageHeader({
|
||||
eyebrow,
|
||||
title,
|
||||
subtitle,
|
||||
helpField,
|
||||
actions,
|
||||
status,
|
||||
className = '',
|
||||
heroClassName = '',
|
||||
}: SacredPageHeaderProps) {
|
||||
return (
|
||||
<header className={`deck-hero sacred-page-header ${heroClassName} ${className}`.trim()}>
|
||||
<div className="deck-hero-text">
|
||||
{eyebrow ? <p className="deck-eyebrow font-tech">{eyebrow}</p> : null}
|
||||
<h1>
|
||||
{title}
|
||||
{helpField ? <> <HelpTip field={helpField} /></> : null}
|
||||
</h1>
|
||||
{subtitle ? <p className="page-subtitle">{subtitle}</p> : null}
|
||||
</div>
|
||||
{actions ? <div className="deck-hero-actions">{actions}</div> : null}
|
||||
{status ? <div className="deck-hero-status">{status}</div> : null}
|
||||
<div className="sacred-page-header-motif" aria-hidden>
|
||||
<SacredMotif name="hex" opacity={0.55} />
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user