diff --git a/app/(auth-pages)/forgot-password/page.tsx b/app/(auth-pages)/forgot-password/page.tsx deleted file mode 100644 index ac67693..0000000 --- a/app/(auth-pages)/forgot-password/page.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { forgotPasswordAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; - -export default async function ForgotPassword(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - return ( - <> -
-
-

Reset Password

-

- Already have an account?{" "} - - Sign in - -

-
-
- - - - Reset Password - - -
-
- - - ); -} diff --git a/app/(auth-pages)/layout.tsx b/app/(auth-pages)/layout.tsx deleted file mode 100644 index 47c4dd7..0000000 --- a/app/(auth-pages)/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export default async function Layout({ - children, -}: { - children: React.ReactNode; -}) { - return ( -
{children}
- ); -} diff --git a/app/(auth-pages)/sign-in/page.tsx b/app/(auth-pages)/sign-in/page.tsx deleted file mode 100644 index 85fc62f..0000000 --- a/app/(auth-pages)/sign-in/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { signInAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; - -export default async function Login(props: { searchParams: Promise }) { - const searchParams = await props.searchParams; - return ( -
-

Sign in

-

- Don't have an account?{" "} - - Sign up - -

-
- - -
- - - Forgot Password? - -
- - - Sign in - - -
-
- ); -} diff --git a/app/(auth-pages)/sign-up/page.tsx b/app/(auth-pages)/sign-up/page.tsx deleted file mode 100644 index 1bbe9dd..0000000 --- a/app/(auth-pages)/sign-up/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { signUpAction } from "@/app/actions"; -import { FormMessage, Message } from "@/components/form-message"; -import { SubmitButton } from "@/components/submit-button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; - -export default async function Signup(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - if ("message" in searchParams) { - return ( -
- -
- ); - } - - return ( - <> -
-

Sign up

-

- Already have an account?{" "} - - Sign in - -

-
- - - - - - Sign up - - -
-
- - - ); -} diff --git a/app/(auth-pages)/smtp-message.tsx b/app/(auth-pages)/smtp-message.tsx deleted file mode 100644 index a3d050e..0000000 --- a/app/(auth-pages)/smtp-message.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ArrowUpRight, InfoIcon } from "lucide-react"; -import Link from "next/link"; - -export function SmtpMessage() { - return ( -
- -
- - Note: Emails are rate limited. Enable Custom SMTP to - increase the rate limit. - -
- - Learn more - -
-
-
- ); -} diff --git a/app/api/process-document/route.ts b/app/api/process-document/route.ts index 503011c..695b003 100644 --- a/app/api/process-document/route.ts +++ b/app/api/process-document/route.ts @@ -21,7 +21,7 @@ Do not return the Markdown as a code block, only as a raw string, without any ne No data or information should ever be removed, it should only be processed and formatted. -There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. If any citations contain JSON-breaking characters, ensure they are properly escaped. This includes characters like double quotes, backslashes, and newlines. +There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. (**Note that there may be multiple citations, usually split by commas. Ensure these are added too.**) If any citations contain JSON-breaking characters, ensure they are properly escaped. This includes characters like double quotes, backslashes, and newlines. The Markdown should be human-readable and well-formatted. The markdown string should properly sanitized and should not break a JSON parser when returned as the final format. @@ -263,7 +263,11 @@ export async function POST(req: NextRequest) { ], }); - const split = response.choices[0].message.content.split("---------"); + const contentData = response.choices?.[0]?.message?.content; + const split = + typeof contentData === "string" + ? contentData.split("---------") + : ["", "{}"]; const content = split[0].trim(); const citationsStr = split[1]?.trim() || "{}"; console.log("Citations string:", citationsStr); diff --git a/app/dashboard/documents/[id]/page.tsx b/app/dashboard/documents/[id]/page.tsx index d223d0b..922f11a 100644 --- a/app/dashboard/documents/[id]/page.tsx +++ b/app/dashboard/documents/[id]/page.tsx @@ -19,7 +19,11 @@ import { redirect } from "next/navigation"; import { TTSProvider } from "@/components/TTSProvider"; import MarkdownRenderer from "@/components/MarkdownRenderer"; -export default async function DocumentPage(props: { params: { id: string } }) { +export default async function DocumentPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { const supabase = await createClient(); const { @@ -35,7 +39,7 @@ export default async function DocumentPage(props: { params: { id: string } }) { return redirect("/login"); } - const { id } = await props.params; + const { id } = await params; // Fetch the document details based on the ID from params const { data: document, error } = await supabase diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index ad3a32a..094522f 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -73,9 +73,6 @@ export default async function Page() { -
- -
diff --git a/bun.lockb b/bun.lockb index f87940c..4bf9419 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/MarkdownRenderer.tsx b/components/MarkdownRenderer.tsx index aabde57..c28326b 100644 --- a/components/MarkdownRenderer.tsx +++ b/components/MarkdownRenderer.tsx @@ -10,6 +10,7 @@ import rehypeRaw from "rehype-raw"; import { useTTS } from "./TTSProvider"; import rehypeHighlight from "@/lib/utils"; import { Database } from "@/utils/supabase/types"; +import remarkGfm from "remark-gfm"; // Utility to escape regex special characters: function escapeRegExp(text: string) { @@ -18,7 +19,14 @@ function escapeRegExp(text: string) { export type OCRData = { index: number; - images: string[]; + images: { + id: string; + topLeftX: number; + topLeftY: number; + bottomRightX: number; + bottomRightY: number; + imageBase64: string; + }[]; markdown: string; citations: { text: string; @@ -54,13 +62,22 @@ export default function MarkdownRenderer({ const ocr = document?.ocr_data as OCRData[]; - const rawContent = ocr.map((page) => page.markdown).join("\n") || ""; + // Join all markdown content from the OCR data into a single string. Add page separators. + const rawContent = ocr + .map((page) => page.markdown) + .map((page, index) => { + const pageIndex = index + 1; // 1-based index for pages + const pageSeparator = `\n\n###### Page ${pageIndex}\n\n`; + return `${page}${pageSeparator}`; + }) + .join("\n\n"); const citations: { text: string; page: number; index: string; number: number; + inTextNumber: string; }[] = []; const totalPages = ocr.length; const totalSentences = sentences.length; @@ -75,6 +92,7 @@ export default function MarkdownRenderer({ page: page.index, index: (totalCitations + index).toString(), // unique index across all pages number: totalCitations + index + 1, // 1-based numbering + inTextNumber: citation.number, }); }); }); @@ -92,16 +110,61 @@ export default function MarkdownRenderer({ h4: ({ node, ...props }) => (

), + h6: ({ node, ...props }) => { + const text = props.children!.toString().split(" ")[1]; + const pageIndex = parseInt(text) - 1; // Convert to 0-based index + const page = ocr[pageIndex]; + + return ( +
+
+ + Page {pageIndex + 1} of {totalPages} + +
+
+ ); + }, p: ({ node, ...props }) => ( -

- ), - img: ({ node, ...props }) => ( - +

), + img: ({ node, ...props }) => { + const { src, alt } = props as any; + + const pageIndex = ocr.findIndex((p) => + p.images.find((image) => image.id === src) + ); + if (pageIndex === -1) return null; // Handle the case where the page is not found + + const page = ocr.find((p) => p.index === pageIndex); + if (!page) return null; // Handle the case where the page is not found + + const img = page.images.find((image) => { + if (image.id === src) { + return true; + } + }); + + if (!img) return null; // Handle the case where the image is not found + + // Calculate the width and height based on the image dimensions and DPI + const dpi = page.dimensions.dpi; + const width = ((img.bottomRightX - img.topLeftX) / dpi) * 96; // Convert to pixels + const height = ((img.bottomRightY - img.topLeftY) / dpi) * 96; // Convert to pixels + + return ( +

+ {alt} +
+ ); + }, a: ({ node, ...props }) => ( ), @@ -122,6 +185,10 @@ export default function MarkdownRenderer({ ), sup: ({ node, ...props }) => { // Check if the text contains a reference number + if (!props.children) { + return ; + } + const text = props.children!.toString(); const referenceNumber = text; @@ -139,7 +206,6 @@ export default function MarkdownRenderer({ } return ( - // TODO: get the references from the document and display them in a popover

{citation.text}

+

+ Page {citation.page}, Reference {citation.inTextNumber} +

); }, + table: ({ node, ...props }) => ( +
+ + + ), + thead: ({ node, ...props }) => ( + + ), + tbody: ({ node, ...props }) => , + tr: ({ node, ...props }) => ( + + ), + th: ({ node, ...props }) => ( +
+ ), + td: ({ node, ...props }) => , }; return ( @@ -162,6 +260,7 @@ export default function MarkdownRenderer({ children={rawContent} components={components} rehypePlugins={rehypePlugins} + remarkPlugins={[remarkGfm]} /> ); } diff --git a/components/TTSProvider.tsx b/components/TTSProvider.tsx index 803a760..1e8b5aa 100644 --- a/components/TTSProvider.tsx +++ b/components/TTSProvider.tsx @@ -85,6 +85,8 @@ export const TTSProvider = ({ audioCache.current.set(i, audioUrl); // Cache the audio URL } catch (error) { console.error(`Error preloading audio for sentence ${i}:`, error); + } finally { + setProcessing((prev) => prev.filter((item) => item !== i)); // Remove from processing } } } @@ -112,6 +114,7 @@ export const TTSProvider = ({ } const playSentence = async (index: number) => { + if (index === currentSentence) return; // Prevent redundant updates setCurrentSentence(index); const sentence = removeMarkdown(sentences[index]); @@ -142,13 +145,12 @@ export const TTSProvider = ({ playSentence(index); }; + let shouldContinue = true; + const playInOrder = async (index: number) => { if (index < 0 || index >= sentences.length) return; setCurrentSentence(index); - // Introduce a flag to track whether playback should continue - let shouldContinue = true; - for (let i = index; i < sentences.length; i++) { if (!shouldContinue) { console.log("Playback stopped or paused."); @@ -158,14 +160,13 @@ export const TTSProvider = ({ console.log("Playing sentence:", i, sentences[i]); try { await playSentence(i); - preloadAudio(i + 1); // Preload the next sentence after playing + await preloadAudio(i + 1); // Preload the next sentence after playing } catch (error) { console.error("Error playing sentence:", error); break; // Stop playback on error } } - // Reset the playback state when done setStatus("ready"); }; @@ -198,6 +199,23 @@ export const TTSProvider = ({ preloadAudio(currentSentence); }, [currentSentence]); + useEffect(() => { + if (audioRef.current) { + const handleEnded = () => { + console.log("Audio playback ended."); + if (status === "running") { + playInOrder(currentSentence + 1); // Play the next sentence + } + }; + + audioRef.current.addEventListener("ended", handleEnded); + + return () => { + audioRef.current?.removeEventListener("ended", handleEnded); + }; + } + }, [currentSentence, status]); + const value: TTSContextType = { sentences, currentSentence, diff --git a/components/UploadZone.tsx b/components/UploadZone.tsx index 2c52db0..1a172bf 100644 --- a/components/UploadZone.tsx +++ b/components/UploadZone.tsx @@ -39,7 +39,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { }, }); - eventSource.addEventListener("status", (event) => { + eventSource.addEventListener("status", (event: any) => { const data = JSON.parse(event.data); console.log("Status Event:", data); supabase.auth.setSession; @@ -47,7 +47,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { setStatus(data.message); }); - eventSource.addEventListener("error", (event) => { + eventSource.addEventListener("error", (event: any) => { console.error("SSE Error:", event); toast.error("An error occurred while processing the document", { description: event.data || "Unknown error", @@ -56,7 +56,7 @@ export default function UploadZone({ user }: { user?: { id: string } }) { eventSource.close(); }); - eventSource.addEventListener("complete", (event) => { + eventSource.addEventListener("complete", (event: any) => { const data = JSON.parse(event.data); console.log("Processing Complete:", data); toast.success("Document processing complete!"); diff --git a/lib/utils.ts b/lib/utils.ts index 50ea601..f76239f 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -9,6 +9,7 @@ export function cn(...inputs: ClassValue[]) { export async function detectWebGPU() { try { + // @ts-ignore const adapter = await navigator.gpu.requestAdapter(); return !!adapter; } catch (e) { diff --git a/package.json b/package.json index 2dae9b9..17d5438 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-markdown": "^10.1.0", "rehype-raw": "^7.0.0", "remark": "^15.0.1", + "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", "remove-markdown": "^0.6.0", "sonner": "^2.0.3", diff --git a/supabase/.gitignore b/supabase/.gitignore deleted file mode 100644 index ad9264f..0000000 --- a/supabase/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Supabase -.branches -.temp - -# dotenvx -.env.keys -.env.local -.env.*.local diff --git a/supabase/config.toml b/supabase/config.toml deleted file mode 100644 index 2691649..0000000 --- a/supabase/config.toml +++ /dev/null @@ -1,319 +0,0 @@ -# For detailed configuration reference documentation, visit: -# https://supabase.com/docs/guides/local-development/cli/config -# A string used to distinguish different Supabase projects on the same host. Defaults to the -# working directory name when running `supabase init`. -project_id = "neuro-read" - -[api] -enabled = true -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. `public` and `graphql_public` schemas are included by default. -schemas = ["public", "graphql_public"] -# Extra schemas to add to the search_path of every request. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 - -[api.tls] -# Enable HTTPS endpoints locally using a self-signed certificate. -enabled = false - -[db] -# Port to use for the local database URL. -port = 54322 -# Port used by db diff command to initialize the shadow database. -shadow_port = 54320 -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 15 - -[db.pooler] -enabled = false -# Port to use for the local connection pooler. -port = 54329 -# Specifies when a server connection can be reused by other clients. -# Configure one of the supported pooler modes: `transaction`, `session`. -pool_mode = "transaction" -# How many server connections to allow per user/database pair. -default_pool_size = 20 -# Maximum number of client connections allowed. -max_client_conn = 100 - -# [db.vault] -# secret_key = "env(SECRET_VALUE)" - -[db.migrations] -# Specifies an ordered list of schema files that describe your database. -# Supports glob patterns relative to supabase directory: "./schemas/*.sql" -schema_paths = [] - -[db.seed] -# If enabled, seeds the database after migrations during a db reset. -enabled = true -# Specifies an ordered list of seed files to load during db reset. -# Supports glob patterns relative to supabase directory: "./seeds/*.sql" -sql_paths = ["./seed.sql"] - -[realtime] -enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv4) -# ip_version = "IPv6" -# The maximum length in bytes of HTTP request headers. (default: 4096) -# max_header_length = 4096 - -[studio] -enabled = true -# Port to use for Supabase Studio. -port = 54323 -# External URL of the API server that frontend connects to. -api_url = "http://127.0.0.1" -# OpenAI API Key to use for Supabase AI in the Supabase Studio. -openai_api_key = "env(OPENAI_API_KEY)" - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[inbucket] -enabled = true -# Port to use for the email testing server web interface. -port = 54324 -# Uncomment to expose additional ports for testing user applications that send emails. -# smtp_port = 54325 -# pop3_port = 54326 -# admin_email = "admin@email.com" -# sender_name = "Admin" - -[storage] -enabled = true -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -# Image transformation API is available to Supabase Pro plan. -# [storage.image_transformation] -# enabled = true - -# Uncomment to configure local storage buckets -# [storage.buckets.images] -# public = false -# file_size_limit = "50MiB" -# allowed_mime_types = ["image/png", "image/jpeg"] -# objects_path = "./images" - -[auth] -enabled = true -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://127.0.0.1:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://127.0.0.1:3000"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). -jwt_expiry = 3600 -# If disabled, the refresh token will never expire. -enable_refresh_token_rotation = true -# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. -# Requires enable_refresh_token_rotation = true. -refresh_token_reuse_interval = 10 -# Allow/disallow new user signups to your project. -enable_signup = true -# Allow/disallow anonymous sign-ins to your project. -enable_anonymous_sign_ins = false -# Allow/disallow testing manual linking of accounts -enable_manual_linking = false -# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. -minimum_password_length = 6 -# Passwords that do not meet the following requirements will be rejected as weak. Supported values -# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` -password_requirements = "" - -[auth.rate_limit] -# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. -email_sent = 2 -# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. -sms_sent = 30 -# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. -anonymous_users = 30 -# Number of sessions that can be refreshed in a 5 minute interval per IP address. -token_refresh = 150 -# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). -sign_in_sign_ups = 30 -# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. -token_verifications = 30 - -# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. -# [auth.captcha] -# enabled = true -# provider = "hcaptcha" -# secret = "" - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = true -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false -# If enabled, users will need to reauthenticate or have logged in recently to change their password. -secure_password_change = false -# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. -max_frequency = "1s" -# Number of characters used in the email OTP. -otp_length = 6 -# Number of seconds before the email OTP expires (defaults to 1 hour). -otp_expiry = 3600 - -# Use a production-ready SMTP server -# [auth.email.smtp] -# enabled = true -# host = "smtp.sendgrid.net" -# port = 587 -# user = "apikey" -# pass = "env(SENDGRID_API_KEY)" -# admin_email = "admin@email.com" -# sender_name = "Admin" - -# Uncomment to customize email template -# [auth.email.template.invite] -# subject = "You have been invited" -# content_path = "./supabase/templates/invite.html" - -[auth.sms] -# Allow/disallow new user signups via SMS to your project. -enable_signup = false -# If enabled, users need to confirm their phone number before signing in. -enable_confirmations = false -# Template for sending OTP to users -template = "Your code is {{ .Code }}" -# Controls the minimum amount of time that must pass before sending another sms otp. -max_frequency = "5s" - -# Use pre-defined map of phone number to OTP for testing. -# [auth.sms.test_otp] -# 4152127777 = "123456" - -# Configure logged in session timeouts. -# [auth.sessions] -# Force log out after the specified duration. -# timebox = "24h" -# Force log out if the user has been inactive longer than the specified duration. -# inactivity_timeout = "8h" - -# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. -# [auth.hook.custom_access_token] -# enabled = true -# uri = "pg-functions:////" - -# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. -[auth.sms.twilio] -enabled = false -account_sid = "" -message_service_sid = "" -# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: -auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" - -# Multi-factor-authentication is available to Supabase Pro plan. -[auth.mfa] -# Control how many MFA factors can be enrolled at once per user. -max_enrolled_factors = 10 - -# Control MFA via App Authenticator (TOTP) -[auth.mfa.totp] -enroll_enabled = false -verify_enabled = false - -# Configure MFA via Phone Messaging -[auth.mfa.phone] -enroll_enabled = false -verify_enabled = false -otp_length = 6 -template = "Your code is {{ .Code }}" -max_frequency = "5s" - -# Configure MFA via WebAuthn -# [auth.mfa.web_authn] -# enroll_enabled = true -# verify_enabled = true - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, -# `twitter`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" -# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" -# Overrides the default auth redirectUrl. -redirect_uri = "" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "" -# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. -skip_nonce_check = false - -# Use Firebase Auth as a third-party provider alongside Supabase Auth. -[auth.third_party.firebase] -enabled = false -# project_id = "my-firebase-project" - -# Use Auth0 as a third-party provider alongside Supabase Auth. -[auth.third_party.auth0] -enabled = false -# tenant = "my-auth0-tenant" -# tenant_region = "us" - -# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. -[auth.third_party.aws_cognito] -enabled = false -# user_pool_id = "my-user-pool-id" -# user_pool_region = "us-east-1" - -# Use Clerk as a third-party provider alongside Supabase Auth. -[auth.third_party.clerk] -enabled = false -# Obtain from https://clerk.com/setup/supabase -# domain = "example.clerk.accounts.dev" - -[edge_runtime] -enabled = true -# Configure one of the supported request policies: `oneshot`, `per_worker`. -# Use `oneshot` for hot reload, or `per_worker` for load testing. -policy = "oneshot" -# Port to attach the Chrome inspector for debugging edge functions. -inspector_port = 8083 -# The Deno major version to use. -deno_version = 1 - -# [edge_runtime.secrets] -# secret_key = "env(SECRET_VALUE)" - -[analytics] -enabled = true -port = 54327 -# Configure one of the supported backends: `postgres`, `bigquery`. -backend = "postgres" - -# Experimental features may be deprecated any time -[experimental] -# Configures Postgres storage engine to use OrioleDB (S3) -orioledb_version = "" -# Configures S3 bucket URL, eg. .s3-.amazonaws.com -s3_host = "env(S3_HOST)" -# Configures S3 bucket region, eg. us-east-1 -s3_region = "env(S3_REGION)" -# Configures AWS_ACCESS_KEY_ID for S3 bucket -s3_access_key = "env(S3_ACCESS_KEY)" -# Configures AWS_SECRET_ACCESS_KEY for S3 bucket -s3_secret_key = "env(S3_SECRET_KEY)" - -[functions.generate-tts] -enabled = true -verify_jwt = true -import_map = "./functions/generate-tts/deno.json" -# Uncomment to specify a custom file path to the entrypoint. -# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx -entrypoint = "./functions/generate-tts/index.ts" -# Specifies static files to be bundled with the function. Supports glob patterns. -# For example, if you want to serve static HTML pages in your function: -# static_files = [ "./functions/generate-tts/*.html" ] diff --git a/supabase/functions/generate-tts/.npmrc b/supabase/functions/generate-tts/.npmrc deleted file mode 100644 index 48c6388..0000000 --- a/supabase/functions/generate-tts/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -# Configuration for private npm package dependencies -# For more information on using private registries with Edge Functions, see: -# https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries diff --git a/supabase/functions/generate-tts/deno.json b/supabase/functions/generate-tts/deno.json deleted file mode 100644 index f6ca845..0000000 --- a/supabase/functions/generate-tts/deno.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "imports": {} -} diff --git a/supabase/functions/generate-tts/deno.lock b/supabase/functions/generate-tts/deno.lock deleted file mode 100644 index 502fad9..0000000 --- a/supabase/functions/generate-tts/deno.lock +++ /dev/null @@ -1,664 +0,0 @@ -{ - "version": "4", - "specifiers": { - "jsr:@supabase/functions-js@*": "2.4.4", - "npm:@mistralai/mistralai@*": "1.6.0_zod@3.24.4", - "npm:@types/node@*": "22.12.0", - "npm:kokoro-js@*": "1.2.1", - "npm:kokoro-tts@*": "0.0.1-security", - "npm:openai@^4.52.5": "4.97.0", - "npm:p-limit@*": "6.2.0" - }, - "jsr": { - "@supabase/functions-js@2.4.4": { - "integrity": "38456509a6e22fb116b118464cbb36357256f9048d3580632b63af91f63769f7", - "dependencies": [ - "npm:openai" - ] - } - }, - "npm": { - "@emnapi/runtime@1.4.3": { - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dependencies": [ - "tslib" - ] - }, - "@huggingface/jinja@0.4.1": { - "integrity": "sha512-3WXbMFaPkk03LRCM0z0sylmn8ddDm4ubjU7X+Hg4M2GOuMklwoGAFXp9V2keq7vltoB/c7McE5aHUVVddAewsw==" - }, - "@huggingface/transformers@3.5.1": { - "integrity": "sha512-qWsPoJMBPYcrGuzRMVL//3dwcLXED9r+j+Hzw+hZpBfrMPdyq57ehNs7lew0D34Paj0DO0E0kZQjOZcIVN17hQ==", - "dependencies": [ - "@huggingface/jinja", - "onnxruntime-node", - "onnxruntime-web", - "sharp" - ] - }, - "@img/sharp-darwin-arm64@0.34.1": { - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", - "dependencies": [ - "@img/sharp-libvips-darwin-arm64" - ] - }, - "@img/sharp-darwin-x64@0.34.1": { - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", - "dependencies": [ - "@img/sharp-libvips-darwin-x64" - ] - }, - "@img/sharp-libvips-darwin-arm64@1.1.0": { - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==" - }, - "@img/sharp-libvips-darwin-x64@1.1.0": { - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==" - }, - "@img/sharp-libvips-linux-arm64@1.1.0": { - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==" - }, - "@img/sharp-libvips-linux-arm@1.1.0": { - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==" - }, - "@img/sharp-libvips-linux-ppc64@1.1.0": { - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==" - }, - "@img/sharp-libvips-linux-s390x@1.1.0": { - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==" - }, - "@img/sharp-libvips-linux-x64@1.1.0": { - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==" - }, - "@img/sharp-libvips-linuxmusl-arm64@1.1.0": { - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==" - }, - "@img/sharp-libvips-linuxmusl-x64@1.1.0": { - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==" - }, - "@img/sharp-linux-arm64@0.34.1": { - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", - "dependencies": [ - "@img/sharp-libvips-linux-arm64" - ] - }, - "@img/sharp-linux-arm@0.34.1": { - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", - "dependencies": [ - "@img/sharp-libvips-linux-arm" - ] - }, - "@img/sharp-linux-s390x@0.34.1": { - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", - "dependencies": [ - "@img/sharp-libvips-linux-s390x" - ] - }, - "@img/sharp-linux-x64@0.34.1": { - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", - "dependencies": [ - "@img/sharp-libvips-linux-x64" - ] - }, - "@img/sharp-linuxmusl-arm64@0.34.1": { - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", - "dependencies": [ - "@img/sharp-libvips-linuxmusl-arm64" - ] - }, - "@img/sharp-linuxmusl-x64@0.34.1": { - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", - "dependencies": [ - "@img/sharp-libvips-linuxmusl-x64" - ] - }, - "@img/sharp-wasm32@0.34.1": { - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", - "dependencies": [ - "@emnapi/runtime" - ] - }, - "@img/sharp-win32-ia32@0.34.1": { - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==" - }, - "@img/sharp-win32-x64@0.34.1": { - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==" - }, - "@isaacs/fs-minipass@4.0.1": { - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dependencies": [ - "minipass" - ] - }, - "@mistralai/mistralai@1.6.0_zod@3.24.4": { - "integrity": "sha512-PQwGV3+n7FbE7Dp3Vnd8DAa3ffx6WuVV966Gfmf4QvzwcO3Mvxpz0SnJ/PjaZcsCwApBCZpNyQzvarAKEQLKeQ==", - "dependencies": [ - "zod", - "zod-to-json-schema" - ] - }, - "@protobufjs/aspromise@1.1.2": { - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64@1.1.2": { - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen@2.0.4": { - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter@1.1.0": { - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch@1.1.0": { - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/inquire" - ] - }, - "@protobufjs/float@1.0.2": { - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire@1.1.0": { - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path@1.1.2": { - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool@1.1.0": { - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8@1.1.0": { - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@types/node-fetch@2.6.12": { - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "dependencies": [ - "@types/node@22.12.0", - "form-data" - ] - }, - "@types/node@18.19.87": { - "integrity": "sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A==", - "dependencies": [ - "undici-types@5.26.5" - ] - }, - "@types/node@22.12.0": { - "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", - "dependencies": [ - "undici-types@6.20.0" - ] - }, - "abort-controller@3.0.0": { - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": [ - "event-target-shim" - ] - }, - "agentkeepalive@4.6.0": { - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dependencies": [ - "humanize-ms" - ] - }, - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "boolean@3.2.0": { - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" - }, - "call-bind-apply-helpers@1.0.2": { - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": [ - "es-errors", - "function-bind" - ] - }, - "chownr@3.0.0": { - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==" - }, - "color-convert@2.0.1": { - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": [ - "color-name" - ] - }, - "color-name@1.1.4": { - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string@1.9.1": { - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": [ - "color-name", - "simple-swizzle" - ] - }, - "color@4.2.3": { - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": [ - "color-convert", - "color-string" - ] - }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": [ - "delayed-stream" - ] - }, - "define-data-property@1.1.4": { - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": [ - "es-define-property", - "es-errors", - "gopd" - ] - }, - "define-properties@1.2.1": { - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": [ - "define-data-property", - "has-property-descriptors", - "object-keys" - ] - }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "detect-libc@2.0.4": { - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==" - }, - "detect-node@2.1.0": { - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "dunder-proto@1.0.1": { - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": [ - "call-bind-apply-helpers", - "es-errors", - "gopd" - ] - }, - "es-define-property@1.0.1": { - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" - }, - "es-errors@1.3.0": { - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-object-atoms@1.1.1": { - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": [ - "es-errors" - ] - }, - "es-set-tostringtag@2.1.0": { - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dependencies": [ - "es-errors", - "get-intrinsic", - "has-tostringtag", - "hasown" - ] - }, - "es6-error@4.1.1": { - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "escape-string-regexp@4.0.0": { - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "event-target-shim@5.0.1": { - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "flatbuffers@25.2.10": { - "integrity": "sha512-7JlN9ZvLDG1McO3kbX0k4v+SUAg48L1rIwEvN6ZQl/eCtgJz9UylTMzE9wrmYrcorgxm3CX/3T/w5VAub99UUw==" - }, - "form-data-encoder@1.7.2": { - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "form-data@4.0.2": { - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dependencies": [ - "asynckit", - "combined-stream", - "es-set-tostringtag", - "mime-types" - ] - }, - "formdata-node@4.4.1": { - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": [ - "node-domexception", - "web-streams-polyfill" - ] - }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "get-intrinsic@1.3.0": { - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": [ - "call-bind-apply-helpers", - "es-define-property", - "es-errors", - "es-object-atoms", - "function-bind", - "get-proto", - "gopd", - "has-symbols", - "hasown", - "math-intrinsics" - ] - }, - "get-proto@1.0.1": { - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": [ - "dunder-proto", - "es-object-atoms" - ] - }, - "global-agent@3.0.0": { - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dependencies": [ - "boolean", - "es6-error", - "matcher", - "roarr", - "semver", - "serialize-error" - ] - }, - "globalthis@1.0.4": { - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dependencies": [ - "define-properties", - "gopd" - ] - }, - "gopd@1.2.0": { - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" - }, - "guid-typescript@1.0.9": { - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" - }, - "has-property-descriptors@1.0.2": { - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": [ - "es-define-property" - ] - }, - "has-symbols@1.1.0": { - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" - }, - "has-tostringtag@1.0.2": { - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": [ - "has-symbols" - ] - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": [ - "function-bind" - ] - }, - "humanize-ms@1.2.1": { - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": [ - "ms" - ] - }, - "is-arrayish@0.3.2": { - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "json-stringify-safe@5.0.1": { - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "kokoro-js@1.2.1": { - "integrity": "sha512-oq0HZJWis3t8lERkMJh84WLU86dpYD0EuBPtqYnLlQzyFP1OkyBRDcweAqCfhNOpltyN9j/azp1H6uuC47gShw==", - "dependencies": [ - "@huggingface/transformers", - "phonemizer" - ] - }, - "kokoro-tts@0.0.1-security": { - "integrity": "sha512-VK7ILDqP0J88kiXdFrq7ymugipw42xLGmAU3kqpyFwJKlBqcsiTXVbvdTtehwYhYPw5QKJPmN2j7tD6fhPe6qw==" - }, - "long@5.3.2": { - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, - "matcher@3.0.0": { - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dependencies": [ - "escape-string-regexp" - ] - }, - "math-intrinsics@1.1.0": { - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": [ - "mime-db" - ] - }, - "minipass@7.1.2": { - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" - }, - "minizlib@3.0.2": { - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dependencies": [ - "minipass" - ] - }, - "mkdirp@3.0.1": { - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==" - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node-domexception@1.0.0": { - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, - "node-fetch@2.7.0": { - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": [ - "whatwg-url" - ] - }, - "object-keys@1.1.1": { - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "onnxruntime-common@1.21.0": { - "integrity": "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==" - }, - "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4": { - "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==" - }, - "onnxruntime-node@1.21.0": { - "integrity": "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==", - "dependencies": [ - "global-agent", - "onnxruntime-common@1.21.0", - "tar" - ] - }, - "onnxruntime-web@1.22.0-dev.20250409-89f8206ba4": { - "integrity": "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==", - "dependencies": [ - "flatbuffers", - "guid-typescript", - "long", - "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", - "platform", - "protobufjs" - ] - }, - "openai@4.97.0": { - "integrity": "sha512-LRoiy0zvEf819ZUEJhgfV8PfsE8G5WpQi4AwA1uCV8SKvvtXQkoWUFkepD6plqyJQRghy2+AEPQ07FrJFKHZ9Q==", - "dependencies": [ - "@types/node@18.19.87", - "@types/node-fetch", - "abort-controller", - "agentkeepalive", - "form-data-encoder", - "formdata-node", - "node-fetch" - ] - }, - "p-limit@6.2.0": { - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "dependencies": [ - "yocto-queue" - ] - }, - "phonemizer@1.2.1": { - "integrity": "sha512-v0KJ4mi2T4Q7eJQ0W15Xd4G9k4kICSXE8bpDeJ8jisL4RyJhNWsweKTOi88QXFc4r4LZlz5jVL5lCHhkpdT71A==" - }, - "platform@1.3.6": { - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, - "protobufjs@7.5.0": { - "integrity": "sha512-Z2E/kOY1QjoMlCytmexzYfDm/w5fKAiRwpSzGtdnXW1zC88Z2yXazHHrOtwCzn+7wSxyE8PYM4rvVcMphF9sOA==", - "dependencies": [ - "@protobufjs/aspromise", - "@protobufjs/base64", - "@protobufjs/codegen", - "@protobufjs/eventemitter", - "@protobufjs/fetch", - "@protobufjs/float", - "@protobufjs/inquire", - "@protobufjs/path", - "@protobufjs/pool", - "@protobufjs/utf8", - "@types/node@22.12.0", - "long" - ] - }, - "roarr@2.15.4": { - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dependencies": [ - "boolean", - "detect-node", - "globalthis", - "json-stringify-safe", - "semver-compare", - "sprintf-js" - ] - }, - "semver-compare@1.0.0": { - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" - }, - "semver@7.7.1": { - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" - }, - "serialize-error@7.0.1": { - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dependencies": [ - "type-fest" - ] - }, - "sharp@0.34.1": { - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", - "dependencies": [ - "@img/sharp-darwin-arm64", - "@img/sharp-darwin-x64", - "@img/sharp-libvips-darwin-arm64", - "@img/sharp-libvips-darwin-x64", - "@img/sharp-libvips-linux-arm", - "@img/sharp-libvips-linux-arm64", - "@img/sharp-libvips-linux-ppc64", - "@img/sharp-libvips-linux-s390x", - "@img/sharp-libvips-linux-x64", - "@img/sharp-libvips-linuxmusl-arm64", - "@img/sharp-libvips-linuxmusl-x64", - "@img/sharp-linux-arm", - "@img/sharp-linux-arm64", - "@img/sharp-linux-s390x", - "@img/sharp-linux-x64", - "@img/sharp-linuxmusl-arm64", - "@img/sharp-linuxmusl-x64", - "@img/sharp-wasm32", - "@img/sharp-win32-ia32", - "@img/sharp-win32-x64", - "color", - "detect-libc", - "semver" - ] - }, - "simple-swizzle@0.2.2": { - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": [ - "is-arrayish" - ] - }, - "sprintf-js@1.1.3": { - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, - "tar@7.4.3": { - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dependencies": [ - "@isaacs/fs-minipass", - "chownr", - "minipass", - "minizlib", - "mkdirp", - "yallist" - ] - }, - "tr46@0.0.3": { - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "tslib@2.8.1": { - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "type-fest@0.13.1": { - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==" - }, - "undici-types@5.26.5": { - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "undici-types@6.20.0": { - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - }, - "web-streams-polyfill@4.0.0-beta.3": { - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" - }, - "webidl-conversions@3.0.1": { - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url@5.0.0": { - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": [ - "tr46", - "webidl-conversions" - ] - }, - "yallist@5.0.0": { - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==" - }, - "yocto-queue@1.2.1": { - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==" - }, - "zod-to-json-schema@3.24.5_zod@3.24.4": { - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dependencies": [ - "zod" - ] - }, - "zod@3.24.4": { - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==" - } - }, - "remote": { - "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.5.1/+esm": "fb0a2ba5088bbde44cddff091a0d1844147cd08c75c75985df70672c7151efd5", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/+esm": "cf3368de4ad22103fe3083600aa9a1bb764d12a0795b2994110836fb5b9091e4", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.js/+esm": "cf3368de4ad22103fe3083600aa9a1bb764d12a0795b2994110836fb5b9091e4", - "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.web.js": "6067712fe4c43cdb36c762f860a5ab8d96ea1d9c8882466438eb9f3d0d20be8f", - "https://cdn.jsdelivr.net/npm/onnxruntime-common/+esm": "54aea20a2aaa80823ca7c15c084008e838fe74b61f3026d7d5e0b0187b6b9fcb", - "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0-dev.20250409-89f8206ba4/+esm": "53bf11d80531b341f46d4dc695a3d143d8b4c71d30e43905f9588db9804832db", - "https://cdn.jsdelivr.net/npm/phonemizer@1.2.1/+esm": "b8b4327641797c57274be04e4c82a561dd1e63ec7d37bcbb9820f8be1e332696" - } -} diff --git a/supabase/functions/generate-tts/index.ts b/supabase/functions/generate-tts/index.ts deleted file mode 100644 index f6d7ae2..0000000 --- a/supabase/functions/generate-tts/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Import necessary modules -import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { KokoroTTS } from "https://cdn.jsdelivr.net/npm/kokoro-js@1.2.1/dist/kokoro.js/+esm"; - -console.log("generate-tts function initialized"); - -Deno.serve(async (req) => { - if (req.method === "OPTIONS") { - // Handle preflight requests - return new Response(null, { - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", - }, - }); - } - - try { - console.log("Received request for TTS generation"); - // Parse the incoming request body - const { text, voice, index } = await req.json(); - console.log("Parsed request body:", { text, voice, index }); - - // Validate the input - if (!text || !voice) { - return new Response( - JSON.stringify({ error: "Missing required parameters: text or voice" }), - { status: 400, headers: { "Content-Type": "application/json" } } - ); - } - - console.log(`Generating TTS for text: "${text}" with voice: "${voice}"`); - - // Initialize KokoroTTS - const model_id = "onnx-community/Kokoro-82M-v1.0-ONNX"; - const tts = await KokoroTTS.from_pretrained(model_id, { - dtype: "fp32", - }); - - // Generate the speech audio - const audio = await tts.generate(text, { - voice, - }); - - const arrayBuffer = await audio.toWav(); - - // audioUrl should be the base64 encoded audio blob - const audioUrl = `data:audio/wav;base64,${btoa( - String.fromCharCode(...new Uint8Array(arrayBuffer)) - )}`; - - console.log(`TTS generated successfully for index: ${index}`); - - // Return the audio URL - return new Response(JSON.stringify({ audioUrl }), { - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - console.error("Error generating TTS:", error); - - // Return an error response - return new Response(JSON.stringify({ error: "Failed to generate TTS" }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } -}); diff --git a/supabase/functions/process-document/index.ts b/supabase/functions/process-document/index.ts deleted file mode 100644 index e43cdb9..0000000 --- a/supabase/functions/process-document/index.ts +++ /dev/null @@ -1,510 +0,0 @@ -import "jsr:@supabase/functions-js/edge-runtime.d.ts"; -import { createClient } from "jsr:@supabase/supabase-js@2"; -import { Mistral } from "npm:@mistralai/mistralai"; -import pLimit from "npm:p-limit"; -export const corsHeaders = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": - "authorization, x-client-info, apikey, content-type", -}; -const apiKey = Deno.env.get("MISTRAL_API_KEY"); -const client = new Mistral({ - apiKey: apiKey, -}); -const PROCESSING_PROMPT = ` -You are a document processing AI. Your task is to process the Markdown text scanned from a document page and return it in a clean and structured format. - -The textual page data should only be returned in valid Markdown format. Use proper headings and subheadings to structure the content. **Do not add headings if they do not exist in the original text.** If there is a title to the document, it should be the first heading. -Any images should be included. -Do not return the Markdown as a code block, only as a raw string, without any new lines. - -No data or information should ever be removed, it should only be processed and formatted. - -There are in-text citations/references in the text, remove them from the text (**but most importantly, keep the reference number in the text. use a tag**) and put them into an object where the key is the reference number and the value is the text. - -The Markdown should be human-readable and well-formatted. The markdown string should properly sanitized and should not break a JSON parser when returned as the final format. - -Return the final result as a text object with the following structure (without code block formatting): - -""" - - ---------- - -{ - "citations": [ - { - "number": 1, // The number as it appears in the text - "text": "Citation text 1" // Ensure any JSON-breaking characters are properly escaped - }, - { - "number": 2, - "text": "Citation text 2" - } - ] -} -""" - -Do not return the text object as a code block, only as a raw string. -`; -Deno.serve(async (req) => { - if (req.method === "OPTIONS") { - console.log("Handling OPTIONS request..."); - return new Response(null, { - headers: { - ...corsHeaders, - "Access-Control-Allow-Methods": "POST, OPTIONS", - }, - }); - } - - if (req.method === "POST") { - console.log("Processing POST request..."); - const { body, writable } = new TransformStream(); - const writer = writable.getWriter(); - - // Set up the SSE response - const headers = new Headers({ - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - ...corsHeaders, - }); - - let activeOperations = 0; // Track active operations - let streamClosed = false; // Track if the stream is closed - - const sendEvent = async (event, data) => { - if (streamClosed) { - console.warn("Attempted to write to a closed stream."); - return; - } - const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`; - console.log("Sending event:", message); - try { - activeOperations++; - await writer.write(new TextEncoder().encode(message)); - } catch (error) { - console.error("Error writing to stream:", error); - } finally { - activeOperations--; - } - }; - - // Start streaming updates - sendEvent("status", { - message: "Initializing...", - }); - - try { - const supabase = createClient( - Deno.env.get("SUPABASE_URL"), - Deno.env.get("SUPABASE_ANON_KEY") - ); - - const supabaseServer = createClient( - Deno.env.get("SUPABASE_URL"), - Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") - ); - - const formData = await req.formData(); - const accessToken = formData.get("access_token"); - const refreshToken = formData.get("refresh_token"); - var reprocessing = false; - var uuid = crypto.randomUUID(); - - const { - data: { user }, - error: sessionError, - } = await supabase.auth.setSession({ - access_token: accessToken, - refresh_token: refreshToken, - }); - - if (sessionError) { - console.error("Error setting session:", sessionError); - sendEvent("error", { - message: "Error setting session", - error: sessionError, - }); - throw new Error("Setting session failed"); - } - - if (formData.has("id")) { - console.log("Reprocessing document..."); - reprocessing = true; - console.log("File ID found in form data."); - sendEvent("status", { - message: "File ID found in form data.", - }); - const docId = formData.get("id"); - console.log("Document ID:", docId, formData); - const { data: documentData, error: documentError } = await supabase - .from("documents") - .select("*") - .eq("id", docId) - .single(); - - if (documentError) { - console.error("Error fetching document record:", documentError); - sendEvent("error", { - message: "Error fetching document record", - error: documentError, - }); - throw new Error("Document record fetch failed"); - } - - if (documentData) { - await supabase - .from("documents") - .update({ - is_processing: true, - }) - .eq("id", documentData.id); - uuid = documentData.id; - } else { - console.error("Document record not found."); - sendEvent("error", { - message: "Document record not found", - }); - throw new Error("Document record not found"); - } - - const { data: fileData, error: fileError } = await supabase.storage - .from("documents") - .download(`${user.id}/${uuid}.pdf`); - - if (fileError) { - console.error("Error downloading file from storage:", fileError); - sendEvent("error", { - message: "Error downloading file from storage", - error: fileError, - }); - throw new Error("File download failed"); - } - - console.log("File downloaded from storage:", fileData); - sendEvent("status", { - message: "File downloaded from storage", - fileData, - }); - - formData.set("file", fileData); - } - - if (!formData.has("file")) { - console.error("File not found in form data."); - sendEvent("error", { - message: "File not found in form data", - }); - throw new Error("File not found"); - } - if (!formData.has("access_token") || !formData.has("refresh_token")) { - console.error("Access token or refresh token not found in form data."); - sendEvent("error", { - message: "Access token or refresh token not found in form data", - }); - throw new Error("Tokens not found"); - } - - const file = formData.get("file") as File; - const fileName = file.name; - - console.log("Generated UUID:", uuid); - sendEvent("status", { - message: "Generated UUID", - uuid, - }); - - console.log("Authenticated user:", user); - sendEvent("status", { - message: "Authenticated user", - user, - }); - - if (!reprocessing) { - const { data: storageData, error: storageError } = - await supabase.storage - .from("documents") - .upload(`${user.id}/${uuid}.pdf`, file); - - if (storageError) { - console.error("Error uploading file to storage:", storageError); - sendEvent("error", { - message: "Error uploading file to storage", - error: storageError, - }); - throw new Error("File upload failed"); - } - - console.log("File uploaded to storage:", storageData); - sendEvent("status", { - message: "File uploaded to storage", - storageData, - }); - - const { error: docError } = await supabase.from("documents").insert({ - id: uuid, - file_name: file.name, - owner: user.id, - raw_file: storageData.id, - is_processing: true, - }); - - if (docError) { - console.error("Error inserting document record:", docError); - sendEvent("error", { - message: "Error inserting document record", - error: docError, - }); - throw new Error("Document record insertion failed"); - } - - console.log("Document record inserted successfully."); - sendEvent("status", { - message: "Document record inserted successfully", - }); - } else { - console.log("Reprocessing document..."); - sendEvent("status", { - message: "Reprocessing document", - }); - - const { error: docError } = await supabase - .from("documents") - .update({ - is_processing: true, - }) - .eq("id", uuid); - if (docError) { - console.error("Error updating document record:", docError); - sendEvent("error", { - message: "Error updating document record", - error: docError, - }); - throw new Error("Document record update failed"); - } - console.log("Document record updated successfully."); - sendEvent("status", { - message: "Document record updated successfully", - }); - } - - console.log("Uploading file to Mistral..."); - sendEvent("status", { - message: "Uploading file to Mistral...", - }); - - const uploaded_pdf = await client.files.upload({ - file: { - fileName, - content: file, - }, - purpose: "ocr", - }); - - console.log("File uploaded to Mistral:", uploaded_pdf); - sendEvent("status", { - message: "File uploaded to Mistral", - uploaded_pdf, - }); - - const signedUrl = await client.files.getSignedUrl({ - fileId: uploaded_pdf.id, - }); - - console.log("Generated signed URL:", signedUrl); - sendEvent("status", { - message: "Generated signed URL", - signedUrl, - }); - - console.log("Processing OCR..."); - sendEvent("status", { - message: "Processing OCR...", - }); - - const ocrResponse = await client.ocr.process({ - model: "mistral-ocr-latest", - document: { - type: "document_url", - documentUrl: signedUrl.url, - }, - }); - - console.log("OCR response received:", ocrResponse); - sendEvent("status", { - message: "OCR response received", - ocrResponse, - }); - - const limit = pLimit(2); - const promises = []; - - for (const page of ocrResponse.pages) { - console.log("Processing page:", page.index); - sendEvent("status", { - message: `Processing page ${page.index}`, - }); - - const pagePromise = limit(async () => { - console.log(`Processing page ${page.index} with Mistral...`); - - const response = await client.chat.complete({ - model: "mistral-small-latest", - messages: [ - { - role: "system", - content: [ - { - type: "text", - text: PROCESSING_PROMPT, - }, - ], - }, - { - role: "user", - content: [ - { - type: "text", - text: page.markdown, - }, - ], - }, - ], - }); - - if (!response.choices) { - console.error("No choices in response for page:", page.index); - sendEvent("error", { - message: `No choices in response for page ${page.index}`, - }); - return; - } - - console.log("Response received for page:", page.index); - sendEvent("status", { - message: `Response received for page ${page.index}`, - }); - - const imageData = {}; - if (page.images.length > 0) { - console.log( - `Processing ${page.images.length} images for page ${page.index}...` - ); - sendEvent("status", { - message: `Processing images for page ${page.index}`, - }); - for (const img of page.images) { - imageData[img.id] = img.imageBase64; - } - } - - if (response.choices[0].message.content) { - // remove any potential code block formatting from the content - console.log( - `[${page.index}] ${response.choices[0].message.content}` - ); - const split = - response.choices[0].message.content.split("---------"); - - const content = split[0].trim(); - const citationsStr = split[1]?.trim() || "{}"; - console.log(`[${page.index}] Citations: ${citationsStr}`); - const citations = JSON.parse(citationsStr).citations || {}; - - console.log("Generating Markdown for page:", page.index); - sendEvent("status", { - message: `Generating Markdown for page ${page.index}`, - }); - const markdown = replaceImagesInMarkdown(content, imageData); - - return { - ...page, - markdown, - citations, - }; - } else { - console.error("Message content is undefined for page:", page.index); - sendEvent("error", { - message: `Message content is undefined for page ${page.index}`, - }); - } - }); - - promises.push(pagePromise); - } - - console.log("Waiting for all pages to be processed..."); - sendEvent("status", { - message: "Waiting for all pages to be processed...", - }); - - const results = await Promise.all(promises); - - console.log("All pages processed. Results:", results); - sendEvent("status", { - message: "All pages processed", - results, - }); - - const sortedResults = results.sort((a, b) => a.index - b.index); - console.log("Sorted results:", sortedResults); - sendEvent("status", { message: "Sorted results", sortedResults }); - - const { data, error } = await supabase - .from("documents") - .update({ - ocr_data: sortedResults, - is_processing: false, - }) - .eq("id", uuid); - - if (error) { - console.error("Error updating document record:", error); - sendEvent("error", { - message: "Error updating document record", - error, - }); - throw new Error("Document record update failed"); - } - - console.log("Closing SSE stream..."); - } catch (error) { - console.error("Error during processing:", error); - sendEvent("error", { - message: "Error during processing", - error, - }); - } finally { - // Wait for all active operations to complete before closing the stream - const interval = setInterval(() => { - if (activeOperations === 0) { - clearInterval(interval); - streamClosed = true; - writer.close(); - } - }, 100); // Check every 100ms - } - - return new Response(body, { - headers, - }); - } - - console.error("Method not allowed:", req.method); - return new Response("Method not allowed", { - status: 405, - }); -}); -function replaceImagesInMarkdown(markdownStr, imagesDict) { - console.log("Replacing images in Markdown..."); - for (const [imgName, base64Str] of Object.entries(imagesDict)) { - markdownStr = markdownStr.replace( - new RegExp(`!\\[${imgName}\\]\\(${imgName}\\)`, "g"), - `![${imgName}](${base64Str})` - ); - } - console.log("Image replacement complete."); - return markdownStr; -} diff --git a/tailwind.config.ts b/tailwind.config.ts index fab6d90..1e92049 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,7 +1,5 @@ -import type { Config } from "tailwindcss"; - const config = { - darkMode: ["class"], + darkMode: "class", content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", @@ -75,6 +73,6 @@ const config = { }, }, plugins: [require("tailwindcss-animate")], -} satisfies Config; +}; export default config;