diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts index ebafd99..bad936f 100644 --- a/app/auth/callback/route.ts +++ b/app/auth/callback/route.ts @@ -14,7 +14,6 @@ export async function GET(request: Request) { const supabase = await createClient(); await supabase.auth.exchangeCodeForSession(code); } - console.log("code", code); if (redirectTo) { return NextResponse.redirect(`${origin}${redirectTo}`); } diff --git a/app/dashboard/documents/[id]/page.tsx b/app/dashboard/documents/[id]/page.tsx index 7309060..10136c6 100644 --- a/app/dashboard/documents/[id]/page.tsx +++ b/app/dashboard/documents/[id]/page.tsx @@ -1,5 +1,4 @@ import { AppSidebar } from "@/components/app-sidebar"; -import KokoroReader from "@/components/KokoroReader"; import { NavActions } from "@/components/nav-actions"; import { Breadcrumb, @@ -7,12 +6,7 @@ import { BreadcrumbList, BreadcrumbPage, } from "@/components/ui/breadcrumb"; -import { Button } from "@/components/ui/button"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; + import { Separator } from "@/components/ui/separator"; import { SidebarInset, @@ -20,18 +14,23 @@ import { SidebarTrigger, } from "@/components/ui/sidebar"; import { createClient } from "@/utils/supabase/server"; -import { Speech } from "lucide-react"; import { redirect } from "next/navigation"; -import { remark } from "remark"; -import remarkHtml from "remark-html"; + +import { TTSProvider } from "@/components/TTSProvider"; +import MarkdownRenderer from "@/components/MarkdownRenderer"; export default async function DocumentPage(props: { params: { id: string } }) { const supabase = await createClient(); const { data: { user }, + error: userError, } = await supabase.auth.getUser(); + if (userError) { + console.error("Error fetching user:", userError); + } + if (!user) { return redirect("/login"); } @@ -66,44 +65,46 @@ export default async function DocumentPage(props: { params: { id: string } }) { const pages = (document.ocr_data as any).map((page: any) => page.markdown); - const processedContent = await remark() - .use(remarkHtml) - .process(pages.join("\n")); + // Here we simply recreate the pages string: + const rawContent = + (document?.ocr_data as any)?.map((page: any) => page.markdown).join("\n") || + ""; return ( - { - return { - name: d.file_name, - url: `/dashboard/documents/${d.id}`, - emoji: "📄", - }; - })} - /> - -
-
- - - - - - - {document.file_name || "Document Details"} - - - - -
-
- -
-
-
+ { + return { + name: d.file_name, + url: `/dashboard/documents/${d.id}`, + emoji: "📄", + }; + })} + /> + +
+
+ + + + + + + {document.file_name || "Document Details"} + + + + +
+
+ +
+
+ {/*
-
+ >
*/} +
+ +
+
+
); } diff --git a/app/layout.tsx b/app/layout.tsx index 430e63b..5e93219 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -8,6 +8,7 @@ import { ThemeProvider } from "next-themes"; import Link from "next/link"; import "./globals.css"; import { Toaster } from "@/components/ui/sonner"; +import { TooltipProvider } from "@/components/ui/tooltip"; const defaultUrl = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` @@ -15,8 +16,8 @@ const defaultUrl = process.env.VERCEL_URL export const metadata = { metadataBase: new URL(defaultUrl), - title: "Next.js and Supabase Starter Kit", - description: "The fastest way to build apps with Next.js and Supabase", + title: "Neuroread", + description: "The easiest way to read articles and papers.", }; const geistSans = Geist({ @@ -38,7 +39,7 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > - {children} + {children} diff --git a/bun.lockb b/bun.lockb index bacc2ec..84a569d 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/KokoroReader.tsx b/components/KokoroReader.tsx index 90faccf..6c38a84 100644 --- a/components/KokoroReader.tsx +++ b/components/KokoroReader.tsx @@ -1,7 +1,7 @@ "use client"; import { useRef, useState, useEffect } from "react"; import { Button } from "./ui/button"; -import { Play } from "lucide-react"; +import { Loader, Pause, Play } from "lucide-react"; import { Accordion, AccordionContent, @@ -9,93 +9,54 @@ import { AccordionTrigger, } from "./ui/accordion"; import { Label } from "./ui/label"; +import { useTTS } from "./TTSProvider"; export default function KokoroReader({ pages }: { pages: any[] }) { - // Create a reference to the worker object. - const worker = useRef(null); + const { + voices, + selectedSpeaker, + setSelectedSpeaker, + skipToSentence, + currentSentence, + setCurrentSentence, + playSentence, + playInOrder, + status, + } = useTTS(); - const [inputText, setInputText] = useState( - "Life is like a box of chocolates. You never know what you're gonna get." - ); - const [selectedSpeaker, setSelectedSpeaker] = useState("af_heart"); + const [playing, setPlaying] = useState(false); - const [voices, setVoices] = useState([]); - const [status, setStatus] = useState<"ready" | "running" | null>(null); - const [error, setError] = useState(null); - const [loadingMessage, setLoadingMessage] = useState("Loading..."); - - const [results, setResults] = useState<{ text: string; src: string }[]>([]); - - // We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted. useEffect(() => { - // Create the worker if it does not yet exist. - console.log("Initializing worker..."); - worker.current ??= new Worker("/workers/kokoro-worker.js", { - type: "module", - }); + setCurrentSentence(0); // this might just jumpstart the audio + }, [status === "ready"]); - console.log("Worker initialized"); + const play = () => { + if (playing) { + setPlaying(false); + return; + } - // Create a callback function for messages from the worker thread. - const onMessageReceived = (e: any) => { - switch (e.data.status) { - case "device": - setLoadingMessage(`Loading model (device="${e.data.device}")`); - break; - case "ready": - setStatus("ready"); - setVoices(e.data.voices); - break; - case "error": - setError(e.data.data); - break; - case "complete": - const { audio, text } = e.data; - // Generation complete: re-enable the "Generate" button - setResults((prev) => [{ text, src: audio }, ...prev]); - setStatus("ready"); - break; - } - }; - - console.log("onmessagereceived"); - - const onErrorReceived = (e: any) => { - console.error("Worker error:", e); - setError(e.message); - }; - - console.log("Attaching event listeners to worker"); - - // Attach the callback function as an event listener. - worker.current.addEventListener("message", onMessageReceived); - worker.current.addEventListener("error", onErrorReceived); - - console.log(worker.current); - // Define a cleanup function for when the component is unmounted. - return () => { - worker.current!.removeEventListener("message", onMessageReceived); - worker.current!.removeEventListener("error", onErrorReceived); - }; - }, []); - - const handleSubmit = (e: any) => { - e.preventDefault(); - setStatus("running"); - - worker.current!.postMessage({ - type: "generate", - text: inputText.trim(), - voice: selectedSpeaker, - }); + setPlaying(true); + playInOrder(currentSentence || 0); }; return (
-
@@ -123,53 +84,6 @@ export default function KokoroReader({ pages }: { pages: any[] }) {
- {/*
-
-