🚀 Auto-deploy: melhoria no snap e medição AR em 25/05/2026 01:10:53
This commit is contained in:
+153
-140
@@ -43,7 +43,7 @@ import { toast } from 'sonner';
|
|||||||
import {
|
import {
|
||||||
Users, MessageSquare, Mic, MicOff, Home, Send,
|
Users, MessageSquare, Mic, MicOff, Home, Send,
|
||||||
ArrowRight, ChevronLeft, ChevronRight, Share2, LogOut,
|
ArrowRight, ChevronLeft, ChevronRight, Share2, LogOut,
|
||||||
RotateCw, Award, ShieldAlert
|
RotateCw, Award, ShieldAlert, Maximize2, Minimize2
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
// Interfaces tipadas para evitar any
|
// Interfaces tipadas para evitar any
|
||||||
@@ -381,10 +381,21 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
|
|||||||
const { scene } = useGLTF(model.url);
|
const { scene } = useGLTF(model.url);
|
||||||
const clone = useMemo(() => scene.clone(), [scene]);
|
const clone = useMemo(() => scene.clone(), [scene]);
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
|
const baseScaleRef = useRef<number>(1);
|
||||||
|
const centerOffsetRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||||
|
|
||||||
// Calcula escala de aproximadamente 1 metro para caber na mesa
|
// Calcula escala de aproximadamente 1 metro para caber na mesa (apenas UMA vez no carregamento)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!groupRef.current) return;
|
if (!groupRef.current) return;
|
||||||
|
|
||||||
|
// Salva transformações antigas
|
||||||
|
const oldRot = clone.rotation.clone();
|
||||||
|
const oldScale = clone.scale.clone();
|
||||||
|
|
||||||
|
// Reseta temporariamente para obter o bounding box original
|
||||||
|
clone.rotation.set(0, 0, 0);
|
||||||
|
clone.scale.setScalar(1);
|
||||||
|
|
||||||
const box = new THREE.Box3().setFromObject(clone);
|
const box = new THREE.Box3().setFromObject(clone);
|
||||||
const size = new THREE.Vector3();
|
const size = new THREE.Vector3();
|
||||||
box.getSize(size);
|
box.getSize(size);
|
||||||
@@ -393,12 +404,19 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
|
|||||||
if (maxDim > 0) {
|
if (maxDim > 0) {
|
||||||
const targetSize = 0.95;
|
const targetSize = 0.95;
|
||||||
const scaleFactor = targetSize / maxDim;
|
const scaleFactor = targetSize / maxDim;
|
||||||
groupRef.current.scale.setScalar(scaleFactor);
|
baseScaleRef.current = scaleFactor;
|
||||||
|
|
||||||
const center = new THREE.Vector3();
|
const center = new THREE.Vector3();
|
||||||
box.getCenter(center);
|
box.getCenter(center);
|
||||||
clone.position.copy(center).multiplyScalar(-scaleFactor);
|
centerOffsetRef.current.copy(center).multiplyScalar(-scaleFactor);
|
||||||
clone.position.y += 0.02;
|
centerOffsetRef.current.y += 0.02;
|
||||||
|
|
||||||
|
clone.position.copy(centerOffsetRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restaura
|
||||||
|
clone.rotation.copy(oldRot);
|
||||||
|
clone.scale.copy(oldScale);
|
||||||
}, [clone]);
|
}, [clone]);
|
||||||
|
|
||||||
// Aplica transformações de Rotação e Escala compartilhadas
|
// Aplica transformações de Rotação e Escala compartilhadas
|
||||||
@@ -411,14 +429,7 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
|
|||||||
groupRef.current.rotation.z = rotation[2];
|
groupRef.current.rotation.z = rotation[2];
|
||||||
}
|
}
|
||||||
if (scale) {
|
if (scale) {
|
||||||
const box = new THREE.Box3().setFromObject(clone);
|
groupRef.current.scale.setScalar(baseScaleRef.current * scale);
|
||||||
const size = new THREE.Vector3();
|
|
||||||
box.getSize(size);
|
|
||||||
const maxDim = Math.max(size.x, size.y, size.z);
|
|
||||||
if (maxDim > 0) {
|
|
||||||
const baseScale = 0.95 / maxDim;
|
|
||||||
groupRef.current.scale.setScalar(baseScale * scale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -473,131 +484,114 @@ function HologramModel({ model, presentationState }: { model: { url: string }; p
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cenário da Sala de Reunião Clara
|
// Cenário da Sala de Reunião Clara
|
||||||
function ThreeMeetingRoomScene({ currentActiveModel, presentationState }: { currentActiveModel: { url: string; fileName: string } | null; presentationState: PresentationState }) {
|
function ThreeMeetingRoomScene({ currentActiveModel, presentationState, isFocusMode }: { currentActiveModel: { url: string; fileName: string } | null; presentationState: PresentationState; isFocusMode: boolean }) {
|
||||||
const tableGeom = useMemo(() => {
|
|
||||||
// Geometria da mesa em U
|
|
||||||
const shape = new THREE.Shape();
|
|
||||||
shape.moveTo(-1.6, -1.1);
|
|
||||||
shape.lineTo(1.6, -1.1);
|
|
||||||
shape.quadraticCurveTo(1.9, -1.1, 1.9, -0.8);
|
|
||||||
shape.lineTo(1.9, 0.8);
|
|
||||||
shape.quadraticCurveTo(1.9, 1.1, 1.6, 1.1);
|
|
||||||
shape.lineTo(-1.6, 1.1);
|
|
||||||
shape.quadraticCurveTo(-1.9, 1.1, -1.9, 0.8);
|
|
||||||
shape.lineTo(-1.9, -0.8);
|
|
||||||
shape.quadraticCurveTo(-1.9, -1.1, -1.6, -1.1);
|
|
||||||
|
|
||||||
const hole = new THREE.Path();
|
|
||||||
hole.absellipse(0, 0, 0.9, 0.45, 0, Math.PI * 2, true);
|
|
||||||
shape.holes.push(hole);
|
|
||||||
|
|
||||||
return new THREE.ExtrudeGeometry(shape, { depth: 0.06, bevelEnabled: true, bevelThickness: 0.01, bevelSize: 0.01, bevelSegments: 3 });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group>
|
<group>
|
||||||
{/* Piso Clara */}
|
{/* Luzes da Sala / Foco */}
|
||||||
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
|
<ambientLight intensity={isFocusMode ? 0.7 : 0.8} />
|
||||||
<planeGeometry args={[12, 8]} />
|
<directionalLight position={isFocusMode ? [5, 5, 5] : [4, 6, 4]} intensity={1.5} castShadow={!isFocusMode} />
|
||||||
<meshStandardMaterial color="#e2e8f0" roughness={0.4} metalness={0.1} />
|
|
||||||
</mesh>
|
|
||||||
|
|
||||||
{/* Tapete Central */}
|
{isFocusMode && (
|
||||||
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.001, 0]}>
|
<color attach="background" args={['#090d16']} />
|
||||||
<planeGeometry args={[4.8, 3.2]} />
|
|
||||||
<meshStandardMaterial color="#94a3b8" roughness={0.7} />
|
|
||||||
</mesh>
|
|
||||||
|
|
||||||
{/* Paredes Claras */}
|
|
||||||
<mesh position={[0, 2, -4]}>
|
|
||||||
<planeGeometry args={[12, 4]} />
|
|
||||||
<meshStandardMaterial color="#f1f5f9" roughness={0.9} />
|
|
||||||
</mesh>
|
|
||||||
{/* Janelas Panorâmicas */}
|
|
||||||
<mesh position={[-6, 2, 0]} rotation={[0, Math.PI / 2, 0]}>
|
|
||||||
<planeGeometry args={[8, 4]} />
|
|
||||||
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
|
|
||||||
</mesh>
|
|
||||||
<mesh position={[6, 2, 0]} rotation={[0, -Math.PI / 2, 0]}>
|
|
||||||
<planeGeometry args={[8, 4]} />
|
|
||||||
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
|
|
||||||
</mesh>
|
|
||||||
|
|
||||||
{/* Mesa Elíptica em U (Madeira Mel Carvalho Clara) */}
|
|
||||||
<group position={[0, 0.8, 0]} rotation={[Math.PI / 2, 0, 0]}>
|
|
||||||
<mesh geometry={tableGeom}>
|
|
||||||
<meshStandardMaterial color="#d97706" roughness={0.2} metalness={0.1} />
|
|
||||||
</mesh>
|
|
||||||
</group>
|
|
||||||
|
|
||||||
{/* Pés da mesa (Cromados) */}
|
|
||||||
{[-1.3, 1.3].map((x) =>
|
|
||||||
[-0.7, 0.7].map((z) => (
|
|
||||||
<mesh key={`${x}-${z}`} position={[x, 0.4, z]}>
|
|
||||||
<cylinderGeometry args={[0.035, 0.035, 0.8]} />
|
|
||||||
<meshStandardMaterial color="#d4d4d8" metalness={0.9} roughness={0.1} />
|
|
||||||
</mesh>
|
|
||||||
))
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Projetor de Holograma (Centro da mesa) */}
|
{!isFocusMode && (
|
||||||
<group position={[0, 0.78, 0]}>
|
<>
|
||||||
<mesh rotation={[-Math.PI / 2, 0, 0]}>
|
{/* Piso Clara */}
|
||||||
<cylinderGeometry args={[0.26, 0.28, 0.05, 32]} />
|
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0, 0]}>
|
||||||
<meshStandardMaterial color="#1e293b" metalness={0.8} roughness={0.2} />
|
<planeGeometry args={[12, 8]} />
|
||||||
</mesh>
|
<meshStandardMaterial color="#e2e8f0" roughness={0.4} metalness={0.1} />
|
||||||
<mesh position={[0, 0.026, 0]} rotation={[-Math.PI / 2, 0, 0]}>
|
</mesh>
|
||||||
<ringGeometry args={[0, 0.22, 32]} />
|
|
||||||
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.7} />
|
{/* Tapete Central */}
|
||||||
</mesh>
|
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, 0.001, 0]}>
|
||||||
<mesh position={[0, 0.45, 0]}>
|
<planeGeometry args={[4.8, 3.2]} />
|
||||||
<cylinderGeometry args={[0.28, 0.2, 0.9, 32, 1, true]} />
|
<meshStandardMaterial color="#94a3b8" roughness={0.7} />
|
||||||
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.15} side={THREE.DoubleSide} />
|
</mesh>
|
||||||
</mesh>
|
|
||||||
|
{/* Paredes Claras */}
|
||||||
{/* Modelo 3D da Maquete Holográfica */}
|
<mesh position={[0, 2, -4]}>
|
||||||
<group position={[0, 0.45, 0]}>
|
<planeGeometry args={[12, 4]} />
|
||||||
<ModelErrorBoundary fallback={
|
<meshStandardMaterial color="#f1f5f9" roughness={0.9} />
|
||||||
// Se falhar ao carregar o modelo (ex: blob local do host inacessível por convidados), renderiza o holograma geométrico
|
</mesh>
|
||||||
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
{/* Janelas Panorâmicas */}
|
||||||
<mesh position={[0, 0, 0]}>
|
<mesh position={[-6, 2, 0]} rotation={[0, Math.PI / 2, 0]}>
|
||||||
<octahedronGeometry args={[0.22]} />
|
<planeGeometry args={[8, 4]} />
|
||||||
<meshStandardMaterial color="#f43f5e" transparent opacity={0.7} wireframe emissive="#f43f5e" emissiveIntensity={0.5} />
|
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
|
||||||
</mesh>
|
</mesh>
|
||||||
<mesh position={[0, -0.15, 0]}>
|
<mesh position={[6, 2, 0]} rotation={[0, -Math.PI / 2, 0]}>
|
||||||
<boxGeometry args={[0.3, 0.02, 0.3]} />
|
<planeGeometry args={[8, 4]} />
|
||||||
<meshStandardMaterial color="#ef4444" transparent opacity={0.4} />
|
<meshStandardMaterial color="#bae6fd" roughness={0.1} emissive="#bae6fd" emissiveIntensity={0.4} />
|
||||||
</mesh>
|
</mesh>
|
||||||
</group>
|
|
||||||
|
{/* Mesa Cilíndrica Central de 1m de diâmetro (raio 0.5) */}
|
||||||
|
<group position={[0, 0, 0]}>
|
||||||
|
{/* Base no chão */}
|
||||||
|
<mesh position={[0, 0.01, 0]} castShadow receiveShadow>
|
||||||
|
<cylinderGeometry args={[0.25, 0.25, 0.02, 32]} />
|
||||||
|
<meshStandardMaterial color="#475569" metalness={0.7} roughness={0.2} />
|
||||||
|
</mesh>
|
||||||
|
{/* Coluna / Pé da mesa */}
|
||||||
|
<mesh position={[0, 0.45, 0]} castShadow receiveShadow>
|
||||||
|
<cylinderGeometry args={[0.06, 0.06, 0.9, 32]} />
|
||||||
|
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.1} />
|
||||||
|
</mesh>
|
||||||
|
{/* Tampo da mesa (Diâmetro 1m, altura 0.9m) */}
|
||||||
|
<mesh position={[0, 0.9, 0]} castShadow receiveShadow>
|
||||||
|
<cylinderGeometry args={[0.5, 0.5, 0.06, 48]} />
|
||||||
|
<meshStandardMaterial color="#d97706" roughness={0.4} metalness={0.1} />
|
||||||
|
</mesh>
|
||||||
|
{/* Projetor de Holograma (Centro da mesa) */}
|
||||||
|
<mesh position={[0, 0.931, 0]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||||
|
<ringGeometry args={[0.22, 0.25, 32]} />
|
||||||
|
<meshBasicMaterial color="#0ea5e9" transparent opacity={0.6} />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Modelo 3D da Maquete Holográfica */}
|
||||||
|
<group position={isFocusMode ? [0, 0, 0] : [0, 1.23, 0]}>
|
||||||
|
<ModelErrorBoundary fallback={
|
||||||
|
// Se falhar ao carregar o modelo (ex: blob local do host inacessível por convidados), renderiza o holograma geométrico
|
||||||
|
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
||||||
|
<mesh position={[0, 0, 0]}>
|
||||||
|
<octahedronGeometry args={[0.22]} />
|
||||||
|
<meshStandardMaterial color="#f43f5e" transparent opacity={0.7} wireframe emissive="#f43f5e" emissiveIntensity={0.5} />
|
||||||
|
</mesh>
|
||||||
|
<mesh position={[0, -0.15, 0]}>
|
||||||
|
<boxGeometry args={[0.3, 0.02, 0.3]} />
|
||||||
|
<meshStandardMaterial color="#ef4444" transparent opacity={0.4} />
|
||||||
|
</mesh>
|
||||||
|
</group>
|
||||||
|
}>
|
||||||
|
<Suspense fallback={
|
||||||
|
<mesh>
|
||||||
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
|
<meshBasicMaterial color="#00f5ff" wireframe />
|
||||||
|
</mesh>
|
||||||
}>
|
}>
|
||||||
<Suspense fallback={
|
{currentActiveModel ? (
|
||||||
<mesh>
|
<HologramModel model={currentActiveModel} presentationState={presentationState} />
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
) : (
|
||||||
<meshBasicMaterial color="#00f5ff" wireframe />
|
// Holograma Padrão Geométrico se não houver maquete carregada
|
||||||
</mesh>
|
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
||||||
}>
|
<mesh position={[0, 0, 0]}>
|
||||||
{currentActiveModel ? (
|
<octahedronGeometry args={[0.22]} />
|
||||||
<HologramModel model={currentActiveModel} presentationState={presentationState} />
|
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
|
||||||
) : (
|
</mesh>
|
||||||
// Holograma Padrão Geométrico se não houver maquete carregada
|
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
|
||||||
<group rotation={[0, Date.now() * 0.00012, 0]}>
|
<boxGeometry args={[0.25, 0.04, 0.25]} />
|
||||||
<mesh position={[0, 0, 0]}>
|
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
|
||||||
<octahedronGeometry args={[0.22]} />
|
</mesh>
|
||||||
<meshStandardMaterial color="#0ea5e9" transparent opacity={0.7} wireframe emissive="#0ea5e9" emissiveIntensity={0.5} />
|
</group>
|
||||||
</mesh>
|
)}
|
||||||
<mesh position={[0, 0, 0]} rotation={[0, Math.PI / 4, 0]}>
|
</Suspense>
|
||||||
<boxGeometry args={[0.25, 0.04, 0.25]} />
|
</ModelErrorBoundary>
|
||||||
<meshStandardMaterial color="#3b82f6" transparent opacity={0.4} emissive="#3b82f6" emissiveIntensity={0.2} />
|
|
||||||
</mesh>
|
|
||||||
</group>
|
|
||||||
)}
|
|
||||||
</Suspense>
|
|
||||||
</ModelErrorBoundary>
|
|
||||||
</group>
|
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
{/* Renderizar as Cadeiras Físicas da mesa */}
|
{/* Renderizar as Cadeiras Físicas da mesa - Ocultadas no Modo Foco */}
|
||||||
{CHAIRS.map((chair) => (
|
{!isFocusMode && CHAIRS.map((chair) => (
|
||||||
<group key={chair.id} position={[chair.pos[0], 0, chair.pos[2]]} rotation={[0, Math.atan2(chair.lookAt[0] - chair.pos[0], chair.lookAt[2] - chair.pos[2]), 0]}>
|
<group key={chair.id} position={[chair.pos[0], 0, chair.pos[2]]} rotation={[0, Math.atan2(chair.lookAt[0] - chair.pos[0], chair.lookAt[2] - chair.pos[2]), 0]}>
|
||||||
{/* Base giratória metálica */}
|
{/* Base giratória metálica */}
|
||||||
<mesh position={[0, 0.25, 0]}>
|
<mesh position={[0, 0.25, 0]}>
|
||||||
@@ -622,11 +616,15 @@ function ThreeMeetingRoomScene({ currentActiveModel, presentationState }: { curr
|
|||||||
</group>
|
</group>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Iluminação Clara da Sala */}
|
{/* Iluminação Clara da Sala - Ocultada no Modo Foco */}
|
||||||
<hemisphereLight color="#ffffff" groundColor="#64748b" intensity={1.4} />
|
{!isFocusMode && (
|
||||||
<directionalLight position={[6, 12, 6]} intensity={1.8} castShadow />
|
<>
|
||||||
<directionalLight position={[-6, 8, -6]} intensity={0.5} />
|
<hemisphereLight color="#ffffff" groundColor="#64748b" intensity={1.4} />
|
||||||
<pointLight position={[0, 3.5, 0]} intensity={2.0} color="#0ea5e9" distance={8} />
|
<directionalLight position={[6, 12, 6]} intensity={1.8} castShadow />
|
||||||
|
<directionalLight position={[-6, 8, -6]} intensity={0.5} />
|
||||||
|
<pointLight position={[0, 3.5, 0]} intensity={2.0} color="#0ea5e9" distance={8} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -641,6 +639,7 @@ export default function MeetingRoom() {
|
|||||||
// Estados do Lobby
|
// Estados do Lobby
|
||||||
const [inRoom, setInRoom] = useState(false);
|
const [inRoom, setInRoom] = useState(false);
|
||||||
const [isConnecting, setIsConnecting] = useState(false);
|
const [isConnecting, setIsConnecting] = useState(false);
|
||||||
|
const [isFocusMode, setIsFocusMode] = useState(false);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [cargo, setCargo] = useState('');
|
const [cargo, setCargo] = useState('');
|
||||||
const [empresa, setEmpresa] = useState('');
|
const [empresa, setEmpresa] = useState('');
|
||||||
@@ -1131,10 +1130,10 @@ export default function MeetingRoom() {
|
|||||||
{/* 3D Canvas - Sala Clara */}
|
{/* 3D Canvas - Sala Clara */}
|
||||||
<div className="flex-1 h-full relative">
|
<div className="flex-1 h-full relative">
|
||||||
<Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows gl={{ localClippingEnabled: true }}>
|
<Canvas camera={{ position: [0, 2.5, 2.8], fov: 45 }} shadows gl={{ localClippingEnabled: true }}>
|
||||||
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} />
|
<ThreeMeetingRoomScene currentActiveModel={activeModel} presentationState={presentationState} isFocusMode={isFocusMode} />
|
||||||
|
|
||||||
{/* Renderizar avatares de TODOS na sala */}
|
{/* Renderizar avatares de TODOS na sala - Ocultados se modo foco */}
|
||||||
{Object.keys(peers).map((chairKey) => {
|
{!isFocusMode && Object.keys(peers).map((chairKey) => {
|
||||||
const peer = peers[chairKey];
|
const peer = peers[chairKey];
|
||||||
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
|
const chairConf = CHAIRS.find((c) => c.id === peer.chairId);
|
||||||
if (!chairConf) return null;
|
if (!chairConf) return null;
|
||||||
@@ -1160,7 +1159,7 @@ export default function MeetingRoom() {
|
|||||||
minDistance={0.5}
|
minDistance={0.5}
|
||||||
maxDistance={8}
|
maxDistance={8}
|
||||||
maxPolarAngle={Math.PI / 2 - 0.05}
|
maxPolarAngle={Math.PI / 2 - 0.05}
|
||||||
target={[0, 0.95, 0]}
|
target={isFocusMode ? [0, 0, 0] : [0, 0.95, 0]}
|
||||||
/>
|
/>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
@@ -1334,7 +1333,7 @@ export default function MeetingRoom() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* HUD Inferior - Controles de Áudio */}
|
{/* HUD Inferior - Controles de Áudio & Modo Foco */}
|
||||||
<div className="absolute bottom-4 left-4 pointer-events-none z-10 flex gap-2">
|
<div className="absolute bottom-4 left-4 pointer-events-none z-10 flex gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant={isMuted ? 'destructive' : 'default'}
|
variant={isMuted ? 'destructive' : 'default'}
|
||||||
@@ -1346,6 +1345,20 @@ export default function MeetingRoom() {
|
|||||||
{isMuted ? <MicOff className="h-4.5 w-4.5" /> : <Mic className="h-4.5 w-4.5" />}
|
{isMuted ? <MicOff className="h-4.5 w-4.5" /> : <Mic className="h-4.5 w-4.5" />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant={isFocusMode ? 'secondary' : 'outline'}
|
||||||
|
size="icon"
|
||||||
|
className={`pointer-events-auto h-10 w-10 rounded-xl shadow-2xl border ${
|
||||||
|
isFocusMode
|
||||||
|
? 'bg-sky-500 hover:bg-sky-600 text-slate-950 border-sky-400'
|
||||||
|
: 'bg-slate-900/90 text-white border-slate-700 hover:bg-slate-800'
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsFocusMode(!isFocusMode)}
|
||||||
|
title={isFocusMode ? 'Voltar para Sala Virtual (Minimizar)' : 'Focar na Peça (Tela Cheia)'}
|
||||||
|
>
|
||||||
|
{isFocusMode ? <Minimize2 className="h-4.5 w-4.5" /> : <Maximize2 className="h-4.5 w-4.5" />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
<div className="pointer-events-auto rounded-xl border bg-slate-900/90 backdrop-blur-md px-4 py-2 flex flex-col justify-center shadow-2xl">
|
<div className="pointer-events-auto rounded-xl border bg-slate-900/90 backdrop-blur-md px-4 py-2 flex flex-col justify-center shadow-2xl">
|
||||||
<span className="font-mono text-[8px] text-muted-foreground uppercase">
|
<span className="font-mono text-[8px] text-muted-foreground uppercase">
|
||||||
{isPresenter ? 'Apresentação Ativa' : 'Observador'}
|
{isPresenter ? 'Apresentação Ativa' : 'Observador'}
|
||||||
|
|||||||
Reference in New Issue
Block a user