-
-
+
+
+
+
-
-
-
-
-
- Hyper AI
-
-
- {isTopicMode ? (
- <>
-
- {topicTitle}
-
-
-
- {moduleTitle}
-
-
-
- {subject.toUpperCase()} • {branch.toUpperCase()} •{" "}
- {semester.toUpperCase()}
-
-
-
- One cached explanation for every student. Ask follow-up
- questions live — only your chat uses Gemini, not the topic
- itself.
-
- >
- ) : (
- <>
-
- Learn With Hyper AI
-
-
-
- {subject.toUpperCase()} • {branch.toUpperCase()} •{" "}
- {semester.toUpperCase()}
-
-
-
- Pick a topic from the syllabus to open its AI workspace with
- cached explanations and live follow-up chat.
-
- >
- )}
-
-
-
-
-
-
-
-
-
-
-
- {isTopicMode && (
-
-
-
- Follow-up Ideas
-
-
- The explanation above is cached once. Each question below
- uses a live Gemini call.
-
-
- {getFollowupPrompts(topicTitle).map((prompt) => (
-
- {prompt}
-
- ))}
-
-
-
- )}
-
-
-
-
+
+
);
}
diff --git a/app/rgpv/layout.tsx b/app/rgpv/layout.tsx
index bd696f8..3d01e7d 100644
--- a/app/rgpv/layout.tsx
+++ b/app/rgpv/layout.tsx
@@ -6,9 +6,9 @@ export default function RGPVLayout({
children: React.ReactNode;
}) {
return (
- <>
+
- {children}
- >
+
{children}
+
);
}
diff --git a/components/ai/workspace-chat.tsx b/components/ai/workspace-chat.tsx
index 17a22da..4fc293a 100644
--- a/components/ai/workspace-chat.tsx
+++ b/components/ai/workspace-chat.tsx
@@ -1,13 +1,24 @@
"use client";
-import { useState, useRef, useEffect, useCallback } from "react";
+import { useState, useRef, useEffect } from "react";
+import Link from "next/link";
import { motion, AnimatePresence } from "framer-motion";
-import { Send, Loader2, Sparkles, AlertCircle, ArrowDown } from "lucide-react";
+import {
+ Send,
+ Loader2,
+ Sparkles,
+ AlertCircle,
+ ChevronDown,
+ ChevronRight,
+ X,
+ Home,
+ PanelLeft,
+ PanelLeftClose,
+} from "lucide-react";
import WorkspaceMessage from "@/components/ai/workspace-message";
import ContinueExternalAI from "@/components/ai/continue-external-ai";
import { API_ENDPOINTS } from "@/lib/api-endpoints";
import {
- formatContinueLearningLabel,
formatLastStudied,
fromStoredMessages,
loadSession,
@@ -31,6 +42,7 @@ export interface WorkspaceChatProps {
topicId?: string;
topicTitle?: string;
moduleTitle?: string;
+ contextLabel?: string;
cachedExplanation?: string;
explanationCached?: boolean;
initialPrompts?: Array<{
@@ -58,7 +70,6 @@ interface WorkspaceInitialState {
messages: Message[];
lastStudiedAt: string | null;
sessionSaved: boolean;
- showContinueLearning: boolean;
skipPersist: boolean;
explanationRequested: boolean;
}
@@ -75,7 +86,6 @@ function getWorkspaceInitialState(params: {
messages: [],
lastStudiedAt: null,
sessionSaved: false,
- showContinueLearning: false,
skipPersist: false,
explanationRequested: false,
};
@@ -97,7 +107,6 @@ function getWorkspaceInitialState(params: {
messages: fromStoredMessages(session.messages),
lastStudiedAt: session.updatedAt,
sessionSaved: true,
- showContinueLearning: session.questionCount > 0,
skipPersist: true,
explanationRequested: true,
};
@@ -122,8 +131,8 @@ export default function WorkspaceChat({
topicId,
topicTitle,
moduleTitle,
+ contextLabel,
cachedExplanation,
- explanationCached: initialExplanationCached,
initialPrompts = [],
welcomeMessage = "Ask me anything about your subject. I can help with explanations, examples, and exam preparation.",
inputPlaceholder = "Type your question here...",
@@ -147,9 +156,6 @@ export default function WorkspaceChat({
const [topicExplanation, setTopicExplanation] = useState(
initialWorkspace.topicExplanation
);
- const [explanationCached, setExplanationCached] = useState(
- initialExplanationCached
- );
const [explanationLoading, setExplanationLoading] = useState(false);
const [messages, setMessages] = useState
(
initialWorkspace.messages
@@ -163,11 +169,10 @@ export default function WorkspaceChat({
const [sessionSaved, setSessionSaved] = useState(
initialWorkspace.sessionSaved
);
- const [showContinueLearning, setShowContinueLearning] = useState(
- initialWorkspace.showContinueLearning
- );
+ const [suggestionsCollapsed, setSuggestionsCollapsed] = useState(false);
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
+ const [sidebarOpen, setSidebarOpen] = useState(false);
- const messagesEndRef = useRef(null);
const inputRef = useRef(null);
const abortControllerRef = useRef(null);
const explanationRequestedRef = useRef(initialWorkspace.explanationRequested);
@@ -185,16 +190,6 @@ export default function WorkspaceChat({
isTopicContextReady &&
followUpCount >= followupLimit &&
!explanationLoading;
- const messageCount = messages.length;
- const hasConversation = followUpCount > 0;
-
- const scrollToBottom = useCallback((behavior: ScrollBehavior = "smooth") => {
- messagesEndRef.current?.scrollIntoView({ behavior });
- }, []);
-
- useEffect(() => {
- scrollToBottom();
- }, [messageCount, limitReached, loading, scrollToBottom]);
useEffect(() => {
inputRef.current?.focus();
@@ -238,7 +233,6 @@ export default function WorkspaceChat({
}
setTopicExplanation(data.answer);
- setExplanationCached(data.cached);
setMessages([buildInitialExplanationMessage(data.answer)]);
} catch (err: unknown) {
if (!cancelled) {
@@ -320,8 +314,6 @@ export default function WorkspaceChat({
return;
}
- setShowContinueLearning(false);
-
const userMessage: Message = {
id: crypto.randomUUID(),
role: "user",
@@ -433,11 +425,6 @@ export default function WorkspaceChat({
}
};
- const handleContinueLearning = () => {
- setShowContinueLearning(false);
- scrollToBottom();
- };
-
const followupPlaceholder = isFollowupMode
? `Ask a follow-up about ${topicTitle}...`
: inputPlaceholder;
@@ -454,289 +441,396 @@ export default function WorkspaceChat({
: `${followUpCount} / ${followupLimit}`;
const lastStudied = lastStudiedAt ? formatLastStudied(lastStudiedAt) : null;
+ const explanationMessage =
+ messages.find((message) => message.id === "initial-cached-explanation") ??
+ null;
+ const conversationMessages = messages.filter(
+ (message) => message.id !== "initial-cached-explanation"
+ );
+ const breadcrumbSegments = [
+ { label: "RGPV", href: "/rgpv" },
+ ...(branch
+ ? [{ label: branch.replace(/-/g, " "), href: `/rgpv/${branch}` }]
+ : []),
+ ...(branch && semester
+ ? [
+ {
+ label: semester.replace(/-/g, " "),
+ href: `/rgpv/${branch}/${semester}`,
+ },
+ ]
+ : []),
+ ...(branch && semester && subjectCode
+ ? [
+ {
+ label: subjectCode,
+ href: `/rgpv/${branch}/${semester}/${subjectCode.toLowerCase()}`,
+ },
+ ]
+ : []),
+ { label: "AI", href: "#" },
+ ];
return (
-
-
-
-
-
-
-
-
- Hyper AI Workspace
-
- {sessionSaved && isTopicContextReady && (
-
-
- Saved on this device
-
- )}
-
-
- {isFollowupMode
- ? topicTitle
- : `Ask questions about ${subjectCode.toUpperCase()}`}
-
-
-
-
- {isTopicContextReady && (
-
-
-
- Follow-up Questions
-
-
- {followupStatusLabel}
-
-
-
-
+
+ {sidebarOpen && (
+ <>
+ setSidebarOpen(false)}
/>
-
- {lastStudied && (
-
- Last studied{" "}
-
- {lastStudied.prefix}
- {lastStudied.detail ? ` · ${lastStudied.detail}` : ""}
-
-
+
+
+
+
+ >
+ )}
+
+
+