Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 3x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 6x 6x 3x 3x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x | "use client";
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import { AnimatePresence, motion } from "framer-motion";
import { AISummaryPanel } from "./AISummaryPanel";
import { NeuralVoiceHUD } from "./NeuralVoiceHUD";
import type { Insight } from "../analysis/insight";
export const HUDLayerManager: React.FC<{
showAI: boolean;
showVoice: boolean;
onCloseAI: () => void;
onCloseVoice: () => void;
initialInsight?: Insight | null;
}> = ({ showAI, showVoice, onCloseAI, onCloseVoice, initialInsight }) => {
const [rootEl, setRootEl] = useState<HTMLElement | null>(null);
useEffect(() => {
let el = document.getElementById("hud-global-root") as HTMLElement | null;
if (!el) {
el = document.createElement("div");
el.id = "hud-global-root";
Object.assign(el.style, {
position: "fixed",
top: "0",
left: "0",
width: "100vw",
height: "100vh",
zIndex: "1999",
pointerEvents: "none",
});
document.body.appendChild(el);
}
setRootEl(el);
}, []);
if (!rootEl) return null;
const overlay = (
<AnimatePresence>
{(showAI || showVoice) && (
<motion.div
key="hud-overlay"
className="fixed inset-0 flex items-center justify-center"
style={{
backdropFilter: "blur(28px) saturate(180%)",
background:
"linear-gradient(135deg, rgba(0,8,20,0.7), rgba(0,10,25,0.85))",
zIndex: 2000,
pointerEvents: "auto",
}}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{showAI && (
<motion.div
key="ai"
className="relative pointer-events-auto"
style={{ zIndex: 2001 }}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ type: "spring", stiffness: 140, damping: 16 }}
>
<AISummaryPanel
onClose={onCloseAI}
initialInsight={initialInsight}
/>
</motion.div>
)}
{showVoice && (
<motion.div
key="voice"
className="relative pointer-events-auto"
style={{ zIndex: 2001 }}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ type: "spring", stiffness: 140, damping: 16 }}
>
<NeuralVoiceHUD onClose={onCloseVoice} />
</motion.div>
)}
</motion.div>
)}
</AnimatePresence>
);
return ReactDOM.createPortal(overlay, rootEl);
};
|