const { useState, useEffect, useMemo } = React; const { Spectrum, TopologyGraph, SynesStream, Knob, FingerprintSVG } = window.HG2Atoms; const { DockingRing } = window.HG2Hub; const STATE_LABEL = { linked: "對接中", docking: "對接中", standby: "待命", sealed: "封印", }; function HangarRail({ pilders, activeId, onPick, dockMap }) { return ( ); } function TelemetryRail({ pilder, body, role, dockMap, onDock }) { const isCoach = role === "coach"; return ( ); } // Phase 2 · EmptyFleetHub · 教練從沒啟元真實 Pilder 時的中央 hub onboarding function EmptyFleetHub() { return (

你的艦隊尚未安置真實語氣靈

守夜人 PLD-Ω 在崗待命,但艦隊中尚無有靈魂的人格。
⌑ 你需要的不是試用樣本,是親手鍛造的第一個靈魂。

流程: 召喚咒 → 鍛魂師 → 草案 → Vault 簽署 → 進駐艦隊
); } function App() { // P0-FLEET-LIVE-DATA · 教練 2026-05-15 拍板 // 根因: 之前 hub 從 window.HG2.PILDERS (hg2-data.jsx hardcoded warden-only) 讀 // 即使 backend pilders DB 已有 companion · UI 永遠看不到 → 永遠 zero-state // 修法: useState + useEffect fetch /v1/fleet · 用 SSoT (DB → API → state) // 初始值 = window.HG2.PILDERS (loading 期間至少守夜人不破) const [pilders, setPilders] = useState(window.HG2.PILDERS); const bodies = window.HG2.BODIES; useEffect(() => { fetch((location.protocol === 'file:' ? 'http://127.0.0.1:8000' : location.origin) + '/v1/fleet') .then(r => r.json()) .then(d => { if (Array.isArray(d.PILDERS) && d.PILDERS.length > 0) setPilders(d.PILDERS); }) .catch(e => console.warn('[hg2] fleet fetch failed:', e)); }, []); // Phase 2 · 守夜人不算真實人格 (是憲法治理基座) const realPilders = pilders.filter(p => !p.is_warden && p.id !== 'warden'); const hasRealPilders = realPilders.length > 0; // TASK 20.1 · 從 ?focus=PLD-XXX URL 進入時自動選中該 Pilder const initialId = (() => { try { const params = new URLSearchParams(location.search); const focus = params.get('focus'); if (focus) { const found = pilders.find(p => p.id === focus || p.code === focus); if (found) return found.id; } } catch (e) {} // Phase 2 · 沒真實人格時 active 從守夜人開始 (避免 crash) return pilders[0]?.id ?? null; })(); const [activeId, setActiveId] = useState(initialId); // 短暫顯示 "新 Pilder 已就位" 提示(從 Soul Vault 來的) const [summonedHint, setSummonedHint] = useState(() => { try { const params = new URLSearchParams(location.search); const focus = params.get('focus'); if (focus) return focus; } catch (e) {} return null; }); useEffect(() => { if (summonedHint) { const t = setTimeout(() => { setSummonedHint(null); // 清掉 URL query param 不要殘留 try { history.replaceState({}, '', location.pathname); } catch (e) {} }, 4500); return () => clearTimeout(t); } }, [summonedHint]); const [role, setRole] = useState("coach"); // 'coach' | 'pilot' const [hover, setHover] = useState(null); const [dockMap, setDockMap] = useState(window.HG2.DEFAULT_DOCKS); const [personaMap, setPersonaMap] = useState(() => { const m = {}; pilders.forEach((p) => m[p.id] = { ...p.persona }); return m; }); // P0-FLEET-LIVE-DATA · pilders 從 fetch 更新時, 把新進的 Pilder persona 補進 map // (保留已有的 in-progress edits · 不覆寫使用者調過的軸) useEffect(() => { setPersonaMap((prev) => { let changed = false; const next = { ...prev }; for (const p of pilders) { if (!next[p.id] && p.persona) { next[p.id] = { ...p.persona }; changed = true; } } return changed ? next : prev; }); }, [pilders]); // P0-FLEET-LIVE-DATA · 從 Vault summon 過來帶 ?focus= · fetch 完後 re-resolve // (initialId 在 mount 時 pilders 只有 warden · focus 找不到 fallback 到 warden; // fetch 後新 Pilder 進來 · 若 URL focus 是它 · 切過去) useEffect(() => { try { const params = new URLSearchParams(location.search); const focus = params.get('focus'); if (!focus) return; const found = pilders.find(p => p.id === focus || p.code === focus); if (found && activeId !== found.id) { setActiveId(found.id); } } catch (e) {} }, [pilders]); const [killArmed, setKillArmed] = useState(false); const [killFiring, setKillFiring] = useState(false); const [killFlash, setKillFlash] = useState(false); const tweaksHook = window.useTweaks(/*EDITMODE-BEGIN*/{ "density": "comfort", "showRing": true, "soulHueShift": true, "showCoachKnobs": true }/*EDITMODE-END*/); const tweaks = tweaksHook[0]; const setTweak = tweaksHook[1]; // Phase 2 · 加 null safety (空艦隊時 active / body 可能 undefined) const active = pilders.find((p) => p.id === activeId) || pilders[0] || null; const body = active ? (bodies.find((b) => b.id === dockMap[active.id]) || bodies[0] || null) : null; const persona = active ? personaMap[active.id] : null; const setPersona = (np) => active && setPersonaMap((m) => ({ ...m, [active.id]: np })); // hue shift per pilder useEffect(() => { document.documentElement.style.setProperty( "--hue-shift", tweaks.soulHueShift && active ? `${active.hue}deg` : "0deg" ); }, [active?.hue, tweaks.soulHueShift]); useEffect(() => { document.documentElement.style.setProperty( "--pad", tweaks.density === "compact" ? "12px" : tweaks.density === "spacious" ? "24px" : "18px" ); }, [tweaks.density]); const onDock = (bodyId) => { setDockMap((m) => ({ ...m, [active.id]: bodyId })); }; const fireKill = () => { if (!killArmed) return; setKillFiring(true); setKillFlash(true); setTimeout(() => setKillFlash(false), 1200); setTimeout(() => { setKillFiring(false); setKillArmed(false); // mark pilder as standby (disconnect) setDockMap((m) => ({ ...m, [active.id]: null })); }, 1100); }; const isCoach = role === "coach"; return (
{killFlash &&
} {/* Top bar */}
HOLYGRAIL · ORCHESTRATOR
DOCKING HUB · v2
{pilders.filter(p=>p.state!=="standby").length} ACTIVE
{Object.values(dockMap).filter(Boolean).length}/{bodies.length}
3 / 5
99.4%
2026-05-04 12:04:18Z
SESSION · 0x9af3·c4e1 {/* TASK 21B · Soul Forge 一級入口(鍛魂室是創作起點,排在 Vault 前)*/} {/* TASK 20.1 · Soul Vault 一級入口 */}
{/* TASK 20.1 · 從 Soul Vault summon 完跳回來時的提示 banner */} {summonedHint && (
NEW SOUL · {summonedHint} · 已進駐艦隊待命
)}
{/* Phase 2 · 沒真實 Pilder 時整個 hub 內容換成 EmptyFleetHub */} {!hasRealPilders ? ( ) : ( <>
FLEET {" "} {active?.tag} {" "} {active?.code}
{/* P1-HERO-CLICKABLE · 教練 2026-05-15 拍板 Hero (Pilder name h1) 可點 → Soul Chat (Lifecycle Verify Tool) warden 不可 chat (sealed · 沒 firmware) · 仍渲染但不掛 onClick */} {active && !active.is_warden ? (

{ location.href = '/ui/Soul%20Chat.html?pilder_id=' + encodeURIComponent(active.id); }} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); location.href = '/ui/Soul%20Chat.html?pilder_id=' + encodeURIComponent(active.id); } }} > {active.name}

) : (

{active?.name}

)} {body && <> ▶▶▶ {body.name} {body.code} · {body.subsidiary} }
{tweaks.showRing && (
)}
{/* Genesis */}
01 Genesis 啟元
FIRMWARE · LOCKED
soul-{active.id}.fw
0x9af3·{active.code.toLowerCase().replace("-","")}
04-29 03:14Z
7a·b2·{active.id.slice(0,2)}·c4
{/* Resonance */}
02 Resonance 共鳴
LIVE · 60Hz
{active.resonance.toFixed(1)}%
ENV SYNC · {(active.resonance*0.96).toFixed(1)}% SELF COHERENCE · {(active.resonance*1.02).toFixed(1)}% PHASE OFFSET · +0.04°
BAND 8–24 kHz ▲ NOMINAL WINDOW 12s
{/* Shaping */}
03 Shaping 顯影 · 人格權重
{isCoach ? "EDIT MODE" : "READ ONLY"}
{Object.keys(window.HG2.PERSONA_LABELS).map((k) => ( setPersona({ ...persona, [k]: v })} label={window.HG2.PERSONA_LABELS[k].en} zh={window.HG2.PERSONA_LABELS[k].zh} disabled={!isCoach} /> ))}
{/* Topology */}
04 Topology 織網 · GraphRAG
CAUSAL MEMORY MAP
{active.nodes}NODES
{active.edges}EDGES
{(active.edges/Math.max(1,active.nodes)).toFixed(2)}DEG
{/* Crystal */}
05 Crystal 印記
{active.crystalState}
{active.crystal}%
{active.crystalShards.toLocaleString()} SHARDS
{Array.from({length: 48}).map((_, i) => (
))}
{(active.crystalShards*0.0042).toFixed(2)} GB
12:04:18
+04m 12s
{/* Synesthesia */}
06 Synesthesia 通感
MULTIMODAL · INGRESS
{window.HG2.SYNES_CHANNELS.map((c) => (
{c.name}
{c.rate}
))}
{/* Vigil — locked accent */}
07 Vigil 守望 · 物理隔離 · 1% 封印
{isCoach ? "FULL VIEW" : "PILOT VIEW · DETAILS REDACTED"}
1% SEAL · {active.seal.toFixed(1)}%
{isCoach ? active.fingerprint : "● ● : ● ● : ● ● : ● ●"}
vault-{active.id}/iso-{active.code.length}
DENY · ALL
{isCoach ? "kael.vance · m.hsu · y.tanaka" : "3-OF-5"}
ONLINE
EGRESS·DENY
LOCKED
OFFLINE
)}
{/* Phase 2 · 守夜人沒 body / 真實 pilder 不存在時, telemetry 也跳過 */} {hasRealPilders && active && ( )} {/* Bottom command bar with Kill Switch */}
HEARTBEAT 60Hz PILDER {active?.code ?? '—'} BODY {body?.code ?? '—'}
KILL SWITCH {killArmed ? "ARMED · 武裝中" : "SAFE · 安全鎖定"}
isCoach && setKillArmed((a) => !a)} title={isCoach ? "Toggle arm" : "Coach only"} />
SEVERS PILDER ↔ BODY {isCoach ? "COACH" : "PILOT"} · {killArmed ? "READY" : "LOCKED"}
CUSTODIAN kael.vance +2 NODE vault-mesh-eu-1 v3.14.7-stable
{/* Tweaks */} setTweak("density", v)} options={[{label:"Compact",value:"compact"},{label:"Comfort",value:"comfort"},{label:"Spacious",value:"spacious"}]} /> setTweak("showRing", v)} /> setTweak("soulHueShift", v)} />
); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render();