Spaces:
Running
Running
BRIDGE Platform Agent
feat(platform): BRIDGE Modules hub with /modules/e and /modules/s routed workspaces
1955a0f Download src/modules/ModuleShell.tsx from sitcod3/BRIDGE_Modul-S_prod: direct link, hf CLI and curl.
- Browser
- Download file 5.49 kB
-
https://huggingface.co/spaces/sitcod3/BRIDGE_Modul-S_prod/resolve/main/src/modules/ModuleShell.tsx
- Command line
-
hf download hf://spaces/sitcod3/BRIDGE_Modul-S_prod/src/modules/ModuleShell.tsx
-
curl -L -o ModuleShell.tsx https://huggingface.co/spaces/sitcod3/BRIDGE_Modul-S_prod/resolve/main/src/modules/ModuleShell.tsx
5.49 kB
| import React, { useEffect, useMemo, useRef, useState } from "react"; | |
| import { Outlet } from "react-router-dom"; | |
| import { AnimatePresence, motion } from "motion/react"; | |
| import { ThemeConfig, ThemeContext } from "../context/ThemeContext"; | |
| import GeometricBackground from "../components/GeometricBackground"; | |
| import BackgroundElements from "../components/BackgroundElements"; | |
| import TopBar from "../components/TopBar"; | |
| import PlatformSidebar from "./PlatformSidebar"; | |
| import ModuleLaunchAnimation, { hashModuleId } from "./ModuleLaunchAnimation"; | |
| import { useLaunch } from "./LaunchContext"; | |
| import { MODULES, ModuleId } from "./registry"; | |
| /** | |
| * Nested theme provider — overrides the platform default (mint) with the | |
| * active module palette for the whole module subtree. | |
| */ | |
| function ModuleThemeProvider({ config, children }: { config: ThemeConfig; children: React.ReactNode }) { | |
| const value = useMemo(() => ({ theme: config.id, config }), [config]); | |
| return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>; | |
| } | |
| interface ModuleShellProps { | |
| module: ModuleId; | |
| } | |
| /** | |
| * Route shell for /modules/e and /modules/s. | |
| * | |
| * Launch guard: on every mount (menu launch, hub launch, direct URL) the | |
| * module's real dashboard primitives random-reassemble in slow motion before | |
| * the workspace is revealed. Leaving via the menu plays the reverse-assembly | |
| * exit. Internal navigation never replays the animation (shell stays mounted). | |
| */ | |
| export default function ModuleShell({ module }: ModuleShellProps) { | |
| const def = MODULES[module]; | |
| const { seq, exiting, finishExit } = useLaunch(); | |
| const [launchDone, setLaunchDone] = useState(false); | |
| const [isSidebarOpen, setIsSidebarOpen] = useState(false); | |
| const seedRef = useRef<number>(0); | |
| if (seedRef.current === 0) { | |
| seedRef.current = Date.now() ^ hashModuleId(module) ^ ((seq + 1) * 0x9e3779b9); | |
| } | |
| // A fresh mount is a fresh launch — clear any stale exit flag from the | |
| // previous module. | |
| useEffect(() => { | |
| if (exiting) finishExit(); | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | |
| }, []); | |
| const isExiting = exiting === module; | |
| return ( | |
| <ModuleThemeProvider config={def.theme}> | |
| <div | |
| data-module={module} | |
| data-testid={`module-shell-${module}`} | |
| className={module === "s" ? "bg-module-s min-h-full" : ""} | |
| > | |
| <div className="flex h-screen bg-transparent text-zinc-100 overflow-hidden relative"> | |
| <GeometricBackground /> | |
| <BackgroundElements /> | |
| {/* Mobile overlay */} | |
| {isSidebarOpen && ( | |
| <div | |
| className="fixed inset-0 bg-black/60 z-30 md:hidden backdrop-blur-sm" | |
| onClick={() => setIsSidebarOpen(false)} | |
| /> | |
| )} | |
| {/* Sidebar with mobile slide transition */} | |
| <div | |
| className={`fixed inset-y-0 left-0 z-40 transform transition-transform duration-300 ease-in-out md:relative md:translate-x-0 ${ | |
| isSidebarOpen ? "translate-x-0" : "-translate-x-full" | |
| }`} | |
| > | |
| <PlatformSidebar module={module} onClose={() => setIsSidebarOpen(false)} /> | |
| </div> | |
| <div className="flex flex-col flex-1 min-w-0 overflow-hidden w-full"> | |
| <TopBar module={module} onMenuClick={() => setIsSidebarOpen(true)} /> | |
| <main className="flex-1 overflow-y-auto bg-transparent relative z-10 w-full p-2 md:p-0"> | |
| {/* Launch guard: the animation covers the workspace; pages load | |
| (and prefetch their data) behind the opaque overlay. */} | |
| <div className="relative min-h-full"> | |
| {!isExiting && ( | |
| <React.Suspense fallback={<div className="p-6" />}> | |
| <Outlet /> | |
| </React.Suspense> | |
| )} | |
| <AnimatePresence> | |
| {!launchDone && !isExiting && ( | |
| <motion.div | |
| key="launch-overlay" | |
| className={module === "s" ? "absolute inset-0 z-30 bg-module-s" : "absolute inset-0 z-30 bg-[#05070a]"} | |
| exit={{ opacity: 0, transition: { duration: 0.45, ease: "easeOut" } }} | |
| > | |
| <ModuleLaunchAnimation | |
| module={module} | |
| mode="enter" | |
| seed={seedRef.current} | |
| onDone={() => setLaunchDone(true)} | |
| accent={def.theme.accent} | |
| accentSoft={def.theme.accentText} | |
| /> | |
| </motion.div> | |
| )} | |
| {isExiting && ( | |
| <motion.div | |
| key="exit-overlay" | |
| className={module === "s" ? "absolute inset-0 z-30 bg-module-s" : "absolute inset-0 z-30 bg-[#05070a]"} | |
| > | |
| <ModuleLaunchAnimation | |
| module={module} | |
| mode="exit" | |
| seed={Date.now() ^ hashModuleId(module)} | |
| onDone={() => finishExit()} | |
| accent={def.theme.accent} | |
| accentSoft={def.theme.accentText} | |
| /> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| </main> | |
| </div> | |
| </div> | |
| </div> | |
| </ModuleThemeProvider> | |
| ); | |
| } | |