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:
AetherForge
2026-06-03 20:32:59 -07:00
parent 03937edba7
commit d52479c9a6
139 changed files with 10611 additions and 369 deletions

View File

@@ -125,6 +125,7 @@ export default function CursorFire() {
return (
<canvas
ref={canvasRef}
className="cursor-fire-fx"
style={{
position: 'fixed',
inset: 0,

View File

@@ -364,6 +364,24 @@
color: var(--neon-green);
}
@media (max-width: 768px) {
.system-status-bar {
flex-wrap: nowrap;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.system-status-bar::-webkit-scrollbar {
display: none;
}
.status-pill {
flex-shrink: 0;
white-space: nowrap;
}
}
@media (max-width: 640px) {
.compare-grid {
grid-template-columns: 1fr;

View File

@@ -73,12 +73,12 @@ interface ActivityPulseProps {
items: { id: string; label: string; ok: boolean; time?: string }[];
}
export function ActivityPulse({ items }: ActivityPulseProps) {
export function ActivityPulse({ items, sample = false }: ActivityPulseProps & { sample?: boolean }) {
if (items.length === 0) {
return <p className="activity-empty font-tech">Awaiting fleet activity</p>;
}
return (
<div className="activity-pulse">
<div className={`activity-pulse${sample ? ' sample-activity' : ''}`}>
{items.slice(0, 12).map((item) => (
<div key={item.id} className={`activity-blip ${item.ok ? 'ok' : 'bad'}`} title={item.time || item.label}>
<span className="activity-blip-core" />

View File

@@ -0,0 +1,29 @@
import { SacredMotif, type MotifName } from './motifs';
const ACCENT_MOTIFS: Record<string, [MotifName, MotifName]> = {
cyan: ['hex', 'seed'],
magenta: ['vesica', 'yantra'],
amber: ['spiral', 'key'],
green: ['seed', 'hex'],
purple: ['metatron', 'torus'],
brass: ['flower', 'key'],
gold: ['yantra', 'spiral'],
};
/** Corner watermarks inside neon cards */
export default function SacredCardWatermark({ accent = 'brass' }: { accent?: string }) {
const [tl, br] = ACCENT_MOTIFS[accent] ?? ACCENT_MOTIFS.brass;
return (
<>
<div className="neon-card-sacred neon-card-sacred--tl" aria-hidden>
<SacredMotif name={tl} opacity={0.5} />
</div>
<div className="neon-card-sacred neon-card-sacred--tr" aria-hidden>
<SacredMotif name="seed" opacity={0.35} />
</div>
<div className="neon-card-sacred neon-card-sacred--br" aria-hidden>
<SacredMotif name={br} opacity={0.45} />
</div>
</>
);
}

View File

@@ -0,0 +1,17 @@
/* Layer-specific overrides (shared tokens in sacred-geometry.css) */
.sacred-layer__corner--mid-l {
transform: translateY(-50%);
}
.sacred-layer__corner--mid-r {
transform: translateY(-50%);
}
.sacred-layer__corner--mid-l svg,
.sacred-layer__corner--mid-r svg {
animation: sacred-geo-rotate 220s linear infinite;
}
.sacred-layer__corner--mid-r svg {
animation-direction: reverse;
}

View File

@@ -0,0 +1,50 @@
import { SacredMotif } from './motifs';
import './SacredGeometryLayer.css';
/** Viewport-wide sacred geometry — corners, keys, wisdom rail */
export default function SacredGeometryLayer() {
return (
<div className="sacred-layer" aria-hidden>
<div className="sacred-layer__corner sacred-layer__corner--tl">
<SacredMotif name="metatron" opacity={0.55} />
</div>
<div className="sacred-layer__corner sacred-layer__corner--tr">
<SacredMotif name="yantra" opacity={0.5} />
</div>
<div className="sacred-layer__corner sacred-layer__corner--bl">
<SacredMotif name="spiral" opacity={0.5} />
</div>
<div className="sacred-layer__corner sacred-layer__corner--br">
<SacredMotif name="key" opacity={0.55} />
</div>
<div className="sacred-layer__corner sacred-layer__corner--mid-l">
<SacredMotif name="vesica" opacity={0.45} />
</div>
<div className="sacred-layer__corner sacred-layer__corner--mid-r">
<SacredMotif name="torus" opacity={0.45} />
</div>
<div className="sacred-layer__keys">
<span className="sacred-key sacred-key--1" title="Shell index">
</span>
<span className="sacred-key sacred-key--2" title="Ley ledger">
</span>
<span className="sacred-key sacred-key--3" title="Root chord">
</span>
<span className="sacred-key sacred-key--4" title="Archive gate">
ψ
</span>
</div>
<div className="sacred-layer__wisdom-rail font-tech" title="Whispers from the deck">
<span>Ω</span>
<span></span>
<span></span>
<span></span>
</div>
</div>
);
}

View File

@@ -0,0 +1,20 @@
import type { ReactNode } from 'react';
import { SacredMotif } from './motifs';
/** Optional sacred divider + motif beside page titles */
export default function SacredPageHeader({
children,
className = '',
}: {
children: ReactNode;
className?: string;
}) {
return (
<header className={`page-header page-header--sacred ${className}`.trim()}>
{children}
<div className="page-header-sacred-motif" aria-hidden>
<SacredMotif name="hex" opacity={0.55} />
</div>
</header>
);
}

View File

@@ -0,0 +1,292 @@
/** Reusable sacred-geometry SVG motifs — decorative only (aria-hidden at call sites). */
type MotifProps = {
className?: string;
stroke?: string;
opacity?: number;
};
const DEFAULT_STROKE = '#c9a227';
export function FlowerOfLifeWatermark({ className = '', stroke = DEFAULT_STROKE, opacity = 0.55 }: MotifProps) {
const cx = 50;
const cy = 50;
const R = 32;
const petalAngles = [0, 60, 120, 180, 240, 300];
const petals = petalAngles.map((deg) => {
const rad = (deg * Math.PI) / 180;
return { x: cx + R * Math.cos(rad), y: cy + R * Math.sin(rad) };
});
const starR = R * 1.15;
const starPts = Array.from({ length: 5 }, (_, i) => {
const rad = ((i * 72 - 90) * Math.PI) / 180;
return { x: cx + starR * Math.cos(rad), y: cy + starR * Math.sin(rad) };
});
const starPath = starPts.map((p, i) => (i === 0 ? `M${p.x},${p.y}` : `L${p.x},${p.y}`)).join(' ') + ' Z';
const triR = R * 0.7;
const triUp = [0, 120, 240]
.map((d) => {
const rad = ((d - 90) * Math.PI) / 180;
return `${cx + triR * Math.cos(rad)},${cy + triR * Math.sin(rad)}`;
})
.join(' ');
const triDown = [60, 180, 300]
.map((d) => {
const rad = ((d - 90) * Math.PI) / 180;
return `${cx + triR * Math.cos(rad)},${cy + triR * Math.sin(rad)}`;
})
.join(' ');
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity}>
<circle cx={cx} cy={cy} r={R * 1.35} fill="none" stroke={stroke} strokeWidth="0.18" strokeDasharray="1.2 1.8" />
<circle cx={cx} cy={cy} r={R} fill="none" stroke={stroke} strokeWidth="0.22" />
<circle cx={cx} cy={cy} r={R * 0.5} fill="none" stroke={stroke} strokeWidth="0.18" strokeDasharray="0.6 1.2" />
{petals.map((p, i) => (
<circle key={i} cx={p.x} cy={p.y} r={R} fill="none" stroke={stroke} strokeWidth="0.16" opacity="0.7" />
))}
<path d={starPath} fill="none" stroke="#ff8c00" strokeWidth="0.2" strokeLinejoin="round" opacity="0.6" />
<polygon points={triUp} fill="none" stroke={stroke} strokeWidth="0.2" opacity="0.8" />
<polygon points={triDown} fill="none" stroke={stroke} strokeWidth="0.2" opacity="0.8" />
<circle cx={cx} cy={cy} r="0.6" fill={stroke} opacity="0.9" />
{starPts.map((p, i) => (
<line key={i} x1={cx} y1={cy} x2={p.x} y2={p.y} stroke={stroke} strokeWidth="0.1" opacity="0.35" />
))}
</g>
</svg>
);
}
export function SeedOfLife({ className = '', stroke = DEFAULT_STROKE, opacity = 0.5 }: MotifProps) {
const cx = 50;
const cy = 50;
const r = 14;
const centers = [{ x: cx, y: cy }];
for (let i = 0; i < 6; i++) {
const a = (i * 60 * Math.PI) / 180;
centers.push({ x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) });
}
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity}>
<circle cx={cx} cy={cy} r={r * 2.2} fill="none" stroke={stroke} strokeWidth="0.2" strokeDasharray="0.8 1.4" />
{centers.map((c, i) => (
<circle key={i} cx={c.x} cy={c.y} r={r} fill="none" stroke={stroke} strokeWidth="0.18" />
))}
</g>
</svg>
);
}
export function MetatronCube({ className = '', stroke = DEFAULT_STROKE, opacity = 0.45 }: MotifProps) {
const cx = 50;
const cy = 50;
const r = 28;
const pts: { x: number; y: number }[] = [];
for (let i = 0; i < 6; i++) {
const a = ((i * 60 - 90) * Math.PI) / 180;
pts.push({ x: cx + r * Math.cos(a), y: cy + r * Math.sin(a) });
}
const inner = r * 0.55;
const innerPts: { x: number; y: number }[] = [];
for (let i = 0; i < 6; i++) {
const a = ((i * 60 - 90) * Math.PI) / 180;
innerPts.push({ x: cx + inner * Math.cos(a), y: cy + inner * Math.sin(a) });
}
const lines: [number, number][] = [];
for (let i = 0; i < pts.length; i++) {
for (let j = i + 1; j < pts.length; j++) lines.push([i, j]);
}
for (let i = 0; i < innerPts.length; i++) {
for (let j = i + 1; j < innerPts.length; j++) lines.push([i + 6, j + 6]);
}
pts.forEach((_, i) => lines.push([i, i + 6]));
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} stroke={stroke} strokeWidth="0.14" fill="none">
<circle cx={cx} cy={cy} r={r * 1.05} strokeWidth="0.16" />
<circle cx={cx} cy={cy} r={inner} strokeDasharray="0.5 1" />
{lines.map(([a, b], i) => {
const p1 = a < 6 ? pts[a] : innerPts[a - 6];
const p2 = b < 6 ? pts[b] : innerPts[b - 6];
return <line key={i} x1={p1.x} y1={p1.y} x2={p2.x} y2={p2.y} opacity="0.5" />;
})}
{[...pts, ...innerPts].map((p, i) => (
<circle key={`d${i}`} cx={p.x} cy={p.y} r="0.5" fill={stroke} stroke="none" />
))}
</g>
</svg>
);
}
export function VesicaPiscis({ className = '', stroke = DEFAULT_STROKE, opacity = 0.5 }: MotifProps) {
const cx = 50;
const cy = 50;
const r = 22;
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke}>
<circle cx={cx - r * 0.5} cy={cy} r={r} strokeWidth="0.2" />
<circle cx={cx + r * 0.5} cy={cy} r={r} strokeWidth="0.2" />
<path
d={`M ${cx} ${cy - r * 0.87} A ${r * 0.5} ${r * 0.87} 0 0 1 ${cx} ${cy + r * 0.87} A ${r * 0.5} ${r * 0.87} 0 0 1 ${cx} ${cy - r * 0.87}`}
stroke="#00f5ff"
strokeWidth="0.16"
opacity="0.6"
/>
<line x1={cx} y1={cy - r} x2={cx} y2={cy + r} strokeWidth="0.12" opacity="0.35" />
</g>
</svg>
);
}
export function HexLattice({ className = '', stroke = DEFAULT_STROKE, opacity = 0.35 }: MotifProps) {
const hex = (cx: number, cy: number, s: number) => {
const pts = Array.from({ length: 6 }, (_, i) => {
const a = ((60 * i - 30) * Math.PI) / 180;
return `${cx + s * Math.cos(a)},${cy + s * Math.sin(a)}`;
}).join(' ');
return <polygon key={`${cx}-${cy}`} points={pts} />;
};
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke} strokeWidth="0.12">
{hex(50, 50, 18)}
{hex(50, 32, 10)}
{hex(50, 68, 10)}
{hex(34, 41, 10)}
{hex(66, 41, 10)}
{hex(34, 59, 10)}
{hex(66, 59, 10)}
</g>
</svg>
);
}
export function GoldenSpiral({ className = '', stroke = DEFAULT_STROKE, opacity = 0.4 }: MotifProps) {
const cx = 18;
const cy = 82;
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke} strokeWidth="0.18">
<rect x="18" y="18" width="64" height="64" strokeDasharray="1 2" opacity="0.35" />
<path
d="M 18 82 A 64 64 0 0 1 82 18 A 40 40 0 0 1 58 42 A 24 24 0 0 1 42 58 A 14 14 0 0 1 50 50"
stroke="#ffb020"
/>
<circle cx={cx} cy={cy} r="1" fill={stroke} />
</g>
</svg>
);
}
export function TorusRings({ className = '', stroke = DEFAULT_STROKE, opacity = 0.42 }: MotifProps) {
const cx = 50;
const cy = 50;
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke}>
{[34, 26, 18, 10].map((r, i) => (
<ellipse
key={r}
cx={cx}
cy={cy}
rx={r}
ry={r * (0.55 + i * 0.08)}
strokeWidth="0.16"
transform={`rotate(${i * 12} ${cx} ${cy})`}
/>
))}
<circle cx={cx} cy={cy} r="2" fill={stroke} opacity="0.8" />
</g>
</svg>
);
}
/** Whimsical “key to knowledge” — geometric bow + shaft */
export function KnowledgeKey({ className = '', stroke = DEFAULT_STROKE, opacity = 0.55 }: MotifProps) {
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke} strokeLinecap="round" strokeLinejoin="round">
<circle cx="38" cy="38" r="16" strokeWidth="0.35" />
<circle cx="38" cy="38" r="8" stroke="#00f5ff" strokeWidth="0.25" />
<polygon
points="38,22 46,30 38,38 30,30"
strokeWidth="0.2"
fill="rgba(201,162,39,0.08)"
/>
<line x1="50" y1="50" x2="88" y2="88" strokeWidth="0.35" />
<rect x="72" y="72" width="8" height="8" strokeWidth="0.22" transform="rotate(45 76 76)" />
<rect x="80" y="64" width="6" height="6" strokeWidth="0.2" transform="rotate(45 83 67)" />
<path d="M 62 62 L 68 56 M 70 70 L 76 64" strokeWidth="0.18" opacity="0.7" />
</g>
</svg>
);
}
export function SriYantraLite({ className = '', stroke = DEFAULT_STROKE, opacity = 0.4 }: MotifProps) {
const cx = 50;
const cy = 52;
const tri = (r: number, flip: boolean) => {
const pts = [0, 120, 240].map((d) => {
const rad = ((d - 90) * Math.PI) / 180;
const y = (flip ? -1 : 1) * r * Math.sin(rad);
return `${cx + r * Math.cos(rad)},${cy + y}`;
});
return pts.join(' ');
};
return (
<svg className={className} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g opacity={opacity} fill="none" stroke={stroke} strokeWidth="0.16">
<circle cx={cx} cy={cy} r="38" strokeDasharray="1.5 2" />
<polygon points={tri(32, false)} />
<polygon points={tri(24, true)} stroke="#ff2da6" opacity="0.65" />
<polygon points={tri(16, false)} stroke="#00f5ff" opacity="0.55" />
<circle cx={cx} cy={cy} r="1.2" fill={stroke} />
</g>
</svg>
);
}
export type MotifName =
| 'flower'
| 'seed'
| 'metatron'
| 'vesica'
| 'hex'
| 'spiral'
| 'torus'
| 'key'
| 'yantra';
export function SacredMotif({
name,
className = '',
stroke,
opacity,
}: MotifProps & { name: MotifName }) {
const props = { className, stroke, opacity };
switch (name) {
case 'seed':
return <SeedOfLife {...props} />;
case 'metatron':
return <MetatronCube {...props} />;
case 'vesica':
return <VesicaPiscis {...props} />;
case 'hex':
return <HexLattice {...props} />;
case 'spiral':
return <GoldenSpiral {...props} />;
case 'torus':
return <TorusRings {...props} />;
case 'key':
return <KnowledgeKey {...props} />;
case 'yantra':
return <SriYantraLite {...props} />;
case 'flower':
default:
return <FlowerOfLifeWatermark {...props} />;
}
}