"use client" import type { CSSProperties, KeyboardEvent, ReactNode } from "react" import { Camera, Keyboard, Warning } from "@phosphor-icons/react" import { Button } from "@/components/ui/button" import { CountdownOverlay } from "@/features/capture/CountdownOverlay" import { ShutterButton } from "@/features/capture/ShutterButton" import { canShoot, type CapturePhase, type CountdownValue, type DeniedReason, } from "@/features/capture/machine" import { cn } from "@/lib/utils" /* DESIGN-SYSTEM ยง9: area kamera `ink`, garis 3 px, radius 16 px, braket bidik di empat sudut, HUD di atas tengah, tombol jepret dan chip pintasan di bawah. Berkas acuan `docs/ui-reference/capture.html` memakai rasio 1 : 1 di bawah 768 px. */ export type FlashKind = "none" | "overlay" | "border" const BRACKETS = [ "top-4 left-4 border-t-4 border-l-4", "top-4 right-4 border-t-4 border-r-4", "bottom-4 left-4 border-b-4 border-l-4", "bottom-4 right-4 border-b-4 border-r-4", ] const DENIED_MESSAGES: Record = { denied: "Kamera belum diizinkan. Buka pengaturan situs di browser, izinkan kamera, lalu muat ulang.", unavailable: "Kamera tidak ditemukan atau sedang dipakai aplikasi lain. Tutup aplikasi lain, lalu coba lagi.", } interface CameraStageProps { ratio: number phase: CapturePhase deniedReason: DeniedReason | null countdownValue: CountdownValue | null flash: FlashKind onRequestPermission: () => void onShoot: () => void children?: ReactNode hud?: ReactNode /** M8 (SE-05): lapisan di atas pratinjau, dipakai untuk bayangan pose foto * pembuat di layar teman. Di bawah HUD, hitung mundur, dan braket bidik. */ overlay?: ReactNode } function PermissionScreen({ onRequestPermission, }: { onRequestPermission: () => void }) { return (

Kamera dipakai motret di perangkat ini.

Foto diproses di perangkat. Tidak ada yang diunggah.

) } function DeniedScreen({ deniedReason, onRequestPermission, }: { deniedReason: DeniedReason | null onRequestPermission: () => void }) { return (

{deniedReason === null ? DENIED_MESSAGES.unavailable : DENIED_MESSAGES[deniedReason]}

) } export function CameraStage({ ratio, phase, deniedReason, countdownValue, flash, onRequestPermission, onShoot, children, hud, overlay, }: CameraStageProps) { /* Tanpa kamera (belum izin atau izin ditolak) kontrol jepret tidak ditampilkan supaya tidak menutupi tombol izin. */ const showControls = phase !== "idle" && phase !== "denied" function handleKeyDown(event: KeyboardEvent) { if (event.key !== " " || event.target !== event.currentTarget) { return } event.preventDefault() onShoot() } return (
{BRACKETS.map((corner) => ( ))} {phase === "idle" ? ( ) : null} {phase === "denied" ? ( ) : null} {phase === "idle" || phase === "denied" ? null : children} {/* M8 (SE-05): bayangan pose teman, opasitasnya diatur pemanggil. z-[5] menjaga braket bidik (z-10) tetap di atasnya. */} {overlay === undefined || phase === "idle" || phase === "denied" ? null : (
{overlay}
)} {hud === undefined ? null : (
{hud}
)} {countdownValue === null ? null : ( )} {flash === "overlay" ? (
) : null} {showControls ? (
Spasi
) : null}
) }