feat: Build Manager, Crucible ops deck, branding, and portable Cloudflare tunnel

Add Build Manager with pin-to-dropper, Crucible multi-node terminal with SSH probe/wake, Command Deck chart balance and pretty stats, AetherForge logo and sacred geometry UI, Field Guide refresh, and LAUNCH.bat Cloudflare MSI + token service install flow.
This commit is contained in:
AetherForge
2026-05-30 22:38:48 -07:00
parent e6b8d84edf
commit 9232f4c448
45 changed files with 4222 additions and 406 deletions

View File

@@ -0,0 +1,136 @@
import { useEffect, useRef } from 'react';
interface Particle {
x: number;
y: number;
vx: number;
vy: number;
life: number; // 1 → 0
size: number;
decay: number;
}
export default function CursorFire() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const particles = useRef<Particle[]>([]);
const mouse = useRef({ x: -9999, y: -9999, moved: false });
const rafRef = useRef<number>(0);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const resize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
resize();
window.addEventListener('resize', resize);
const onMove = (e: MouseEvent) => {
mouse.current = { x: e.clientX, y: e.clientY, moved: true };
};
window.addEventListener('mousemove', onMove);
const emit = () => {
const { x, y } = mouse.current;
// Emit 6 particles per frame at cursor
for (let i = 0; i < 6; i++) {
const spread = 8;
particles.current.push({
x: x + (Math.random() - 0.5) * spread,
y: y + (Math.random() - 0.5) * (spread * 0.5),
vx: (Math.random() - 0.5) * 1.2,
vy: -(Math.random() * 2.8 + 1.8),
life: 1,
size: Math.random() * 14 + 7,
decay: Math.random() * 0.022 + 0.016,
});
}
// Cap particle count for perf
if (particles.current.length > 400) {
particles.current = particles.current.slice(-400);
}
};
const draw = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Additive blending makes overlapping particles look white-hot
ctx.globalCompositeOperation = 'screen';
emit();
const alive: Particle[] = [];
for (const p of particles.current) {
// Turbulent horizontal drift
p.vx += (Math.random() - 0.5) * 0.35;
// Slight drag on vx
p.vx *= 0.97;
// Upward acceleration (heat rises)
p.vy -= 0.04;
p.x += p.vx;
p.y += p.vy;
p.life -= p.decay;
// Particles shrink as they cool
p.size *= 0.982;
if (p.life <= 0 || p.size < 1) continue;
alive.push(p);
const l = p.life;
// Color temperature: white-yellow core → orange → red → dark red
let r: number, g: number, b: number;
if (l > 0.75) {
// White-hot
r = 255; g = 255; b = Math.round((l - 0.75) / 0.25 * 220);
} else if (l > 0.5) {
// Yellow-orange
r = 255; g = Math.round(100 + (l - 0.5) / 0.25 * 155); b = 0;
} else if (l > 0.25) {
// Orange-red
r = 255; g = Math.round((l - 0.25) / 0.25 * 100); b = 0;
} else {
// Deep red, fading
r = Math.round(160 + l / 0.25 * 95); g = 0; b = 0;
}
const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size);
grad.addColorStop(0, `rgba(${r},${g},${b},${l})`);
grad.addColorStop(0.4, `rgba(${r},${Math.round(g * 0.6)},0,${l * 0.6})`);
grad.addColorStop(1, `rgba(0,0,0,0)`);
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
}
particles.current = alive;
rafRef.current = requestAnimationFrame(draw);
};
rafRef.current = requestAnimationFrame(draw);
return () => {
cancelAnimationFrame(rafRef.current);
window.removeEventListener('resize', resize);
window.removeEventListener('mousemove', onMove);
};
}, []);
return (
<canvas
ref={canvasRef}
style={{
position: 'fixed',
inset: 0,
pointerEvents: 'none',
zIndex: 9998,
}}
/>
);
}

View File

@@ -369,3 +369,120 @@
grid-template-columns: 1fr;
}
}
/* ── Guide page additions ─────────────────────────────────────────────────── */
.guide-step-sub {
font-size: 0.82em;
font-weight: 400;
color: var(--text-secondary);
}
.guide-step-btn {
flex-shrink: 0;
align-self: flex-start;
margin-top: 0.15rem;
}
/* Code block with copy button */
.guide-code-block {
position: relative;
margin-top: 0.6rem;
border-radius: 5px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(201, 162, 39, 0.22);
overflow: hidden;
}
.guide-code-pre {
margin: 0;
padding: 0.65rem 3.5rem 0.65rem 0.9rem;
font-family: 'Courier New', monospace;
font-size: 0.75rem;
line-height: 1.55;
color: #b8e0d0;
white-space: pre-wrap;
word-break: break-all;
}
.guide-code-copy {
position: absolute;
top: 0.35rem;
right: 0.35rem;
font-size: 0.65rem;
font-family: var(--font-tech, monospace);
letter-spacing: 0.06em;
padding: 0.15rem 0.45rem;
background: rgba(0, 245, 255, 0.09);
border: 1px solid rgba(0, 245, 255, 0.3);
border-radius: 3px;
color: var(--neon-cyan, #0ff);
cursor: pointer;
transition: background 0.15s;
}
.guide-code-copy:hover {
background: rgba(0, 245, 255, 0.2);
}
/* Network topology ASCII diagram */
.guide-topology {
margin: 0.5rem 0;
border-radius: 5px;
background: rgba(0, 0, 0, 0.45);
border: 1px solid rgba(201, 162, 39, 0.18);
overflow-x: auto;
}
.guide-topology-pre {
margin: 0;
padding: 0.85rem 1rem;
font-family: 'Courier New', monospace;
font-size: 0.72rem;
line-height: 1.6;
color: #c9a227;
white-space: pre;
}
/* Dropper one-liner quick-ref grid */
.guide-dropper-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 0.75rem;
margin-top: 0.25rem;
}
.guide-dropper-item {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.guide-dropper-os {
font-family: 'Courier New', monospace;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--neon-amber, #ffc060);
}
/* Inline code links */
.guide-link {
color: var(--neon-cyan, #0ff);
text-decoration: underline;
text-underline-offset: 2px;
}
.guide-link:hover {
color: #fff;
}
@media (max-width: 640px) {
.guide-dropper-grid {
grid-template-columns: 1fr;
}
.guide-topology-pre {
font-size: 0.6rem;
}
}