Download src/components/study-chat/EmptyStudyWorkspace.tsx from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 12 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/study-chat/EmptyStudyWorkspace.tsx
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/components/study-chat/EmptyStudyWorkspace.tsx
-
curl -L -o EmptyStudyWorkspace.tsx https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/study-chat/EmptyStudyWorkspace.tsx
12 kB
| "use client"; | |
| import type { ComponentProps } from "react"; | |
| import NextLink from "next/link"; | |
| import { | |
| ArrowRight, | |
| BookOpen, | |
| CheckCircle2, | |
| FileQuestion, | |
| FileText, | |
| GraduationCap, | |
| Layers3, | |
| Moon, | |
| Network, | |
| Sparkles, | |
| Target, | |
| Upload, | |
| Video, | |
| } from "lucide-react"; | |
| import type { ApiDocument } from "@/lib/api/types"; | |
| import type { StudyIntent } from "@/lib/study-intents"; | |
| import { QuickActions } from "./QuickActions"; | |
| import { SelectedSourcesStrip } from "./SourceControls"; | |
| import { StudyComposer } from "./StudyComposer"; | |
| import type { StudyActionHandler } from "./types"; | |
| import { greetingForNow } from "./utils"; | |
| type EmptyStudyWorkspaceProps = { | |
| activeIntent: StudyIntent; | |
| composerProps: ComponentProps<typeof StudyComposer>; | |
| documents: ApiDocument[]; | |
| heroSubtitle: string; | |
| heroTitle: string; | |
| isLoading: boolean; | |
| onAction: StudyActionHandler; | |
| onClearSources: () => void; | |
| onToggleSource: (sourceId: string) => void; | |
| onUpload?: () => void; | |
| selectedDocuments: ApiDocument[]; | |
| studentName: string; | |
| }; | |
| export function EmptyStudyWorkspace({ | |
| activeIntent, | |
| composerProps, | |
| documents, | |
| heroSubtitle, | |
| heroTitle, | |
| isLoading, | |
| onAction, | |
| onClearSources, | |
| onToggleSource, | |
| onUpload, | |
| selectedDocuments, | |
| studentName, | |
| }: EmptyStudyWorkspaceProps) { | |
| const readySourceCount = documents.filter((document) => document.status === "ready").length; | |
| const selectedSourceLabel = | |
| selectedDocuments.length > 0 | |
| ? `${selectedDocuments.length} active source${selectedDocuments.length === 1 ? "" : "s"}` | |
| : documents.length > 0 | |
| ? `${readySourceCount} ready source${readySourceCount === 1 ? "" : "s"}` | |
| : "No source selected"; | |
| const starterPrompts: Array<{ | |
| label: string; | |
| detail: string; | |
| intent: StudyIntent; | |
| prompt: string; | |
| icon: typeof BookOpen; | |
| }> = [ | |
| { | |
| label: "Explain a concept", | |
| detail: "Definition, keywords, mark-wise structure", | |
| intent: "teach_topic", | |
| prompt: "Explain photosynthesis with exam keywords and a 4-mark answer frame", | |
| icon: BookOpen, | |
| }, | |
| { | |
| label: "Scoring notes", | |
| detail: "Definitions, traps, revision checklist", | |
| intent: "generate_notes", | |
| prompt: "Generate exam-ready notes on chemical bonding with common mistakes", | |
| icon: FileText, | |
| }, | |
| { | |
| label: "Practice quiz", | |
| detail: "MCQs with explanations and traps", | |
| intent: "generate_quiz", | |
| prompt: "Create a 5-question quiz on trigonometry with answer explanations", | |
| icon: FileQuestion, | |
| }, | |
| { | |
| label: "Flashcards", | |
| detail: "Active recall with memory hooks", | |
| intent: "generate_flashcards", | |
| prompt: "Build flashcards for organic chemistry reactions and named examples", | |
| icon: Layers3, | |
| }, | |
| { | |
| label: "Video plan", | |
| detail: "Scene script with exam focus", | |
| intent: "create_video_plan", | |
| prompt: "Create a 10-minute exam-focused study video plan for electromagnetic induction", | |
| icon: Video, | |
| }, | |
| { | |
| label: "Revision plan", | |
| detail: "Time-blocked cram schedule", | |
| intent: "create_revision_plan", | |
| prompt: "I have one night before my physics exam. Build a focused revision plan", | |
| icon: Moon, | |
| }, | |
| ]; | |
| const groundingHint = selectedDocuments.length | |
| ? `Grounding in ${selectedDocuments[0].title}` | |
| : documents.length | |
| ? "Select a source for textbook-matched retrieval, or ask any topic." | |
| : "Upload material for source-grounded answers, or ask a topic directly."; | |
| const workspaceSignals = [ | |
| { label: selectedSourceLabel, icon: CheckCircle2 }, | |
| { label: "Textbook-aware answers", icon: Network }, | |
| { label: "Exam answer practice", icon: Target }, | |
| ]; | |
| return ( | |
| <div className="kb-empty-scroll min-h-0 flex-1 overflow-y-auto px-4 pb-28 pt-5 sm:px-6 lg:flex lg:items-center lg:px-8 lg:pb-10"> | |
| <div className="mx-auto flex w-full max-w-3xl flex-col py-4 sm:py-8 lg:max-w-none lg:py-6 xl:max-w-[min(100%,56rem)]"> | |
| <div className="kb-empty-enter lg:grid lg:grid-cols-[minmax(0,1fr)_17.5rem] lg:items-end lg:gap-8 xl:grid-cols-[minmax(0,1fr)_19rem] xl:gap-10"> | |
| <div className="text-center lg:text-left"> | |
| <div className="inline-flex items-center gap-2 rounded-full border border-white/[0.09] bg-white/[0.035] px-3 py-1.5 text-xs font-semibold text-white/58"> | |
| <BookOpen className="size-3.5" /> | |
| {greetingForNow()}, {studentName} | |
| </div> | |
| <h1 className="mt-4 text-balance text-[1.85rem] font-semibold leading-[1.08] tracking-[-0.02em] text-white sm:text-[2.35rem] lg:text-[2.75rem] xl:text-[3rem]"> | |
| {heroTitle} | |
| </h1> | |
| <p className="mx-auto mt-3 max-w-xl text-balance text-sm leading-6 text-white/52 sm:text-[15px] lg:mx-0 lg:max-w-2xl lg:text-base"> | |
| {heroSubtitle} | |
| </p> | |
| <div className="mt-5 hidden flex-wrap gap-2 lg:flex"> | |
| {workspaceSignals.map((signal) => { | |
| const Icon = signal.icon; | |
| return ( | |
| <span | |
| key={signal.label} | |
| className="inline-flex min-h-8 items-center gap-2 rounded-full border border-white/[0.08] bg-white/[0.025] px-3 text-[11px] font-semibold text-white/44" | |
| > | |
| <Icon className="size-3.5 text-white/48" /> | |
| {signal.label} | |
| </span> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| <div className="mt-6 hidden rounded-[1.2rem] border border-white/[0.08] bg-white/[0.028] p-4 lg:mt-0 lg:block"> | |
| <div className="flex items-start gap-3"> | |
| <span className="grid size-10 shrink-0 place-items-center rounded-2xl bg-white text-black"> | |
| <Sparkles className="size-5" /> | |
| </span> | |
| <div className="min-w-0"> | |
| <p className="text-sm font-semibold text-white">Study tools ready</p> | |
| <p className="mt-1 text-xs leading-5 text-white/42">{groundingHint}</p> | |
| </div> | |
| </div> | |
| {onUpload ? ( | |
| <button | |
| type="button" | |
| onClick={onUpload} | |
| className="mt-4 inline-flex min-h-10 w-full items-center justify-center gap-2 rounded-xl border border-white/[0.1] bg-white/[0.045] px-3 text-sm font-semibold text-white/70 transition hover:border-white/[0.18] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22" | |
| > | |
| <Upload className="size-4" /> | |
| Add study source | |
| </button> | |
| ) : null} | |
| </div> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d1 mt-5"> | |
| <NextLink | |
| href="/tuition" | |
| className="mx-auto flex min-h-12 w-full max-w-xl items-center justify-between gap-3 rounded-2xl border border-white/[0.1] bg-white/[0.04] px-4 text-left transition hover:border-white/[0.18] hover:bg-white/[0.065] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22 lg:mx-0" | |
| > | |
| <span className="flex min-w-0 items-center gap-3"> | |
| <span className="grid size-9 shrink-0 place-items-center rounded-xl bg-white text-black"> | |
| <GraduationCap className="size-4" aria-hidden="true" /> | |
| </span> | |
| <span className="min-w-0"> | |
| <span className="block text-sm font-bold text-white">Continue Today's Tuition</span> | |
| <span className="mt-0.5 block text-xs leading-5 text-white/44"> | |
| Study Chat is for doubts. Your official class stays in Tuition. | |
| </span> | |
| </span> | |
| </span> | |
| <ArrowRight className="size-4 shrink-0 text-white/40" aria-hidden="true" /> | |
| </NextLink> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d2 mt-7 sm:mt-8 lg:mt-8"> | |
| <StudyComposer {...composerProps} variant="hero" /> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d2 mt-4 lg:hidden"> | |
| <QuickActions activeIntent={activeIntent} disabled={isLoading} onAction={onAction} layout="scroll" /> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d2 mt-4 hidden lg:block xl:hidden"> | |
| <QuickActions activeIntent={activeIntent} disabled={isLoading} onAction={onAction} layout="grid" /> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d3 mt-4 flex flex-wrap items-center justify-center gap-2 lg:hidden"> | |
| {workspaceSignals.map((signal) => { | |
| const Icon = signal.icon; | |
| return ( | |
| <span | |
| key={signal.label} | |
| className="inline-flex min-h-8 items-center gap-2 rounded-full border border-white/[0.08] bg-white/[0.025] px-3 text-[11px] font-semibold text-white/44" | |
| > | |
| <Icon className="size-3.5 text-white/48" /> | |
| {signal.label} | |
| </span> | |
| ); | |
| })} | |
| </div> | |
| {onUpload ? ( | |
| <div className="kb-empty-enter kb-empty-enter-d3 mt-4 lg:hidden"> | |
| <button | |
| type="button" | |
| onClick={onUpload} | |
| className="mx-auto flex min-h-10 w-full max-w-sm items-center justify-center gap-2 rounded-xl border border-white/[0.1] bg-white/[0.04] px-4 text-sm font-semibold text-white/68 transition hover:border-white/[0.18] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22" | |
| > | |
| <Upload className="size-4" /> | |
| Add study source | |
| </button> | |
| </div> | |
| ) : null} | |
| <div className="kb-empty-enter kb-empty-enter-d4 mt-5 hidden min-[390px]:block"> | |
| <SelectedSourcesStrip | |
| selectedDocuments={selectedDocuments} | |
| documents={documents} | |
| onToggleSource={onToggleSource} | |
| onClear={onClearSources} | |
| /> | |
| </div> | |
| <div className="kb-empty-enter kb-empty-enter-d5 mt-8 lg:mt-10"> | |
| <div className="flex items-baseline justify-between gap-3 border-b border-white/10 pb-3"> | |
| <p className="text-[11px] font-bold uppercase tracking-[0.14em] text-white/32">Start here</p> | |
| <p className="hidden text-sm text-white/46 sm:block">or type your own question above</p> | |
| </div> | |
| <ol className="dd-stagger sm:columns-2 sm:gap-10"> | |
| {starterPrompts.map((item, index) => ( | |
| <li key={item.label} className="break-inside-avoid border-b border-white/[0.07]"> | |
| <button | |
| type="button" | |
| disabled={isLoading} | |
| onClick={() => onAction(item.intent, item.prompt)} | |
| className="dd-press group grid w-full grid-cols-[28px_1fr_auto] items-baseline gap-3 py-3.5 text-left transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22 disabled:cursor-not-allowed disabled:opacity-45" | |
| > | |
| <span className="font-mono text-[11px] text-white/30 transition group-hover:text-white/60"> | |
| {String(index + 1).padStart(2, "0")} | |
| </span> | |
| <span className="min-w-0"> | |
| <span className="block text-sm font-semibold text-white/84 transition group-hover:text-white"> | |
| {item.label} | |
| </span> | |
| <span className="mt-0.5 block text-xs leading-5 text-white/40">{item.detail}</span> | |
| </span> | |
| <ArrowRight className="size-4 shrink-0 self-center text-white/20 transition group-hover:translate-x-0.5 group-hover:text-white/70" /> | |
| </button> | |
| </li> | |
| ))} | |
| </ol> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |