Download src/components/workspace/AIStudyAssistantPanel.tsx from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 6.17 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/workspace/AIStudyAssistantPanel.tsx
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/components/workspace/AIStudyAssistantPanel.tsx
-
curl -L -o AIStudyAssistantPanel.tsx https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/components/workspace/AIStudyAssistantPanel.tsx
6.17 kB
| "use client"; | |
| import { ChevronRight, Flame, Plus, Sparkles } from "lucide-react"; | |
| import LiquidGlass from "liquid-glass-react"; | |
| import { cn } from "@/lib/utils"; | |
| import { studyFeatures } from "./workspaceData"; | |
| import type { StudyFeature, StudyProgress } from "./workspaceTypes"; | |
| const DAY_LABELS = ["M", "T", "W", "T", "F", "S", "S"]; | |
| type AIStudyAssistantPanelProps = { | |
| className?: string; | |
| activeFeatureId: string | null; | |
| subject: string; | |
| progress: StudyProgress; | |
| onFeatureSelect: (feature: StudyFeature) => void; | |
| onAddTask: () => void; | |
| }; | |
| export function AIStudyAssistantPanel({ | |
| className, | |
| activeFeatureId, | |
| progress, | |
| onFeatureSelect, | |
| onAddTask, | |
| }: AIStudyAssistantPanelProps) { | |
| return ( | |
| <LiquidGlass | |
| mode="standard" | |
| blurAmount={20} | |
| saturation={1.16} | |
| elasticity={0.14} | |
| displacementScale={0.03} | |
| aberrationIntensity={0.007} | |
| cornerRadius={20} | |
| className={cn( | |
| "flex h-full flex-col overflow-hidden border border-white/[0.07] bg-[#0B0B0C]", | |
| className, | |
| )} | |
| > | |
| {/* Header */} | |
| <div className="flex items-center gap-2 border-b border-white/[0.06] px-4 py-3"> | |
| <Sparkles className="size-3.5 text-amber-400" strokeWidth={2} /> | |
| <h2 className="text-[11px] font-bold uppercase tracking-[0.14em] text-white/50"> | |
| Study guidance | |
| </h2> | |
| </div> | |
| {/* Feature list */} | |
| <div className="min-h-0 flex-1 overflow-y-auto px-2 py-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"> | |
| {studyFeatures.map((feature) => { | |
| const Icon = feature.icon; | |
| const isActive = activeFeatureId === feature.id; | |
| return ( | |
| <button | |
| key={feature.id} | |
| type="button" | |
| onClick={() => onFeatureSelect(feature)} | |
| className={cn( | |
| "group flex w-full items-center gap-3 rounded-2xl px-3 py-2.5 text-left transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20", | |
| isActive | |
| ? "bg-white/[0.07]" | |
| : "hover:bg-white/[0.045]", | |
| )} | |
| > | |
| {/* Icon */} | |
| <span | |
| className={cn( | |
| "grid size-9 shrink-0 place-items-center rounded-2xl", | |
| isActive ? "bg-white/[0.10]" : "bg-white/[0.05]", | |
| )} | |
| > | |
| <Icon | |
| className={cn("size-4", isActive ? "text-white" : "text-white/50 group-hover:text-white/75")} | |
| strokeWidth={1.9} | |
| /> | |
| </span> | |
| {/* Text */} | |
| <span className="min-w-0 flex-1"> | |
| <span | |
| className={cn( | |
| "block truncate text-[12.5px] font-semibold", | |
| isActive ? "text-white" : "text-white/75 group-hover:text-white", | |
| )} | |
| > | |
| {feature.title} | |
| </span> | |
| <span className="mt-0.5 block truncate text-[10.5px] text-white/38"> | |
| {feature.description} | |
| </span> | |
| </span> | |
| {/* Chevron */} | |
| <ChevronRight | |
| className={cn( | |
| "size-3.5 shrink-0 transition-transform", | |
| isActive ? "text-white/60" : "text-white/20 group-hover:translate-x-0.5 group-hover:text-white/40", | |
| )} | |
| strokeWidth={2} | |
| /> | |
| </button> | |
| ); | |
| })} | |
| </div> | |
| {/* Progress Overview */} | |
| <div className="border-t border-white/[0.06] px-4 py-3"> | |
| <p className="mb-3 text-[10px] font-bold uppercase tracking-[0.14em] text-white/35"> | |
| Progress Overview | |
| </p> | |
| {/* Streak */} | |
| <div className="mb-3 flex items-center gap-2"> | |
| <Flame className="size-3.5 text-orange-400" strokeWidth={2} /> | |
| <span className="text-[12px] font-bold text-white/80">{progress.streakDays} days streak</span> | |
| </div> | |
| {/* Week dots */} | |
| <div className="mb-3 flex items-center gap-1.5"> | |
| {DAY_LABELS.map((label, idx) => { | |
| const status = progress.weekDays[idx]; | |
| return ( | |
| <div key={`${label}-${idx}`} className="flex flex-1 flex-col items-center gap-1"> | |
| <div | |
| className={cn( | |
| "size-5 rounded-full border transition", | |
| status === "done" | |
| ? "border-emerald-400/60 bg-emerald-500/25 shadow-[0_0_8px_rgba(52,211,153,0.3)]" | |
| : status === "partial" | |
| ? "border-amber-400/50 bg-amber-500/15" | |
| : status === "today" | |
| ? "border-white/30 bg-white/10" | |
| : "border-white/10 bg-white/[0.03]", | |
| )} | |
| /> | |
| <span className="text-[9px] font-semibold text-white/30">{label}</span> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| {/* Today's goal */} | |
| <div className="mb-2 flex items-center justify-between"> | |
| <span className="text-[11.5px] font-semibold text-white/55">Today's Goal</span> | |
| <span className="text-[11.5px] font-bold text-white/75"> | |
| {progress.todayCompleted}/{progress.todayTotal} tasks | |
| </span> | |
| </div> | |
| <div className="mb-3 h-1.5 overflow-hidden rounded-full bg-white/[0.06]"> | |
| <div | |
| className="h-full rounded-full bg-white/70 transition-all" | |
| style={{ width: `${Math.round((progress.todayCompleted / Math.max(progress.todayTotal, 1)) * 100)}%` }} | |
| /> | |
| </div> | |
| {/* Add task */} | |
| <button | |
| type="button" | |
| onClick={onAddTask} | |
| className="flex w-full items-center justify-center gap-1.5 rounded-xl bg-white py-2 text-[12px] font-bold text-black transition hover:bg-white/90" | |
| > | |
| <Plus className="size-3.5" strokeWidth={2.5} /> | |
| Add New Task | |
| </button> | |
| </div> | |
| </LiquidGlass> | |
| ); | |
| } | |