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,45 @@
import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
export interface ForgeState {
forging: boolean;
stage: string;
progress: number; // 0100
}
interface ForgeContextValue extends ForgeState {
startForge: () => void;
endForge: () => void;
setStage: (stage: string, progress: number) => void;
}
const ForgeContext = createContext<ForgeContextValue>({
forging: false,
stage: '',
progress: 0,
startForge: () => {},
endForge: () => {},
setStage: () => {},
});
export function ForgeProvider({ children }: { children: ReactNode }) {
const [state, setState] = useState<ForgeState>({ forging: false, stage: '', progress: 0 });
const startForge = useCallback(() =>
setState({ forging: true, stage: 'Initializing forge...', progress: 0 }), []);
const endForge = useCallback(() =>
setState({ forging: false, stage: '', progress: 0 }), []);
const setStage = useCallback((stage: string, progress: number) =>
setState((s) => ({ ...s, stage, progress })), []);
return (
<ForgeContext.Provider value={{ ...state, startForge, endForge, setStage }}>
{children}
</ForgeContext.Provider>
);
}
export function useForge() {
return useContext(ForgeContext);
}

View File

@@ -114,6 +114,7 @@ export function WebSocketProvider({ children }: { children: React.ReactNode }) {
(update.shares_accepted ?? a.shares_good)
),
status: 'online' as const,
...(update.ssh_available !== undefined ? { ssh_available: update.ssh_available } : {}),
}
: a
)