Download src/components/video/VideoLessonPreview.tsx from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 7.76 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/video/VideoLessonPreview.tsx
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/components/video/VideoLessonPreview.tsx
-
curl -L -o VideoLessonPreview.tsx https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/video/VideoLessonPreview.tsx
7.76 kB
| import type { VideoLessonPlan } from "@/lib/video/videoLessonPlanner"; | |
| type VideoLessonPreviewProps = { | |
| plan: VideoLessonPlan; | |
| }; | |
| function Metric({ label, value }: { label: string; value: string | number }) { | |
| return ( | |
| <div className="rounded-lg border border-[#2A2A2A] bg-[#050505] p-3"> | |
| <p className="text-[10px] font-semibold uppercase tracking-[0.14em] text-[#6B7280]">{label}</p> | |
| <p className="mt-1 text-[16px] font-bold text-white">{value}</p> | |
| </div> | |
| ); | |
| } | |
| function SectionLabel({ children }: { children: React.ReactNode }) { | |
| return <p className="text-[11px] font-semibold uppercase tracking-[0.14em] text-[#6B7280]">{children}</p>; | |
| } | |
| function sceneTypeLabel(sceneType: string): string { | |
| const labels: Record<string, string> = { | |
| intro: "class intro", | |
| session_plan: "session plan", | |
| teacher_explanation: "tutor explanation", | |
| board_drawing: "board drawing", | |
| worked_example: "worked example", | |
| method: "DocDoe method", | |
| notes: "notebook notes", | |
| exam_answer: "exam answer", | |
| practice_pause: "practice pause", | |
| quiz: "quiz", | |
| revision: "revision", | |
| outro: "class finish", | |
| }; | |
| return labels[sceneType] ?? sceneType.replace(/_/g, " "); | |
| } | |
| function voiceStyleLabel(style: string): string { | |
| const labels: Record<string, string> = { | |
| calm_teacher: "calm tutor", | |
| exam_coach: "exam coach", | |
| quick_revision: "quick revision", | |
| }; | |
| return labels[style] ?? style.replace(/_/g, " "); | |
| } | |
| export function VideoLessonPreview({ plan }: VideoLessonPreviewProps) { | |
| const narrationPreview = plan.audioScript.slice(0, 2); | |
| const boardPreview = plan.boardTimeline.slice(0, 3); | |
| const notebookPreview = plan.notebookSections.slice(0, 2); | |
| const practicePreview = plan.practicePauses.slice(0, 2); | |
| return ( | |
| <section | |
| aria-label="Video lesson plan preview" | |
| className="rounded-xl border border-[#2A2A2A] bg-[#0B0B0B] p-4 md:p-5" | |
| data-testid="video-lesson-preview" | |
| > | |
| <div className="flex flex-wrap items-start justify-between gap-3"> | |
| <div className="min-w-0"> | |
| <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-[#6B7280]">Audio/video preview</p> | |
| <h3 className="mt-1 text-xl font-bold tracking-tight text-white">{plan.missionTitle}</h3> | |
| <p className="mt-2 max-w-2xl text-[13px] leading-6 text-[#A1A1AA]"> | |
| Video renderer coming later; this is today's lesson plan. No MP4 or generated video is created here. | |
| </p> | |
| </div> | |
| <span className="rounded-full border border-white/15 bg-white px-3 py-1 text-[11px] font-black uppercase tracking-[0.12em] text-black"> | |
| Renderer coming later | |
| </span> | |
| </div> | |
| <div className="mt-4 grid gap-3 sm:grid-cols-3"> | |
| <Metric label="Duration" value={`${plan.estimatedDurationMinutes} min`} /> | |
| <Metric label="Scenes" value={plan.scenes.length} /> | |
| <Metric label="Status" value={plan.renderStatus.replace("_", " ")} /> | |
| </div> | |
| {plan.warnings.length > 0 ? ( | |
| <div className="mt-4 rounded-lg border border-[#3A3A3A] bg-[#111111] p-3"> | |
| <SectionLabel>Planner warnings</SectionLabel> | |
| <ul className="mt-2 space-y-1 text-[12px] leading-5 text-[#D4D4D8]"> | |
| {plan.warnings.map((warning) => ( | |
| <li key={warning}>{warning}</li> | |
| ))} | |
| </ul> | |
| </div> | |
| ) : null} | |
| <div className="mt-5 grid gap-4 xl:grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)]"> | |
| <div className="space-y-4"> | |
| <div data-testid="video-scene-timeline"> | |
| <SectionLabel>Lesson timeline</SectionLabel> | |
| <ol className="mt-3 space-y-2"> | |
| {plan.scenes.slice(0, 8).map((scene) => ( | |
| <li key={scene.id} className="rounded-lg border border-[#2A2A2A] bg-[#111111] p-3"> | |
| <div className="flex flex-wrap items-center justify-between gap-2"> | |
| <p className="text-[13px] font-semibold text-white">{scene.title}</p> | |
| <span className="font-mono text-[11px] text-[#A1A1AA]"> | |
| {scene.startMinute}-{scene.startMinute + scene.durationMinutes} min | |
| </span> | |
| </div> | |
| <p className="mt-1 text-[11px] uppercase tracking-[0.12em] text-[#6B7280]">{sceneTypeLabel(scene.sceneType)}</p> | |
| <p className="mt-2 line-clamp-2 text-[12px] leading-5 text-[#D4D4D8]">{scene.narration[0]}</p> | |
| </li> | |
| ))} | |
| </ol> | |
| </div> | |
| <div data-testid="video-board-timeline"> | |
| <SectionLabel>Board drawing</SectionLabel> | |
| <div className="mt-3 grid gap-3 md:grid-cols-2"> | |
| {boardPreview.map((frame) => ( | |
| <article key={frame.id} className="rounded-lg border border-[#2A2A2A] bg-[#111111] p-3"> | |
| <p className="text-[13px] font-semibold text-white">{frame.frameTitle}</p> | |
| <p className="mt-1 font-mono text-[11px] text-[#A1A1AA]">at {frame.timestampMinute} min</p> | |
| <ul className="mt-3 space-y-1 text-[12px] leading-5 text-[#D4D4D8]"> | |
| {frame.drawingSteps.slice(0, 3).map((step) => ( | |
| <li key={step}>{step}</li> | |
| ))} | |
| </ul> | |
| </article> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| <div className="space-y-4"> | |
| <div className="rounded-lg border border-[#2A2A2A] bg-[#111111] p-3" data-testid="video-narration-preview"> | |
| <SectionLabel>Tutor explanation</SectionLabel> | |
| <div className="mt-3 space-y-3"> | |
| {narrationPreview.map((block) => ( | |
| <div key={block.sceneId} className="rounded-lg border border-white/[0.07] bg-white/[0.035] p-3"> | |
| <p className="text-[11px] font-semibold uppercase tracking-[0.12em] text-[#6B7280]">{voiceStyleLabel(block.voiceStyle)}</p> | |
| <p className="mt-2 line-clamp-4 text-[12px] leading-5 text-white">{block.script}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="rounded-lg border border-[#2A2A2A] bg-[#111111] p-3" data-testid="video-notebook-preview"> | |
| <SectionLabel>Copy this to notebook</SectionLabel> | |
| <div className="mt-3 space-y-3"> | |
| {notebookPreview.map((section) => ( | |
| <div key={`${section.sceneId}-${section.title}`}> | |
| <p className="text-[13px] font-semibold text-white">{section.title}</p> | |
| <ul className="mt-2 space-y-1 text-[12px] leading-5 text-[#D4D4D8]"> | |
| {section.copyToNotebook.slice(0, 3).map((line) => ( | |
| <li key={line}>{line}</li> | |
| ))} | |
| </ul> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="rounded-lg border border-[#2A2A2A] bg-[#111111] p-3" data-testid="video-practice-preview"> | |
| <SectionLabel>Notice this</SectionLabel> | |
| <div className="mt-3 space-y-3"> | |
| {practicePreview.map((pause) => ( | |
| <div key={pause.sceneId} className="rounded-lg border border-dashed border-[#3A3A3A] bg-[#0B0B0B] p-3"> | |
| <p className="text-[12px] font-semibold leading-5 text-white">{pause.prompt}</p> | |
| <p className="mt-1 text-[11px] text-[#A1A1AA]">Pause: {pause.pauseSeconds} sec</p> | |
| <p className="mt-2 text-[12px] leading-5 text-[#D4D4D8]">Hint: {pause.hint}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| ); | |
| } | |