From 984f7f9c25c0e16179e1ad4b7148c1aa7dbc454f Mon Sep 17 00:00:00 2001 From: Shiv Date: Wed, 8 Jul 2026 16:46:58 +0530 Subject: [PATCH] refactor(ai): improve workspace navigation layout --- app/layout.tsx | 2 +- .../[branch]/[semester]/[subject]/ai/page.tsx | 125 +--- app/rgpv/layout.tsx | 6 +- components/ai/workspace-chat.tsx | 674 ++++++++++-------- components/ai/workspace-message.tsx | 2 +- components/breadcrumbs.tsx | 36 +- components/navbar.tsx | 51 ++ 7 files changed, 491 insertions(+), 405 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index eedff32..e31831e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -128,7 +128,7 @@ export default function RootLayout({
{children} diff --git a/app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx b/app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx index c7e15b2..e275b98 100644 --- a/app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx +++ b/app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx @@ -1,4 +1,3 @@ -import { BrainCircuit } from "lucide-react"; import WorkspaceChatLoader from "@/components/ai/workspace-chat-loader"; import { getTopicById } from "@/lib/content"; import { generateTopicAnswer } from "@/lib/ai/topic-service"; @@ -65,112 +64,30 @@ export default async function AIPage({ params, searchParams }: AIPageProps) { ]; const welcomeMessage = isTopicMode - ? `Your **${topicTitle}** explanation is ready below. Ask up to 3 follow-up questions — examples, comparisons, exam tips, and clarifications.` + ? `Your **${topicTitle}** explanation is ready below. Ask up to 3 follow-up questions.` : "Select a topic from the syllabus to get a cached explanation and follow-up chat."; return ( -
-
-
-
-
+
+
+
+
- -
-
- - - 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}` : ""} - -

+ + + + + + )} + + +