Download src/components/study-chat/SourceControls.tsx from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 5.66 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/study-chat/SourceControls.tsx
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/components/study-chat/SourceControls.tsx
-
curl -L -o SourceControls.tsx https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/study-chat/SourceControls.tsx
5.66 kB
| "use client"; | |
| import { FileText, X } from "lucide-react"; | |
| import type { ApiDocument } from "@/lib/api/types"; | |
| import { cn } from "@/lib/utils"; | |
| function formatMaterialType(materialType: string | null | undefined): string { | |
| if (!materialType || materialType === "unknown") return "Unclassified"; | |
| return materialType.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()); | |
| } | |
| function getMaterialTypeBadgeColor(materialType: string | null | undefined): string { | |
| if (!materialType || materialType === "unknown") { | |
| return "border-white/12 bg-white/[0.04] text-white/46"; | |
| } | |
| const colors: Record<string, string> = { | |
| notes: "border-white/14 bg-white/[0.06] text-white/72", | |
| chapter: "border-blue-300/16 bg-blue-400/[0.08] text-blue-200/80", | |
| textbook: "border-emerald-300/16 bg-emerald-400/[0.08] text-emerald-200/80", | |
| handwritten_notes: "border-amber-300/16 bg-amber-400/[0.08] text-amber-200/80", | |
| question_paper: "border-rose-300/16 bg-rose-400/[0.08] text-rose-200/80", | |
| pyq_collection: "border-orange-300/16 bg-orange-400/[0.08] text-orange-200/80", | |
| syllabus: "border-cyan-300/16 bg-cyan-400/[0.08] text-cyan-200/80", | |
| formula_sheet: "border-pink-300/16 bg-pink-400/[0.08] text-pink-200/80", | |
| mixed: "border-white/14 bg-white/[0.06] text-white/72", | |
| }; | |
| return colors[materialType] || colors.notes; | |
| } | |
| function compactEducationBadge(document: ApiDocument): string | null { | |
| if (document.education_extraction_status === "extraction_failed") return "Extraction failed"; | |
| if (document.education_extraction_status === "needs_review") return "Needs review"; | |
| if (document.material_type === "syllabus" && (document.syllabus_items_count ?? 0) > 0) { | |
| return `${document.syllabus_items_count} topics`; | |
| } | |
| if (document.material_type === "question_paper" && (document.pyq_questions_count ?? 0) > 0) { | |
| return `${document.pyq_questions_count} PYQ`; | |
| } | |
| return null; | |
| } | |
| export function SourcePill({ | |
| document, | |
| active, | |
| onToggle, | |
| onRemove, | |
| }: { | |
| document: ApiDocument; | |
| active: boolean; | |
| onToggle: () => void; | |
| onRemove?: () => void; | |
| }) { | |
| const educationBadge = compactEducationBadge(document); | |
| return ( | |
| <button | |
| type="button" | |
| onClick={onToggle} | |
| className={cn( | |
| "group inline-flex min-w-0 items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22", | |
| active | |
| ? "border-white/[0.2] bg-white text-black" | |
| : "border-white/[0.1] bg-white/[0.035] text-white/58 hover:border-white/[0.18] hover:text-white", | |
| )} | |
| > | |
| <FileText className="size-3.5 shrink-0" /> | |
| <span className="max-w-[9rem] truncate sm:max-w-[12rem]">{document.title}</span> | |
| <span className={cn("size-1.5 rounded-full", document.status === "ready" ? "bg-current" : "bg-current opacity-45")} /> | |
| {document.material_type && document.material_type !== "unknown" && ( | |
| <span className={cn( | |
| "rounded-full border px-1.5 py-0.5 text-[9px] font-medium", | |
| active ? getMaterialTypeBadgeColor(document.material_type).replace("text-", "text-black/") : getMaterialTypeBadgeColor(document.material_type) | |
| )}> | |
| {formatMaterialType(document.material_type)} | |
| </span> | |
| )} | |
| {educationBadge ? ( | |
| <span className={cn( | |
| "rounded-full border px-1.5 py-0.5 text-[9px] font-medium", | |
| active ? "border-black/10 bg-black/[0.04] text-black/65" : "border-white/[0.08] bg-white/[0.025] text-white/40", | |
| )}> | |
| {educationBadge} | |
| </span> | |
| ) : null} | |
| {onRemove ? ( | |
| <span | |
| role="button" | |
| tabIndex={-1} | |
| aria-label={`Remove ${document.title}`} | |
| onClick={(event) => { | |
| event.stopPropagation(); | |
| onRemove(); | |
| }} | |
| className="ml-0.5 rounded-full opacity-55 transition group-hover:opacity-100" | |
| > | |
| <X className="size-3.5" /> | |
| </span> | |
| ) : null} | |
| </button> | |
| ); | |
| } | |
| export function SelectedSourcesStrip({ | |
| selectedDocuments, | |
| documents, | |
| onToggleSource, | |
| onClear, | |
| }: { | |
| selectedDocuments: ApiDocument[]; | |
| documents: ApiDocument[]; | |
| onToggleSource: (id: string) => void; | |
| onClear: () => void; | |
| }) { | |
| const previewDocuments = selectedDocuments.length > 0 ? selectedDocuments : documents.slice(0, 4); | |
| if (previewDocuments.length === 0) return null; | |
| return ( | |
| <div className="flex min-w-0 max-w-full flex-col items-start gap-2 sm:flex-row sm:items-center"> | |
| <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-white/34"> | |
| {selectedDocuments.length > 0 ? "Selected sources" : "Recent sources"} | |
| </span> | |
| <div className="flex w-full min-w-0 max-w-full gap-2 overflow-x-auto pb-1 [scrollbar-width:none] sm:flex-1 [&::-webkit-scrollbar]:hidden"> | |
| {previewDocuments.map((document) => ( | |
| <SourcePill | |
| key={document.id} | |
| document={document} | |
| active={selectedDocuments.some((item) => item.id === document.id)} | |
| onToggle={() => onToggleSource(document.id)} | |
| onRemove={selectedDocuments.some((item) => item.id === document.id) ? () => onToggleSource(document.id) : undefined} | |
| /> | |
| ))} | |
| </div> | |
| {selectedDocuments.length > 0 ? ( | |
| <button | |
| type="button" | |
| onClick={onClear} | |
| className="rounded-full px-2.5 py-1 text-[11px] font-semibold text-white/38 transition hover:bg-white/[0.04] hover:text-white/70" | |
| > | |
| Clear | |
| </button> | |
| ) : null} | |
| </div> | |
| ); | |
| } | |