'use client'; import { useState } from 'react'; import { Archive, Download, FileDown, Film, Loader2, Monitor, Moon, NotebookText, Package, Settings, Sun, } from 'lucide-react'; import { Switch } from '@/components/ui/switch'; import { useI18n } from '@/lib/hooks/use-i18n'; import { useTheme } from '@/lib/hooks/use-theme'; import { useStageStore } from '@/lib/store'; import { useMediaGenerationStore } from '@/lib/store/media-generation'; import { useExportPPTX } from '@/lib/export/use-export-pptx'; import { useExportClassroom } from '@/lib/export/use-export-classroom'; import { isScriptExportReady, useExportScript } from '@/lib/export/use-export-script'; import { isVideoExportEnabled } from '@/lib/config/feature-flags'; import { useVideoRenderStore } from '@/lib/store/video-render'; import { CircularProgress } from '@/components/ui/circular-progress'; import { VideoExportDialog } from './video-export-dialog'; import { LanguageSwitcher } from '../language-switcher'; import { SettingsDialog } from '../settings'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { cn } from '@/lib/utils'; import type { StageMode } from '@/lib/types/stage'; interface HeaderControlsProps { readonly mode?: StageMode; readonly proModeActive?: boolean; readonly canEdit?: boolean; readonly onToggleEditMode?: () => void; readonly showGlobalControls?: boolean; readonly showCourseActions?: boolean; /** * `default` — the chunky h-9 pill used in the playback Stage Header. * `compact` — slightly tighter padding for embedding in CommandBar's * right slot (Pro mode chrome already eats height, so the pill backs * off ring weight / blur to keep the CommandBar quiet). */ readonly variant?: 'default' | 'compact'; } /** * Stage-level global controls: language picker, theme picker, settings * modal trigger, and the Pro Switch. Extracted out of `Header` so the * Pro mode CommandBar can absorb the same affordances and the playback * Header doesn't need to stay mounted just to host them — Pro mode * therefore lands on a single top-chrome bar instead of stacking the * Stage Header above the EditShell CommandBar. * * Only one instance is ever mounted at a time (Stage renders Header * for playback and EditShell.CommandBar's trailing slot for edit, but * never both), so dropdown / dialog state and refs stay co-located * here without cross-instance leakage. */ export function HeaderControls({ mode, proModeActive, canEdit, onToggleEditMode, showGlobalControls = true, showCourseActions = true, variant = 'default', }: HeaderControlsProps) { const { t } = useI18n(); const { theme, setTheme } = useTheme(); const [settingsOpen, setSettingsOpen] = useState(false); const [videoDialogOpen, setVideoDialogOpen] = useState(false); // Export plumbing — uses the stage / media task stores to check // readiness, then hands off to the export hooks. Available in both // playback and edit chrome so the icon's screen position is stable // across mode swaps (was previously in `Header` only, missing from // CommandBar's right cluster). const scenes = useStageStore((s) => s.scenes); const generatingOutlines = useStageStore((s) => s.generatingOutlines); const failedOutlines = useStageStore((s) => s.failedOutlines); const mediaTasks = useMediaGenerationStore((s) => s.tasks); const { exporting: isExporting, exportPPTX, exportResourcePack } = useExportPPTX(); const { exporting: isExportingZip, exportClassroomZip } = useExportClassroom(); const { exporting: isExportingScript, exportScriptDocx, exportScriptMd } = useExportScript(); const videoExportEnabled = isVideoExportEnabled(); // Video render lives in a global store so its progress ring stays on the // export button even after the menu closes / scenes switch mid-render. const videoRendering = useVideoRenderStore( (s) => s.status === 'compiling' || s.status === 'rendering', ); const videoRenderPercent = useVideoRenderStore((s) => s.percent); const [exportMenuOpen, setExportMenuOpen] = useState(false); // Keep the original full-generation gate for the export menu. Script files // are text-only, but the latest review confirmed that this menu intentionally // stays unavailable until all media tasks have completed or failed. const canExport = isScriptExportReady({ scenes, generatingOutlines, failedOutlines }, mediaTasks); const exportLabel = canExport ? t('export.pptx') : t('share.notReady'); const compact = variant === 'compact'; const proChecked = proModeActive ?? mode === 'edit'; if (!showGlobalControls && !showCourseActions) { return onToggleEditMode ? (