feat: Telegram fleet alerts, forge sigil scramble, UI polish, agent ops
- Calibrate: per-event Telegram/SMTP toggles, test notification, chat ID help - Notify on agent connect/reconnect, offline/hashrate/rejection, forge complete - Sigil scramble post-forge uniquification and Dispense Reveal ceremony - Full system check, desktop push, BITS/host-binary persistence, Path Tracer - Dashboard/Crucible visual polish, haptics, sacred geometry, mobile nav - README documents alerts, sigil scramble, and pack-usb workflow - USB bundle repacked via pack-usb.bat (AetherForge.exe + synced agent source)
This commit is contained in:
221
server/web/src/components/Forge/ForgeDispenseReveal.css
Normal file
221
server/web/src/components/Forge/ForgeDispenseReveal.css
Normal file
@@ -0,0 +1,221 @@
|
||||
.forge-dispense-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(4, 8, 18, 0.82);
|
||||
backdrop-filter: blur(8px);
|
||||
animation: forge-dispense-fade 0.4s ease;
|
||||
}
|
||||
|
||||
.forge-dispense-panel {
|
||||
position: relative;
|
||||
max-width: 420px;
|
||||
width: 92%;
|
||||
padding: 2rem 1.75rem 1.5rem;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(120, 200, 255, 0.35);
|
||||
background: linear-gradient(165deg, rgba(12, 22, 42, 0.97), rgba(6, 12, 28, 0.98));
|
||||
box-shadow:
|
||||
0 0 60px rgba(80, 160, 255, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
text-align: center;
|
||||
animation: forge-dispense-rise 0.55s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.forge-dispense-sigil {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin: 0 auto 1rem;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%, rgba(100, 200, 255, 0.25), transparent 55%),
|
||||
conic-gradient(
|
||||
from 0deg,
|
||||
rgba(100, 180, 255, 0.5),
|
||||
rgba(180, 120, 255, 0.4),
|
||||
rgba(100, 200, 255, 0.5)
|
||||
);
|
||||
mask: radial-gradient(circle, transparent 38%, black 39%);
|
||||
animation: forge-dispense-spin 12s linear infinite;
|
||||
}
|
||||
|
||||
.forge-dispense-title {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: #b8e4ff;
|
||||
}
|
||||
|
||||
.forge-dispense-sub {
|
||||
margin: 0 0 1.25rem;
|
||||
font-size: 0.9rem;
|
||||
color: rgba(200, 220, 255, 0.75);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.forge-dispense-shield {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.forge-dispense-shield-ring {
|
||||
--shield-pct: 50%;
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: conic-gradient(
|
||||
#3d9eff calc(var(--shield-pct) * 1%),
|
||||
rgba(60, 80, 120, 0.35) 0
|
||||
);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.forge-dispense-shield-ring::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 6px;
|
||||
border-radius: 50%;
|
||||
background: rgba(8, 14, 28, 0.95);
|
||||
}
|
||||
|
||||
.forge-dispense-shield-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: #7ec8ff;
|
||||
}
|
||||
|
||||
.forge-dispense-shield-label {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
font-size: 0.85rem;
|
||||
color: rgba(180, 210, 255, 0.9);
|
||||
}
|
||||
|
||||
.forge-dispense-shield-hint {
|
||||
font-size: 0.72rem;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.forge-dispense-dna {
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid rgba(100, 160, 255, 0.2);
|
||||
}
|
||||
|
||||
.forge-dispense-dna-label {
|
||||
display: block;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(150, 200, 255, 0.7);
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.forge-dispense-dna-hash {
|
||||
font-size: 0.95rem;
|
||||
color: #9ee0ff;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.forge-dispense-dna-bars {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
height: 48px;
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.forge-dispense-dna-bar {
|
||||
width: 6px;
|
||||
min-height: 4px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
background: linear-gradient(180deg, #6eb8ff, #3a6a9e);
|
||||
opacity: 0.85;
|
||||
animation: forge-dispense-bar 0.6s ease backwards;
|
||||
}
|
||||
|
||||
.forge-dispense-dna-bar:nth-child(odd) {
|
||||
animation-delay: 0.05s;
|
||||
}
|
||||
|
||||
.forge-dispense-layers {
|
||||
list-style: none;
|
||||
margin: 0 0 1.25rem;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.forge-dispense-layers li {
|
||||
font-size: 0.72rem;
|
||||
padding: 0.25rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(80, 120, 180, 0.35);
|
||||
color: rgba(140, 160, 200, 0.7);
|
||||
}
|
||||
|
||||
.forge-dispense-layers li.on {
|
||||
border-color: rgba(100, 200, 255, 0.55);
|
||||
color: #a8dcff;
|
||||
box-shadow: 0 0 12px rgba(80, 160, 255, 0.2);
|
||||
}
|
||||
|
||||
.forge-dispense-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@keyframes forge-dispense-fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes forge-dispense-rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(24px) scale(0.96);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes forge-dispense-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes forge-dispense-bar {
|
||||
from {
|
||||
transform: scaleY(0);
|
||||
}
|
||||
to {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
91
server/web/src/components/Forge/ForgeDispenseReveal.tsx
Normal file
91
server/web/src/components/Forge/ForgeDispenseReveal.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useEffect } from 'react';
|
||||
import type { BuildResponse } from '../../types';
|
||||
import { useSound } from '../../context/SoundContext';
|
||||
import DownloadButton from '../DownloadButton';
|
||||
import './ForgeDispenseReveal.css';
|
||||
|
||||
interface Props {
|
||||
result: BuildResponse;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function dnaBars(fingerprint?: string): number[] {
|
||||
const raw = fingerprint || 'aetherforge';
|
||||
const bars: number[] = [];
|
||||
for (let i = 0; i < 24; i++) {
|
||||
const c = raw.charCodeAt(i % raw.length);
|
||||
bars.push(12 + ((c * (i + 3)) % 88));
|
||||
}
|
||||
return bars;
|
||||
}
|
||||
|
||||
export default function ForgeDispenseReveal({ result, onClose }: Props) {
|
||||
const { play } = useSound();
|
||||
const score = result.stealth_score ?? 0;
|
||||
const bars = dnaBars(result.binary_fingerprint);
|
||||
|
||||
useEffect(() => {
|
||||
play('success');
|
||||
}, [play]);
|
||||
|
||||
return (
|
||||
<div className="forge-dispense-backdrop" role="dialog" aria-labelledby="forge-dispense-title">
|
||||
<div className="forge-dispense-panel">
|
||||
<div className="forge-dispense-sigil" aria-hidden />
|
||||
<h2 id="forge-dispense-title" className="forge-dispense-title">
|
||||
Dispensed
|
||||
</h2>
|
||||
<p className="forge-dispense-sub">
|
||||
{result.file_name || 'Your worker'} is ready — each forge carries a unique binary signature.
|
||||
</p>
|
||||
|
||||
<div className="forge-dispense-shield">
|
||||
<div
|
||||
className="forge-dispense-shield-ring"
|
||||
style={{ ['--shield-pct' as string]: `${score}%` }}
|
||||
>
|
||||
<span className="forge-dispense-shield-value">{score}</span>
|
||||
</div>
|
||||
<div className="forge-dispense-shield-label">
|
||||
<span>Stealth index</span>
|
||||
<span className="forge-dispense-shield-hint">Polymorph · Garble · Sigil · Sign</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{result.binary_fingerprint && (
|
||||
<div className="forge-dispense-dna">
|
||||
<span className="forge-dispense-dna-label">Binary DNA</span>
|
||||
<code className="forge-dispense-dna-hash">{result.binary_fingerprint}</code>
|
||||
<div className="forge-dispense-dna-bars" aria-hidden>
|
||||
{bars.map((h, i) => (
|
||||
<span key={i} className="forge-dispense-dna-bar" style={{ height: `${h}%` }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ul className="forge-dispense-layers">
|
||||
<li className={result.obfuscated ? 'on' : ''}>Garble obfuscation</li>
|
||||
<li className={result.sigil_scramble ? 'on' : ''}>Sigil scramble</li>
|
||||
<li className={result.signed ? 'on' : ''}>Authenticode sign</li>
|
||||
<li className="on">Polymorph weave</li>
|
||||
</ul>
|
||||
|
||||
<div className="forge-dispense-actions">
|
||||
{result.download_url && result.file_name && (
|
||||
<DownloadButton
|
||||
apiPath={result.download_url}
|
||||
filename={result.file_name}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
↓ Take the forge
|
||||
</DownloadButton>
|
||||
)}
|
||||
<button type="button" className="btn btn-outline" onClick={onClose}>
|
||||
Continue forging
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user