diff --git a/apps/tools/.env.example b/apps/tools/.env.example deleted file mode 100644 index 61fa2fdb..00000000 --- a/apps/tools/.env.example +++ /dev/null @@ -1,20 +0,0 @@ -# Environment variables example -# Copy this file to .env.local and update the values as needed - -# Node -NODE_ENV=development - -# Public -NEXT_PUBLIC_SITE_URL=http://localhost:3001 - -# Server -DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut" -BETTER_AUTH_SECRET=your_better_auth_secret - -UPSTASH_REDIS_REST_URL=http://localhost:8079 -UPSTASH_REDIS_REST_TOKEN=example_token_here - -CLOUDFLARE_ACCOUNT_ID=your_account_id_here -R2_ACCESS_KEY_ID=your_access_key_here -R2_SECRET_ACCESS_KEY=your_secret_key_here -R2_BUCKET_NAME=opencut-tools \ No newline at end of file diff --git a/apps/tools/.gitignore b/apps/tools/.gitignore deleted file mode 100644 index 98519da5..00000000 --- a/apps/tools/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Turborepo -.turbo - -# Env vars -.env* -!.env.example - -# Next -.next/ \ No newline at end of file diff --git a/apps/tools/README.md b/apps/tools/README.md deleted file mode 100644 index c0dfd9ac..00000000 --- a/apps/tools/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# OpenCut Tools - -## Why this exists - -I needed to replace a color in an image the other day. - -Spent 20 minutes trying to find a tool that wasn't shit. - -**Site 1:** Can't paste images. Have to download my clipboard image, upload it, then delete it. Immediately close tab. - -**Site 2:** Can't paste images either. Close. - -**Site 3:** Finally! Paste support! Oh wait—massive "UPGRADE TO PRO NOW" popup blocks the entire screen. Close popup. UI has 500 buttons everywhere. Okay whatever, let's try the color picker... **NO EYEDROPPER**. A color replacement tool with no way to select colors from the image. The most obvious feature. Not there. - -Now I have to open ANOTHER site just to extract the hex code from my image. Google "extract color from image". Find a site. Upload image again. Get the color. Copy hex code. - -Go back to the replacement site. Paste the hex. Click replace. - -**It didn't even work.** - -Started over. Sat there for 10 minutes and it eventually worked. - ---- - -## The no-bullshit version - -Every image/video tool on the internet has the same problems: - -- Can't paste images -- Paywalls for basic features -- Aggressive popups and ads -- Bloated UIs -- Missing obvious features (eyedroppers, copy result, etc) -- Can't batch process -- Tracking your every move -- Login to use the tool - -**OpenCut Tools is different:** - -- ✅ Paste images directly -- ✅ Every tool has the features you'd expect -- ✅ Copy results -- ✅ No login required for free tier -- ✅ Minimal, clean UI -- ✅ Privacy-first (we don't want your data) -- ✅ Batch processing -- ✅ No ads, no popups, no bullshit - -Free for most users. Premium ($8/month) for power features like batch processing and advanced tools. - ---- - -## The mission - -Same as OpenCut: build tools that don't suck and actually respect your privacy. - -No VC money. No dark patterns. No selling your data. - -Just tools that work the way they should have from the beginning. diff --git a/apps/tools/components.json b/apps/tools/components.json deleted file mode 100644 index 3289f237..00000000 --- a/apps/tools/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/apps/tools/drizzle.config.ts b/apps/tools/drizzle.config.ts deleted file mode 100644 index 10e6c375..00000000 --- a/apps/tools/drizzle.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Config } from "drizzle-kit"; -import * as dotenv from "dotenv"; -import { toolsEnv } from "@opencut/env/tools"; - -// Load the right env file based on environment -if (toolsEnv.NODE_ENV === "production") { - dotenv.config({ path: ".env.production" }); -} else { - dotenv.config({ path: ".env.local" }); -} - -export default { - schema: "./src/schema.ts", - dialect: "postgresql", - migrations: { - table: "drizzle_migrations", - }, - dbCredentials: { - url: toolsEnv.DATABASE_URL, - }, - out: "./migrations", - strict: toolsEnv.NODE_ENV === "production", -} satisfies Config; diff --git a/apps/tools/next-env.d.ts b/apps/tools/next-env.d.ts deleted file mode 100644 index 830fb594..00000000 --- a/apps/tools/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/tools/next.config.ts b/apps/tools/next.config.ts deleted file mode 100644 index 4c92fbd2..00000000 --- a/apps/tools/next.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { NextConfig } from "next"; -import { withBotId } from "botid/next/config"; - -const nextConfig: NextConfig = { - compiler: { - removeConsole: process.env.NODE_ENV === "production", - }, - reactStrictMode: true, - productionBrowserSourceMaps: true, - output: "standalone", -}; - -export default withBotId(nextConfig); diff --git a/apps/tools/package.json b/apps/tools/package.json deleted file mode 100644 index 79eb1012..00000000 --- a/apps/tools/package.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "@opencut/tools", - "version": "0.1.0", - "private": true, - "packageManager": "bun@1.2.18", - "scripts": { - "dev": "next dev --turbopack --port 3001", - "build": "next build", - "start": "next start --port 3001", - "lint": "biome check src/", - "lint:fix": "biome check src/ --write", - "format": "biome format src/ --write", - "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit migrate", - "db:push:local": "cross-env NODE_ENV=development drizzle-kit push", - "db:push:prod": "cross-env NODE_ENV=production drizzle-kit push" - }, - "dependencies": { - "@hookform/resolvers": "^3.9.1", - "@opencut/env": "workspace:*", - "@opencut/ui": "workspace:*", - "@radix-ui/react-separator": "^1.1.7", - "@upstash/ratelimit": "^2.0.6", - "@upstash/redis": "^1.35.4", - "aws4fetch": "^1.0.20", - "better-auth": "^1.2.7", - "drizzle-orm": "^0.44.2", - "postgres": "^3.4.5", - "botid": "^1.4.2", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.0.0", - "dotenv": "^16.5.0", - "embla-carousel-react": "^8.5.1", - "input-otp": "^1.4.1", - "lucide-react": "^0.468.0", - "motion": "^12.18.1", - "nanoid": "^5.1.5", - "next": "^15.5.7", - "next-themes": "^0.4.4", - "pg": "^8.16.2", - "radix-ui": "^1.4.2", - "mediabunny": "^1.9.3", - "react": "^18.2.0", - "react-day-picker": "^8.10.1", - "react-dom": "^18.2.0", - "react-hook-form": "^7.54.0", - "react-icons": "^5.4.0", - "react-markdown": "^10.1.0", - "react-phone-number-input": "^3.4.11", - "react-resizable-panels": "^2.1.7", - "recharts": "^2.14.1", - "rehype-autolink-headings": "^7.1.0", - "rehype-parse": "^9.0.1", - "rehype-sanitize": "^6.0.0", - "rehype-slug": "^6.0.0", - "rehype-stringify": "^10.0.1", - "sonner": "^1.7.1", - "tailwind-merge": "^2.5.5", - "tailwindcss-animate": "^1.0.7", - "vaul": "^1.1.1", - "zod": "^3.25.67", - "zustand": "^5.0.2" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.11", - "@tailwindcss/typography": "^0.5.16", - "@types/bun": "latest", - "@types/node": "^24.2.1", - "@types/pg": "^8.15.4", - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", - "cross-env": "^7.0.3", - "drizzle-kit": "^0.31.4", - "dotenv": "^16.5.0", - "postcss": "^8", - "prettier": "^3.6.2", - "prettier-plugin-tailwindcss": "^0.6.14", - "tailwindcss": "^4.1.11", - "tsx": "^4.7.1", - "typescript": "^5.8.3" - } -} \ No newline at end of file diff --git a/apps/tools/postcss.config.mjs b/apps/tools/postcss.config.mjs deleted file mode 100644 index 79bcf135..00000000 --- a/apps/tools/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - "@tailwindcss/postcss": {}, - }, -}; - -export default config; diff --git a/apps/tools/public/bg-remover-2.png b/apps/tools/public/bg-remover-2.png deleted file mode 100644 index 159fe0ea..00000000 Binary files a/apps/tools/public/bg-remover-2.png and /dev/null differ diff --git a/apps/tools/public/bg-remover.png b/apps/tools/public/bg-remover.png deleted file mode 100644 index d5c8e2b1..00000000 Binary files a/apps/tools/public/bg-remover.png and /dev/null differ diff --git a/apps/tools/public/favicon.ico b/apps/tools/public/favicon.ico deleted file mode 100644 index 9b2cba8f..00000000 Binary files a/apps/tools/public/favicon.ico and /dev/null differ diff --git a/apps/tools/public/logo.svg b/apps/tools/public/logo.svg deleted file mode 100644 index 76a6535a..00000000 --- a/apps/tools/public/logo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/apps/tools/src/app/api/auth/[...all]/route.ts b/apps/tools/src/app/api/auth/[...all]/route.ts deleted file mode 100644 index 5deef204..00000000 --- a/apps/tools/src/app/api/auth/[...all]/route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { auth } from "@opencut/auth"; -import { toNextJsHandler } from "better-auth/next-js"; - -export const { POST, GET } = toNextJsHandler(auth); diff --git a/apps/tools/src/app/api/get-upload-url/route.ts b/apps/tools/src/app/api/get-upload-url/route.ts deleted file mode 100644 index 48e0dfc4..00000000 --- a/apps/tools/src/app/api/get-upload-url/route.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { z } from "zod"; -import { AwsClient } from "aws4fetch"; -import { nanoid } from "nanoid"; -import { env } from "@/env"; -import { checkRateLimit } from "@/lib/rate-limit"; -import { isTranscriptionConfigured } from "@/lib/transcription-utils"; - -const uploadRequestSchema = z.object({ - fileExtension: z.enum(["wav", "mp3", "m4a", "flac"], { - errorMap: () => ({ - message: "File extension must be wav, mp3, m4a, or flac", - }), - }), -}); - -const apiResponseSchema = z.object({ - uploadUrl: z.string().url(), - fileName: z.string().min(1), -}); - -export async function POST(request: NextRequest) { - try { - const { limited } = await checkRateLimit({ request }); - if (limited) { - return NextResponse.json({ error: "Too many requests" }, { status: 429 }); - } - - const transcriptionCheck = isTranscriptionConfigured(); - if (!transcriptionCheck.configured) { - console.error( - "Missing environment variables:", - JSON.stringify(transcriptionCheck.missingVars) - ); - - return NextResponse.json( - { - error: "Transcription not configured", - message: `Auto-captions require environment variables: ${transcriptionCheck.missingVars.join(", ")}. Check README for setup instructions.`, - }, - { status: 503 } - ); - } - - const rawBody = await request.json().catch(() => null); - if (!rawBody) { - return NextResponse.json( - { error: "Invalid JSON in request body" }, - { status: 400 } - ); - } - - const validationResult = uploadRequestSchema.safeParse(rawBody); - if (!validationResult.success) { - return NextResponse.json( - { - error: "Invalid request parameters", - details: validationResult.error.flatten().fieldErrors, - }, - { status: 400 } - ); - } - - const { fileExtension } = validationResult.data; - - const client = new AwsClient({ - accessKeyId: env.R2_ACCESS_KEY_ID, - secretAccessKey: env.R2_SECRET_ACCESS_KEY, - }); - - const timestamp = Date.now(); - const fileName = `audio/${timestamp}-${nanoid()}.${fileExtension}`; - - const url = new URL( - `https://${env.R2_BUCKET_NAME}.${env.CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com/${fileName}` - ); - - url.searchParams.set("X-Amz-Expires", "3600"); // 1 hour expiry - - const signed = await client.sign(new Request(url, { method: "PUT" }), { - aws: { signQuery: true }, - }); - - if (!signed.url) { - throw new Error("Failed to generate presigned URL"); - } - - const responseData = { - uploadUrl: signed.url, - fileName, - }; - - const responseValidation = apiResponseSchema.safeParse(responseData); - if (!responseValidation.success) { - console.error( - "Invalid API response structure:", - responseValidation.error - ); - return NextResponse.json( - { error: "Internal response formatting error" }, - { status: 500 } - ); - } - - return NextResponse.json(responseValidation.data); - } catch (error) { - console.error("Error generating upload URL:", error); - return NextResponse.json( - { - error: "Failed to generate upload URL", - message: - error instanceof Error - ? error.message - : "An unexpected error occurred", - }, - { status: 500 } - ); - } -} \ No newline at end of file diff --git a/apps/tools/src/app/api/health/route.ts b/apps/tools/src/app/api/health/route.ts deleted file mode 100644 index ecb9ce04..00000000 --- a/apps/tools/src/app/api/health/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextRequest } from "next/server"; - -export async function GET(request: NextRequest) { - return new Response("OK", { status: 200 }); -} diff --git a/apps/tools/src/app/bg-remover/client-page copy.tsx b/apps/tools/src/app/bg-remover/client-page copy.tsx deleted file mode 100644 index 004a22dc..00000000 --- a/apps/tools/src/app/bg-remover/client-page copy.tsx +++ /dev/null @@ -1,322 +0,0 @@ -"use client"; -import { Header } from "@/components/header"; -import { DownloadIcon } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { UploadIcon } from "lucide-react"; -import { useFileUpload } from "@opencut/hooks/use-file-upload"; -import { useFilePaste } from "@opencut/hooks/use-file-paste"; -import { useState, useEffect, useRef } from "react"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { cn, generateUUID } from "@/lib/utils"; - -interface FileStatus { - file: File; - id: string; - progress: number; - status: "pending" | "processing" | "completed" | "error"; - batchId: string; -} - -export function BGRemoverClient() { - const [files, setFiles] = useState([]); - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [isFinishedUploading, setIsFinishedUploading] = useState(false); - - const handleFiles = ({ files: newFiles }: { files: File[] | FileList }) => { - const newFileStatuses: FileStatus[] = Array.from(newFiles).map((file) => ({ - file, - id: generateUUID(), - progress: 0, - status: "pending", - batchId: generateUUID(), - })); - setFiles((prev) => [...prev, ...newFileStatuses]); - console.log(newFileStatuses); - // setIsFinishedUploading(false); - // setIsPopoverOpen(true); - }; - - useEffect(() => { - const interval = setInterval(() => { - setFiles((prev) => { - if ( - !prev.some((f) => f.status === "pending" || f.status === "processing") - ) { - return prev; - } - return prev.map((f) => { - if (f.status === "completed") return f; - - if (f.status === "pending") { - return { ...f, status: "processing", progress: 0 }; - } - - if (f.status === "processing") { - const newProgress = Math.min( - f.progress + Math.random() * 15 + 5, - 100, - ); - return { - ...f, - progress: newProgress, - status: newProgress >= 100 ? "completed" : "processing", - }; - } - return f; - }); - }); - }, 500); - return () => clearInterval(interval); - }, []); - - useEffect(() => { - if (files.length > 0 && files.every((f) => f.status === "completed")) { - setIsFinishedUploading(true); - } - }, [files]); - - const { isDragOver, openFilePicker, fileInputProps, dragProps } = - useFileUpload({ - accept: "image/*", - multiple: false, - onFilesSelected: (files) => handleFiles({ files }), - }); - - useFilePaste({ onFilesPaste: (files) => handleFiles({ files }) }); - - return ( -
-
- - -
- } - /> -
- -
- BG Remover - BG Remover - BG Remover -
-
-
-

BG Remover

-

- Drag and drop, click to browse or paste from clipboard to upload - an image. -

-
- -
-
- - ); -} - -const FilesPopover = ({ - files, - isPopoverOpen, - setIsPopoverOpen, - isFinishedUploading, -}: { - files: FileStatus[]; - isPopoverOpen: boolean; - setIsPopoverOpen: (open: boolean) => void; - isFinishedUploading: boolean; -}) => { - const scrollContainerRef = useRef(null); - const prevFileLengthRef = useRef(0); - - useEffect(() => { - if (files.length > prevFileLengthRef.current) { - const scrollToBottom = () => { - scrollContainerRef.current?.scrollTo({ - top: scrollContainerRef.current.scrollHeight, - behavior: "smooth", - }); - }; - - if (scrollContainerRef.current) { - scrollToBottom(); - } else { - // popover just opened, wait for open animation to finish - setTimeout(scrollToBottom, 100); - } - } - prevFileLengthRef.current = files.length; - }, [files.length]); - - const isProcessing = files.some( - (f) => f.status === "processing" || f.status === "pending", - ); - - const inProgressFiles = files.filter( - (f) => f.status === "processing" || f.status === "pending", - ); - const overallProgress = - inProgressFiles.length > 0 - ? inProgressFiles.reduce((acc, f) => acc + f.progress, 0) / - inProgressFiles.length - : 0; - - const hasCompletedFiles = files.some((f) => f.status === "completed"); - const canDownloadAll = hasCompletedFiles; - - return ( - - - - - { - const target = e.target as HTMLElement; - const isInteractive = - target.closest("button") || - target.closest("a") || - target.closest("p") || - target.closest("h1") || - target.closest("h2") || - target.closest("h3") || - target.closest("h4") || - target.closest("h5") || - target.closest("h6") || - target.closest("input") || - target.closest("[role='button']") || - target.tagName === "IMG" || - target.tagName === "VIDEO"; - - if (isInteractive) { - e.preventDefault(); - } - }} - > -
-
-

Downloads

-
-
- {files.length === 0 && ( -
- No files uploaded yet -
- )} -
- {files.map((fileStatus) => ( - - ))} -
- -
- {files.length > 0 && ( - - )} -
-
-
-
-
- ); -}; - -const ProgressIndicator = ({ progress }: { progress: number }) => ( - - - -); - -const FileItem = ({ fileStatus }: { fileStatus: FileStatus }) => ( -
- {fileStatus.file.name} -
-
-

- {fileStatus.file.name.split(".").slice(0, -1).join(".")} -

- {fileStatus.status === "completed" ? ( - - ) : ( -

- {Math.round(fileStatus.progress)}% -

- )} -
-
-
-); diff --git a/apps/tools/src/app/bg-remover/client-page.tsx b/apps/tools/src/app/bg-remover/client-page.tsx deleted file mode 100644 index 9381b851..00000000 --- a/apps/tools/src/app/bg-remover/client-page.tsx +++ /dev/null @@ -1,147 +0,0 @@ -"use client"; -import { Header } from "@/components/header"; -import { DownloadIcon } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { UploadIcon } from "lucide-react"; -import { useFileUpload } from "@opencut/hooks/use-file-upload"; -import { useFilePaste } from "@opencut/hooks/use-file-paste"; -import { useState, useEffect, useRef } from "react"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { cn, generateUUID } from "@/lib/utils"; -import { motion, AnimatePresence } from "motion/react"; - -interface FileStatus { - file: File; - id: string; - progress: number; - status: "pending" | "processing" | "completed" | "error"; - batchId: string; -} - -export function BGRemoverClient() { - const [files, setFiles] = useState([]); - - const handleFiles = ({ files: newFiles }: { files: File[] | FileList }) => { - const newFileStatuses: FileStatus[] = Array.from(newFiles).map((file) => ({ - file, - id: generateUUID(), - progress: 0, - status: "pending", - batchId: generateUUID(), - })); - setFiles((prev) => [...prev, ...newFileStatuses]); - console.log(newFileStatuses); - }; - - const { isDragOver, openFilePicker, fileInputProps, dragProps } = - useFileUpload({ - accept: "image/*", - multiple: false, - onFilesSelected: (files) => handleFiles({ files }), - }); - - useFilePaste({ onFilesPaste: (files) => handleFiles({ files }) }); - - return ( -
-
-
- - {files.length === 0 ? ( - - -
- BG Remover - BG Remover - BG Remover -
-
-
-

BG Remover

-

- Drag and drop, click to browse or paste from clipboard to - upload an image. -

-
- -
-
- ) : ( - -
-

Processing Images...

-
- {files.map((fileStatus) => ( -
- {fileStatus.file.name} -
- {fileStatus.file.name} -
-
- ))} -
- -
-
- )} -
-
-
- ); -} diff --git a/apps/tools/src/app/bg-remover/flow.md b/apps/tools/src/app/bg-remover/flow.md deleted file mode 100644 index 11f7b1f2..00000000 --- a/apps/tools/src/app/bg-remover/flow.md +++ /dev/null @@ -1,7 +0,0 @@ -user uploads an image - -new view shows with the image - -they can "paint" on it (to create rough mask around subject to keep) - -button to "remove background" \ No newline at end of file diff --git a/apps/tools/src/app/bg-remover/page.tsx b/apps/tools/src/app/bg-remover/page.tsx deleted file mode 100644 index 76e969fc..00000000 --- a/apps/tools/src/app/bg-remover/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { BGRemoverClient } from "./client-page"; - -export default function BGRemoverPage() { - return ; -} diff --git a/apps/tools/src/app/globals.css b/apps/tools/src/app/globals.css deleted file mode 100644 index ed389192..00000000 --- a/apps/tools/src/app/globals.css +++ /dev/null @@ -1,242 +0,0 @@ -@import "tailwindcss"; - -/* Custom variant for dark mode */ -@custom-variant dark (&:where(.dark, .dark *)); - -/* Plugins */ -@plugin "@tailwindcss/typography"; -@plugin "tailwindcss-animate"; - -:root { - --background: hsl(0, 0%, 100%); - --foreground: hsl(0 0% 11%); - --card: hsl(0, 0%, 100%); - --card-foreground: hsl(0 0% 11%); - --popover: hsl(0, 0%, 100%); - --popover-foreground: hsl(0 0% 2%); - --primary: hsl(214, 92%, 51%); - --primary-foreground: hsl(0 0% 91%); - --secondary: hsl(216, 13%, 92%); - --secondary-foreground: hsl(0 0% 2%); - --muted: hsl(0 0% 85.1%); - --muted-foreground: hsl(0 0% 50%); - --accent: hsl(216, 13%, 92%); - --accent-foreground: hsl(0 0% 2%); - --destructive: hsl(0, 83%, 50%); - --destructive-foreground: hsl(0, 0%, 100%); - --border: hsl(0 0% 83%); - --input: hsl(0 0% 85.1%); - --ring: hsl(0, 0%, 55%); - --chart-1: hsl(220 70% 50%); - --chart-2: hsl(160 60% 45%); - --chart-3: hsl(30 80% 55%); - --chart-4: hsl(280 65% 60%); - --chart-5: hsl(340 75% 55%); - --sidebar-background: hsl(0 0% 96.1%); - --sidebar-foreground: hsl(0 0% 2%); - --sidebar-primary: hsl(0 0% 2%); - --sidebar-primary-foreground: hsl(0 0% 91%); - --sidebar-accent: hsl(0 0% 85.1%); - --sidebar-accent-foreground: hsl(0 0% 2%); - --sidebar-border: hsl(0 0% 85.1%); - --sidebar-ring: hsl(0 0% 16.9%); - --panel-background: hsl(216 13% 92%); - --panel-accent: hsl(216, 8%, 86%); - - --radius: 1rem; -} -.dark { - --background: hsl(0 0% 4%); - --foreground: hsl(0 0% 89%); - --card: hsl(0 0% 4%); - --card-foreground: hsl(0 0% 89%); - --popover: hsl(0 0% 14.9%); - --popover-foreground: hsl(0 0% 98%); - --primary: hsl(205, 84%, 53%); - --primary-foreground: hsl(0 0% 9%); - --secondary: hsl(0 0% 14.9%); - --secondary-foreground: hsl(0 0% 98%); - --muted: hsl(0 0% 14.9%); - --muted-foreground: hsl(0 0% 63.9%); - --accent: hsl(0 0% 14.9%); - --accent-foreground: hsl(0 0% 98%); - --destructive: hsl(0 100% 60%); - --destructive-foreground: hsl(0 0% 98%); - --border: hsl(0 0% 17%); - --input: hsl(0 0% 14.9%); - --ring: hsl(0 0% 83.1%); - --chart-1: hsl(220 70% 50%); - --chart-2: hsl(160 60% 45%); - --chart-3: hsl(30 80% 55%); - --chart-4: hsl(280 65% 60%); - --chart-5: hsl(340 75% 55%); - --sidebar-background: hsl(0 0% 3.9%); - --sidebar-foreground: hsl(0 0% 98%); - --sidebar-primary: hsl(0 0% 98%); - --sidebar-primary-foreground: hsl(0 0% 9%); - --sidebar-accent: hsl(0 0% 14.9%); - --sidebar-accent-foreground: hsl(0 0% 98%); - --sidebar-border: hsl(0 0% 14.9%); - --sidebar-ring: hsl(0 0% 83.1%); - --panel-background: hsl(0 0% 11%); - --panel-accent: hsl(0 0% 15%); -} - -@layer base { - /* - The default border color has changed to `currentcolor` in Tailwind CSS v4, - so we've added these compatibility styles to make sure everything still - looks the same as it did with Tailwind CSS v3. - - If we ever want to remove these styles, we need to add an explicit border - color utility to any element that depends on these defaults. - */ - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentcolor); - } - /* Other default base styles */ - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - /* Prevent back/forward swipe */ - overscroll-behavior-x: contain; - } -} - -@theme inline { - /* Responsive breakpoints */ - --breakpoint-xs: 30rem; - - /* Typography */ - --font-sans: var(--font-inter), sans-serif; - - /* Font sizes */ - --text-base: 0.95rem; - --text-base--line-height: calc(1.5 / 0.95); - --text-xs: 0.8rem; - --text-xs--line-height: calc(1 / 0.8); - - /* Border radius */ - --radius-lg: var(--radius); - --radius-md: calc(var(--radius) - 2px); - --radius-sm: calc(var(--radius) - 8px); - - /* Palette mapped to root design tokens */ - --color-background: var(--background); - --color-foreground: var(--foreground); - - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - - /* Chart colors */ - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - - /* Sidebar */ - --color-sidebar: var(--sidebar-background); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); - - /* Panel */ - --color-panel: var(--panel-background); - --color-panel-accent: var(--panel-accent); - - /* Animations */ - --animate-accordion-down: accordion-down 0.2s ease-out; - --animate-accordion-up: accordion-up 0.2s ease-out; - - @keyframes accordion-down { - from { - height: 0; - } - to { - height: var(--radix-accordion-content-height); - } - } - - @keyframes accordion-up { - from { - height: var(--radix-accordion-content-height); - } - to { - height: 0; - } - } -} - -@utility scrollbar-hidden { - -ms-overflow-style: none; - scrollbar-width: none; - &::-webkit-scrollbar { - display: none; - } -} - -@utility scrollbar-x-hidden { - -ms-overflow-style: none; - scrollbar-width: none; - &::-webkit-scrollbar:horizontal { - display: none; - } -} - -@utility scrollbar-y-hidden { - -ms-overflow-style: none; - scrollbar-width: none; - &::-webkit-scrollbar:vertical { - display: none; - } -} - -@utility scrollbar-thin { - &::-webkit-scrollbar { - width: 6px; - height: 8px; - } - &::-webkit-scrollbar-track { - background: transparent; - } - &::-webkit-scrollbar-thumb { - background: var(--border); - border-radius: 4px; - } - &::-webkit-scrollbar-thumb:hover { - background: var(--muted-foreground); - } -} diff --git a/apps/tools/src/app/layout.tsx b/apps/tools/src/app/layout.tsx deleted file mode 100644 index 0beeeba8..00000000 --- a/apps/tools/src/app/layout.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { ThemeProvider } from "next-themes"; -import Script from "next/script"; -import "./globals.css"; -import { Toaster } from "../components/ui/sonner"; -import { TooltipProvider } from "../components/ui/tooltip"; -import { baseMetaData } from "./metadata"; -import { BotIdClient } from "botid/client"; -import { toolsEnv } from "@opencut/env/tools"; -import { Inter } from "next/font/google"; - -const siteFont = Inter({ subsets: ["latin"] }); - -export const metadata = baseMetaData; - -const protectedRoutes = [ - { - path: "/none", - method: "GET", - }, -]; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - - - - - - {children} - -