diff --git a/surfsense_backend/app/agents/podcaster/nodes.py b/surfsense_backend/app/agents/podcaster/nodes.py index ceb1bd64b..31a687763 100644 --- a/surfsense_backend/app/agents/podcaster/nodes.py +++ b/surfsense_backend/app/agents/podcaster/nodes.py @@ -99,10 +99,10 @@ async def create_merged_podcast_audio( ) -> dict[str, Any]: """Generate audio for each transcript and merge them into a single podcast file.""" - configuration = Configuration.from_runnable_config(config) + # configuration = Configuration.from_runnable_config(config) starting_transcript = PodcastTranscriptEntry( - speaker_id=1, dialog=f"Welcome to {configuration.podcast_title} Podcast." + speaker_id=1, dialog="Welcome to Surfsense Podcast." ) transcript = state.podcast_transcript diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index e1e51c601..eceb7ecd4 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -1,7 +1,8 @@ "use client"; -import { useAtom } from "jotai"; +import { useAtom, useAtomValue } from "jotai"; import { Loader2, PanelRight } from "lucide-react"; +import { AnimatePresence, motion } from "motion/react"; import { usePathname, useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import type React from "react"; @@ -16,6 +17,7 @@ import { Separator } from "@/components/ui/separator"; import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { useLLMPreferences } from "@/hooks/use-llm-configs"; import { cn } from "@/lib/utils"; +import { activeChatIdAtom } from "@/stores/chat/active-chat.atom"; import { chatUIAtom } from "@/stores/chat/chat-ui.atom"; export function DashboardClientLayout({ @@ -35,9 +37,26 @@ export function DashboardClientLayout({ const searchSpaceIdNum = Number(searchSpaceId); const [chatUIState, setChatUIState] = useAtom(chatUIAtom); + const activeChatId = useAtomValue(activeChatIdAtom); + const [showIndicator, setShowIndicator] = useState(false); const { isChatPannelOpen } = chatUIState; + // Check if we're on the researcher page + const isResearcherPage = pathname?.includes("/researcher"); + + // Show indicator when chat becomes active and panel is closed + useEffect(() => { + if (activeChatId && !isChatPannelOpen) { + setShowIndicator(true); + // Hide indicator after 5 seconds + const timer = setTimeout(() => setShowIndicator(false), 5000); + return () => clearTimeout(timer); + } else { + setShowIndicator(false); + } + }, [activeChatId, isChatPannelOpen]); + const { loading, error, isOnboardingComplete } = useLLMPreferences(searchSpaceIdNum); const [hasCheckedOnboarding, setHasCheckedOnboarding] = useState(false); @@ -161,24 +180,112 @@ export function DashboardClientLayout({
- + {/* Only show artifacts toggle on researcher page */} + {isResearcherPage && ( + + { + setChatUIState((prev) => ({ + ...prev, + isChatPannelOpen: !isChatPannelOpen, + })); + setShowIndicator(false); + }} + className={cn( + "shrink-0 rounded-full p-2 transition-all duration-300 relative", + showIndicator + ? "bg-primary/20 hover:bg-primary/30 shadow-lg shadow-primary/25" + : "hover:bg-muted", + activeChatId && !showIndicator && "hover:bg-primary/10" + )} + title="Toggle Artifacts Panel" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + + + + + {/* Pulsing indicator badge */} + + {showIndicator && ( + + +
+ + + + )} + + + )}
{children}
- + {/* Only render chat panel on researcher page */} + {isResearcherPage && } diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx index a4eb30ef9..098d6324a 100644 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx +++ b/surfsense_web/components/chat/ChatPanel/ChatPanelContainer.tsx @@ -43,7 +43,7 @@ export function ChatPanelContainer() {
{isChatLoading || chatError ? ( diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx index 1d4ac4c71..848028e1f 100644 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx +++ b/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx @@ -1,7 +1,8 @@ "use client"; import { useAtom, useAtomValue } from "jotai"; -import { AlertCircle, Pencil, Play, Podcast, RefreshCw } from "lucide-react"; +import { AlertCircle, Pencil, Play, Podcast, RefreshCw, Sparkles } from "lucide-react"; +import { motion } from "motion/react"; import { useCallback, useContext, useTransition } from "react"; import { cn } from "@/lib/utils"; import { activeChatAtom } from "@/stores/chat/active-chat.atom"; @@ -41,23 +42,26 @@ export function ChatPanelView(props: ChatPanelViewProps) { return (
-
+
{isChatPannelOpen ? (
{/* Show stale podcast warning if applicable */} {podcastIsStale && ( -
+
- -
-

Podcast is outdated

-

+ + + +

+

Podcast Outdated

+

{getPodcastStalenessMessage( chatDetails?.state_version || 0, podcast?.chat_state_version @@ -65,41 +69,96 @@ export function ChatPanelView(props: ChatPanelViewProps) {

-
+
)} -
{ - if (e.key === "Enter") { - e.preventDefault(); - handleGeneratePost(); - } - }} - className={cn( - "w-full space-y-3 rounded-xl p-3 transition-colors", - podcastIsStale - ? "bg-gradient-to-r from-amber-400/50 to-orange-300/50 dark:from-amber-500/30 dark:to-orange-600/30 hover:from-amber-400/60 hover:to-orange-300/60" - : "bg-gradient-to-r from-slate-400/50 to-slate-200/50 dark:from-slate-400/30 dark:to-slate-800/60 hover:from-slate-400/60 hover:to-slate-200/60" - )} + -
- {podcastIsStale ? ( - - ) : ( - +
{ + if (e.key === "Enter") { + e.preventDefault(); + handleGeneratePost(); + } + }} + className={cn( + "relative w-full rounded-2xl p-4 transition-all duration-300 cursor-pointer group overflow-hidden", + "border-2", + podcastIsStale + ? "bg-gradient-to-br from-amber-500/10 via-orange-500/10 to-amber-500/10 dark:from-amber-500/20 dark:via-orange-500/20 dark:to-amber-500/20 border-amber-400/50 hover:border-amber-400 hover:shadow-lg hover:shadow-amber-500/20" + : "bg-gradient-to-br from-primary/10 via-primary/5 to-primary/10 border-primary/30 hover:border-primary/60 hover:shadow-lg hover:shadow-primary/20" )} - + > + {/* Background gradient animation */} + + +
+
+
+ + {podcastIsStale ? ( + + ) : ( + + )} + +
+

+ {podcastIsStale ? "Regenerate Podcast" : "Generate Podcast"} +

+

+ {podcastIsStale + ? "Update with latest changes" + : "Create podcasts of your chat"} +

+
+
+ +
+
-

- {podcastIsStale ? "Regenerate Podcast" : "Generate Podcast"} -

-
+
) : ( - + {podcastIsStale ? : } + )}
{podcast ? (
{isChatPannelOpen ? ( ) : podcast ? ( - + + ) : null}
) : null} diff --git a/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx b/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx index 2ee0d30cb..f1efdd089 100644 --- a/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx +++ b/surfsense_web/components/chat/ChatPanel/ConfigModal.tsx @@ -44,8 +44,20 @@ export function ConfigModal(props: ConfigModalProps) { e.stopPropagation()} align="end" className="bg-sidebar w-96 ">
+

+ Leave empty to use the default prompt +

+
+

Examples:

+
    +
  • Make hosts speak in London street language
  • +
  • Use real-world analogies and metaphors
  • +
  • Add dramatic pauses like a late-night radio show
  • +
  • Include 90s pop culture references
  • +
+