diff --git a/app/dashboard/documents/[id]/page.tsx b/app/dashboard/documents/[id]/page.tsx index 362a26b..1884fd7 100644 --- a/app/dashboard/documents/[id]/page.tsx +++ b/app/dashboard/documents/[id]/page.tsx @@ -1,117 +1,126 @@ -import { AppSidebar } from "@/components/app-sidebar"; -import { NavActions } from "@/components/nav-actions"; -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbList, - BreadcrumbPage, -} from "@/components/ui/breadcrumb"; -import { Separator } from "@/components/ui/separator"; -import { - SidebarInset, - SidebarProvider, - SidebarTrigger, -} from "@/components/ui/sidebar"; -import { createClient } from "@/utils/supabase/server"; -import { redirect } from "next/navigation"; -import { remark } from "remark"; -import remarkHtml from "remark-html"; - -export default async function DocumentPage({ - params, -}: { - params: { id: string }; -}) { - const supabase = await createClient(); - - const { - data: { user }, - } = await supabase.auth.getUser(); - - if (!user) { - return redirect("/login"); - } - - // Fetch the document details based on the ID from params - const { data: document, error } = await supabase - .from("documents") - .select("*") - .eq("id", params.id) - .single(); - - if (error || !document) { - console.error("Error fetching document:", error); - } - - // If the document doesn't exist, redirect to the documents page or handle it accordingly - if (!document) { - return redirect("/dashboard"); - } - const { data: documents, error: documentsError } = await supabase - .from("documents") - .select("id, file_name, created_at, owner") - .eq("owner", user.id) - .order("created_at", { ascending: false }); - - if (documentsError) { - console.error("Error fetching documents:", error); - return
Error loading documents.
; - } - - const pages = (document.ocr_data as any).pages.map( - (page: any) => page.markdown - ); - - const processedContent = await remark() - .use(remarkHtml) - .process(pages.join(" ")); - - return ( - - { - return { - name: d.file_name, - url: `/dashboard/documents/${d.id}`, - emoji: "📄", - }; - })} - /> - -
-
- - - - - - - {document.file_name || "Document Details"} - - - - -
-
- -
-
-
-
-
- ); -} +import { AppSidebar } from "@/components/app-sidebar"; +import KokoroReader from "@/components/KokoroReader"; +import { NavActions } from "@/components/nav-actions"; +import { + Breadcrumb, + BreadcrumbItem, + 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, + SidebarProvider, + 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"; + +export default async function DocumentPage({ + params, +}: { + params: { id: string }; +}) { + const supabase = await createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user) { + return redirect("/login"); + } + + // Fetch the document details based on the ID from params + const { data: document, error } = await supabase + .from("documents") + .select("*") + .eq("id", params.id) + .single(); + + if (error || !document) { + console.error("Error fetching document:", error); + } + + // If the document doesn't exist, redirect to the documents page or handle it accordingly + if (!document) { + return redirect("/dashboard"); + } + const { data: documents, error: documentsError } = await supabase + .from("documents") + .select("id, file_name, created_at, owner") + .eq("owner", user.id) + .order("created_at", { ascending: false }); + + if (documentsError) { + console.error("Error fetching documents:", error); + return
Error loading documents.
; + } + + const pages = (document.ocr_data as any).pages.map( + (page: any) => page.markdown + ); + + const processedContent = await remark() + .use(remarkHtml) + .process(pages.join(" ")); + + return ( + + { + return { + name: d.file_name, + url: `/dashboard/documents/${d.id}`, + emoji: "📄", + }; + })} + /> + +
+
+ + + + + + + {document.file_name || "Document Details"} + + + + +
+
+ +
+
+
+
+
+ ); +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 6f6cb89..330b40a 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,61 +1,85 @@ -import { AppSidebar } from "@/components/app-sidebar"; -import { NavActions } from "@/components/nav-actions"; -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbList, - BreadcrumbPage, -} from "@/components/ui/breadcrumb"; -import { Separator } from "@/components/ui/separator"; -import { - SidebarInset, - SidebarProvider, - SidebarTrigger, -} from "@/components/ui/sidebar"; -import { createClient } from "@/utils/supabase/server"; -import { redirect } from "next/navigation"; - -export default async function Page() { - const supabase = await createClient(); - - const { - data: { user }, - } = await supabase.auth.getUser(); - - if (!user) { - return redirect("/login"); - } - - return ( - - - -
-
- - - - - - - Project Management & Task Tracking - - - - -
-
- -
-
-
-
-
-
- - - ); -} +import { AppSidebar } from "@/components/app-sidebar"; +import { NavActions } from "@/components/nav-actions"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbList, + BreadcrumbPage, +} from "@/components/ui/breadcrumb"; +import { Separator } from "@/components/ui/separator"; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar"; +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export default async function Page() { + const supabase = await createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user) { + return redirect("/login"); + } + + const { data: documents, error } = await supabase + .from("documents") + .select("*") + .eq("owner", user.id) + .order("created_at", { ascending: false }); + + if (error) { + console.error("Failed to fetch documents:", error); + // Optionally handle the error, e.g., show a message to the user + return ( +
+

Failed to load documents.

+
+ ); + } + + return ( + + { + return { + name: d.file_name, + url: `/dashboard/documents/${d.id}`, + emoji: "📄", + }; + })} + /> + +
+
+ + + + + + + Select a document... + + + + +
+
+ +
+
+
+
+
+
+ + + ); +} diff --git a/bun.lockb b/bun.lockb index e6603c5..c9a33ab 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/KokoroReader.tsx b/components/KokoroReader.tsx new file mode 100644 index 0000000..90faccf --- /dev/null +++ b/components/KokoroReader.tsx @@ -0,0 +1,175 @@ +"use client"; +import { useRef, useState, useEffect } from "react"; +import { Button } from "./ui/button"; +import { Play } from "lucide-react"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "./ui/accordion"; +import { Label } from "./ui/label"; + +export default function KokoroReader({ pages }: { pages: any[] }) { + // Create a reference to the worker object. + const worker = useRef(null); + + 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 [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", + }); + + console.log("Worker initialized"); + + // 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, + }); + }; + + return ( +
+
+
+ +
+ + + + + Settings + + + + + + + +
+ {/*
+
+