diff --git a/README.md b/README.md
index 0638a66..38baace 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,14 @@
Virtual try-on lets shoppers see how clothing looks on them in real time, using just a webcam. Decart's `lucy_2_rt` model takes a live camera feed and a garment reference image, then streams back video of the person wearing that garment - all through a WebRTC connection with no server-side rendering. This repo provides drop-in examples so you can add try-on to your own app in minutes.
-Three production-ready Next.js examples that show how to integrate Decart's realtime virtual try-on. Each example is self-contained and runs independently.
+Four production-ready Next.js examples that show how to integrate Decart's realtime virtual try-on. Each example is self-contained and runs independently.
| Example | Use case | Integration style |
|---------|----------|-------------------|
| [**E-commerce**](examples/ecommerce/) | "Try it on" button on product pages | Simple - hardcoded prompts, modal overlay |
| [**Standalone**](examples/standalone/) | Dedicated try-on experience | Advanced - AI-generated prompts via LLM |
| [**Person Detection**](examples/person-detection/) | Kiosks and unattended displays | Auto-connect - only uses credits when someone is in frame |
+| [**Full-Featured**](examples/full-featured/) | Complete try-on experience | All features - person detection, AI prompts, file upload |
---
@@ -141,7 +142,7 @@ This is a good fallback, but for the best results we still recommend writing det
### Generating prompts with an LLM
-For garments where you don't have a pre-written prompt (e.g. user-uploaded images), you can use any vision LLM to auto-generate one from the garment image. The [standalone example](examples/standalone/) demonstrates this using OpenAI's GPT-4o-mini, but any vision-capable LLM works.
+For garments where you don't have a pre-written prompt (e.g. user-uploaded images), you can use any vision LLM to auto-generate one from the garment image. The [standalone example](examples/standalone/) demonstrates this using OpenAI's GPT-4o-mini, but any vision-capable LLM works (Claude, Gemini, etc.). A built-in Decart solution is coming soon.
The example sends both the garment image and a camera frame of the person to `/api/enhance-prompt`:
@@ -201,7 +202,7 @@ Your permanent `DECART_API_KEY` never leaves the server. The browser receives a
| Variable | Required | Purpose |
|----------|----------|---------|
| `DECART_API_KEY` | Yes | Creates client tokens for realtime WebRTC connections |
-| `OPENAI_API_KEY` | Standalone only | Powers `/api/enhance-prompt` - auto-generates prompts from garment images |
+| `OPENAI_API_KEY` | Standalone / Full-Featured | Powers `/api/enhance-prompt` - auto-generates prompts from garment images. Can be swapped for any vision-capable LLM. |
---
diff --git a/examples/full-featured/.env.example b/examples/full-featured/.env.example
new file mode 100644
index 0000000..d41b6c8
--- /dev/null
+++ b/examples/full-featured/.env.example
@@ -0,0 +1,2 @@
+DECART_API_KEY=sk_your_key_here
+OPENAI_API_KEY=sk_your_openai_key_here
diff --git a/examples/full-featured/README.md b/examples/full-featured/README.md
new file mode 100644
index 0000000..d569173
--- /dev/null
+++ b/examples/full-featured/README.md
@@ -0,0 +1,126 @@
+# Full-Featured Virtual Try-On
+
+> Combines all features: person detection, AI-generated prompts, file upload, and a product catalog. The most complete integration example.
+
+Auto-detects when a person is in the camera frame, generates try-on prompts with GPT-4o-mini, lets users upload their own garment images, and includes a product catalog sidebar — all in one app.
+
+
+---
+
+## Quick start
+
+### 1. Install dependencies
+
+```bash
+cd examples/full-featured
+npm install
+```
+
+### 2. Set your API keys
+
+```bash
+cp .env.example .env.local
+```
+
+Open `.env.local` and add both keys:
+
+```env
+DECART_API_KEY=sk_your_key_here
+OPENAI_API_KEY=sk_your_openai_key_here
+```
+
+> **Note:** This example requires both keys. The Decart key powers the realtime try-on, and the OpenAI key powers the prompt generation.
+
+### 3. Start the dev server
+
+```bash
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000). Your camera will start and MediaPipe will begin scanning. Step in front of the camera to trigger a connection, then click a product or upload your own garment.
+
+---
+
+## How it works
+
+```
+Page loads
+ → Camera starts automatically (getUserMedia)
+ → MediaPipe PoseLandmarker loads (WASM + WebGL, runs in browser)
+ → Every 1s: detectForVideo() checks for body landmarks
+ → Person detected (landmarks found)
+ → Fetch client token from /api/tokens
+ → Connect to Decart's lucy_2_rt model (WebRTC)
+ → User clicks a product OR uploads a garment image
+ → Capture a frame from the camera
+ → Send garment image + person frame to /api/enhance-prompt
+ → GPT-4o-mini generates a try-on prompt
+ → setImage(garment, prompt) sends the garment to the model
+ → AI video stream shows the user wearing the garment
+ → Person leaves (3 consecutive misses, ~3s)
+ → Disconnect from Decart (stops billing)
+ → Person returns → fresh token → reconnect → re-apply last garment
+```
+
+---
+
+## Features
+
+| Feature | Description |
+|---------|-------------|
+| **Person detection** | Auto-connects when someone is in frame, disconnects when they leave (saves credits) |
+| **AI-generated prompts** | Generates descriptive try-on prompts from the garment image + camera frame. This example uses GPT-4o-mini, but any vision-capable LLM works (Claude, Gemini, etc.). A built-in Decart solution is coming soon. |
+| **File upload** | Users can upload their own garment images and get AI-generated prompts automatically |
+| **Product catalog** | Sidebar with product grid, click to try on |
+| **Editable prompts** | View and edit the generated prompt, re-apply with one click |
+| **Auto-reconnect** | When a person leaves and returns, the last garment is automatically re-applied |
+
+---
+
+## Key files
+
+| File | Purpose |
+|------|---------|
+| `app/page.tsx` | Main orchestration — detection-driven connect/disconnect with AI prompt generation |
+| `components/CombinedView.tsx` | Video display with detection status badges, processing overlay, and prompt editor |
+| `components/ProductSidebar.tsx` | Product grid with file upload section |
+| `hooks/usePersonDetection.ts` | MediaPipe PoseLandmarker polling, exposes `personPresent` state |
+| `hooks/useDecartRealtime.ts` | Decart WebRTC connection management |
+| `hooks/useCamera.ts` | Camera access via getUserMedia |
+| `lib/enhance-prompt.ts` | Client-side helper for calling `/api/enhance-prompt` |
+| `app/api/enhance-prompt/route.ts` | Server-side GPT-4o-mini prompt generation |
+| `app/api/tokens/route.ts` | Server-side Decart client token creation |
+
+---
+
+## Customization
+
+### Tuning detection sensitivity
+
+In `hooks/usePersonDetection.ts`:
+
+- **`DETECTION_INTERVAL_MS`** (default: 1000) — how often to check for a person
+- **`MISS_THRESHOLD`** (default: 3) — consecutive missed detections before disconnecting
+
+### Add your own products
+
+Edit `lib/products.ts`. Each product just needs a name, image path, and price — prompts are generated automatically:
+
+```typescript
+{
+ name: "Striped Polo",
+ image: "/products/striped-polo.jpg",
+ price: 45,
+}
+```
+
+Place the garment image in `public/products/`.
+
+---
+
+## Environment variables
+
+| Variable | Required | Purpose |
+|----------|----------|---------|
+| `DECART_API_KEY` | Yes | Creates client tokens for realtime WebRTC connections |
+| `OPENAI_API_KEY` | Yes | Powers `/api/enhance-prompt` for auto-generating prompts. Can be swapped for any vision-capable LLM. |
diff --git a/examples/full-featured/app/api/enhance-prompt/route.ts b/examples/full-featured/app/api/enhance-prompt/route.ts
new file mode 100644
index 0000000..e5b1f87
--- /dev/null
+++ b/examples/full-featured/app/api/enhance-prompt/route.ts
@@ -0,0 +1,118 @@
+import { NextRequest, NextResponse } from "next/server";
+
+const SYSTEM_PROMPT = `You write prompts for a virtual try-on model. You receive a reference clothing image, and optionally a camera frame showing the person.
+
+Follow these steps:
+
+Step 1 - Examine the person's camera frame (if provided):
+Identify what the person is currently wearing on their upper body, lower body, head, etc. Note the specific garment (e.g. "a plain white t-shirt", "dark blue jeans", "a grey hoodie").
+
+Step 2 - Examine the reference clothing image:
+Describe it with material, texture, pattern, fit, and colors. Be specific (e.g. "a red plaid flannel shirt with a relaxed fit" not just "a shirt").
+
+Step 3 - Choose the action:
+- If the person is ALREADY WEARING something in the same slot as the reference item (e.g. they wear a t-shirt and the reference is a blouse), use SUBSTITUTE:
+ "Substitute the [description of current clothing] with [description of reference clothing]"
+ Example: "Substitute the plain white t-shirt with a red plaid flannel shirt with a relaxed fit and chest pockets"
+
+- If the person is NOT wearing anything in that slot (e.g. no hat, no jacket over their shirt), use ADD:
+ "Add [description of reference clothing] to the person's [body part]"
+ Example: "Add a wide-brimmed natural straw hat with a chin strap to the person's head"
+
+Fallback: If no person frame is provided or the relevant body part is not visible, use "the current top" for upper-body items or "the current bottoms" for lower-body items.
+Example: "Substitute the current top with a navy cable-knit sweater with a crew neck"
+
+Keep the total prompt between 20-30 words. Include colors, textures, and patterns. Return only the final prompt, nothing else.`;
+
+interface ChatMessage {
+ type: string;
+ text?: string;
+ image_url?: { url: string; detail: string };
+}
+
+export async function POST(req: NextRequest) {
+ const apiKey = process.env.OPENAI_API_KEY;
+ if (!apiKey) {
+ return NextResponse.json(
+ { error: "OPENAI_API_KEY not set" },
+ { status: 500 }
+ );
+ }
+
+ try {
+ const formData = await req.formData();
+ const file = formData.get("image") as File | null;
+ if (!file) {
+ return NextResponse.json(
+ { error: "No image provided" },
+ { status: 400 }
+ );
+ }
+
+ const buffer = await file.arrayBuffer();
+ const base64 = Buffer.from(buffer).toString("base64");
+ const mimeType = file.type || "image/png";
+ const clothingDataUri = `data:${mimeType};base64,${base64}`;
+
+ const userContent: ChatMessage[] = [
+ {
+ type: "text",
+ text: "Generate a try-on prompt for this clothing item:",
+ },
+ {
+ type: "image_url",
+ image_url: { url: clothingDataUri, detail: "low" },
+ },
+ ];
+
+ const personFrame = formData.get("personFrame") as File | null;
+ if (personFrame) {
+ const personBuffer = await personFrame.arrayBuffer();
+ const personBase64 = Buffer.from(personBuffer).toString("base64");
+ const personMime = personFrame.type || "image/jpeg";
+ const personDataUri = `data:${personMime};base64,${personBase64}`;
+ userContent.push(
+ { type: "text", text: "Here is the person from the camera:" },
+ {
+ type: "image_url",
+ image_url: { url: personDataUri, detail: "low" },
+ }
+ );
+ }
+
+ const res = await fetch("https://api.openai.com/v1/chat/completions", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${apiKey}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ model: "gpt-4o-mini",
+ max_tokens: 200,
+ messages: [
+ { role: "system", content: SYSTEM_PROMPT },
+ { role: "user", content: userContent },
+ ],
+ }),
+ });
+
+ if (!res.ok) {
+ const err = await res.text();
+ console.error("OpenAI API error:", err);
+ return NextResponse.json(
+ { error: "Failed to generate prompt" },
+ { status: 500 }
+ );
+ }
+
+ const data = await res.json();
+ const prompt = data.choices[0]?.message?.content?.trim() || "";
+ return NextResponse.json({ prompt });
+ } catch (error) {
+ console.error("Prompt generation failed:", error);
+ return NextResponse.json(
+ { error: "Prompt generation failed" },
+ { status: 500 }
+ );
+ }
+}
diff --git a/examples/full-featured/app/api/tokens/route.ts b/examples/full-featured/app/api/tokens/route.ts
new file mode 100644
index 0000000..b433c45
--- /dev/null
+++ b/examples/full-featured/app/api/tokens/route.ts
@@ -0,0 +1,24 @@
+import { createDecartClient } from "@decartai/sdk";
+import { NextResponse } from "next/server";
+
+export async function POST() {
+ const apiKey = process.env.DECART_API_KEY;
+ if (!apiKey) {
+ return NextResponse.json(
+ { error: "DECART_API_KEY is not set" },
+ { status: 500 }
+ );
+ }
+
+ try {
+ const client = createDecartClient({ apiKey });
+ const token = await client.tokens.create();
+ return NextResponse.json(token);
+ } catch (error) {
+ console.error("Failed to create token:", error);
+ return NextResponse.json(
+ { error: "Failed to create token" },
+ { status: 500 }
+ );
+ }
+}
diff --git a/examples/full-featured/app/globals.css b/examples/full-featured/app/globals.css
new file mode 100644
index 0000000..d2f6bef
--- /dev/null
+++ b/examples/full-featured/app/globals.css
@@ -0,0 +1,9 @@
+@import "tailwindcss";
+
+@theme inline {
+ --font-sans: system-ui, -apple-system, sans-serif;
+}
+
+button {
+ cursor: pointer;
+}
diff --git a/examples/full-featured/app/layout.tsx b/examples/full-featured/app/layout.tsx
new file mode 100644
index 0000000..270e074
--- /dev/null
+++ b/examples/full-featured/app/layout.tsx
@@ -0,0 +1,20 @@
+import type { Metadata } from "next";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "Virtual Try-On - Full Featured Example",
+ description:
+ "Combines person detection, AI-generated prompts, and file upload",
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/examples/full-featured/app/page.tsx b/examples/full-featured/app/page.tsx
new file mode 100644
index 0000000..9b18b61
--- /dev/null
+++ b/examples/full-featured/app/page.tsx
@@ -0,0 +1,225 @@
+"use client";
+
+import { useState, useEffect, useCallback, useRef } from "react";
+import { Product } from "@/lib/products";
+import { useCamera } from "@/hooks/useCamera";
+import { useDecartRealtime } from "@/hooks/useDecartRealtime";
+import { usePersonDetection } from "@/hooks/usePersonDetection";
+import { urlToImageBlob, resizeImageBlob } from "@/lib/image-utils";
+import { enhancePrompt } from "@/lib/enhance-prompt";
+import { ProductSidebar } from "@/components/ProductSidebar";
+import { CombinedView } from "@/components/CombinedView";
+
+export default function FullFeaturedPage() {
+ const [activeProduct, setActiveProduct] = useState(null);
+ const [prompt, setPrompt] = useState("");
+ const [processingStatus, setProcessingStatus] = useState(null);
+ const [uploadedImage, setUploadedImage] = useState(null);
+ const [isUploadActive, setIsUploadActive] = useState(false);
+
+ const { stream, startCamera, stopCamera } = useCamera();
+ const { status, error, connect, disconnect, clientRef } =
+ useDecartRealtime();
+
+ const remoteVideoRef = useRef>(null);
+ const [localVideoElement, setLocalVideoElement] =
+ useState(null);
+ const localVideoRef = useRef(null);
+ const garmentBlobRef = useRef(null);
+ const lastPromptRef = useRef("");
+ const connectingRef = useRef(false);
+ const uploadedImageRef = useRef(null);
+
+ const { personPresent, isReady: detectionReady } =
+ usePersonDetection(localVideoElement);
+
+ const handleRemoteStreamRef = useCallback(
+ (ref: React.RefObject) => {
+ remoteVideoRef.current = ref;
+ },
+ []
+ );
+
+ const handleLocalVideoRef = useCallback(
+ (ref: React.RefObject) => {
+ setLocalVideoElement(ref.current);
+ localVideoRef.current = ref.current;
+ },
+ []
+ );
+
+ // Start camera on mount
+ useEffect(() => {
+ startCamera();
+ return () => {
+ stopCamera();
+ };
+ }, [startCamera, stopCamera]);
+
+ // Connect/disconnect based on person detection
+ useEffect(() => {
+ if (!detectionReady || !stream) return;
+
+ if (personPresent && !clientRef.current && !connectingRef.current) {
+ connectingRef.current = true;
+
+ (async () => {
+ try {
+ const res = await fetch("/api/tokens", { method: "POST" });
+ if (!res.ok) throw new Error(`Token fetch failed: ${res.status}`);
+ const { apiKey } = await res.json();
+
+ const rtClient = await connect({
+ apiKey,
+ stream,
+ onRemoteStream: (remoteStream) => {
+ if (remoteVideoRef.current?.current) {
+ remoteVideoRef.current.current.srcObject = remoteStream;
+ }
+ },
+ });
+
+ // Re-apply last garment if reconnecting
+ if (rtClient && garmentBlobRef.current && lastPromptRef.current) {
+ rtClient.setImage(garmentBlobRef.current, {
+ prompt: lastPromptRef.current,
+ enhance: false,
+ });
+ }
+ } finally {
+ connectingRef.current = false;
+ }
+ })();
+ } else if (!personPresent && clientRef.current && !connectingRef.current) {
+ disconnect();
+ }
+ }, [personPresent, detectionReady, stream, connect, disconnect, clientRef]);
+
+ // Cleanup on unmount
+ useEffect(() => {
+ return () => {
+ disconnect();
+ if (uploadedImageRef.current) {
+ URL.revokeObjectURL(uploadedImageRef.current);
+ }
+ };
+ }, [disconnect]);
+
+ // Apply garment blob + generate prompt (shared by product select and upload)
+ const applyGarment = useCallback(
+ async (blob: Blob, label: string) => {
+ const resized = await resizeImageBlob(blob);
+ garmentBlobRef.current = resized;
+
+ if (!clientRef.current) return;
+
+ setProcessingStatus(`Generating try-on prompt for ${label}...`);
+ let generatedPrompt: string | null = null;
+ try {
+ generatedPrompt = await enhancePrompt(resized, localVideoRef.current);
+ } catch (err) {
+ console.error("Prompt generation failed:", err);
+ } finally {
+ setProcessingStatus(null);
+ }
+
+ const finalPrompt = generatedPrompt || "Try on this garment";
+ setPrompt(finalPrompt);
+ lastPromptRef.current = finalPrompt;
+
+ if (!clientRef.current) return;
+ clientRef.current.setImage(resized, {
+ prompt: finalPrompt,
+ enhance: false,
+ });
+ },
+ [clientRef]
+ );
+
+ const handleSelectProduct = useCallback(
+ async (product: Product) => {
+ setActiveProduct(product);
+ setIsUploadActive(false);
+
+ const blob = await urlToImageBlob(product.image);
+ await applyGarment(blob, product.name);
+ },
+ [applyGarment]
+ );
+
+ const handleUploadGarment = useCallback(
+ async (file: File) => {
+ if (file.size === 0) return;
+
+ if (uploadedImageRef.current) {
+ URL.revokeObjectURL(uploadedImageRef.current);
+ }
+
+ const previewUrl = URL.createObjectURL(file);
+ uploadedImageRef.current = previewUrl;
+ setUploadedImage(previewUrl);
+ setIsUploadActive(true);
+ setActiveProduct(null);
+
+ await applyGarment(file, "your upload");
+ },
+ [applyGarment]
+ );
+
+ const handleReactivateUpload = useCallback(() => {
+ if (!garmentBlobRef.current || !lastPromptRef.current) return;
+ setIsUploadActive(true);
+ setActiveProduct(null);
+
+ if (!clientRef.current) return;
+ clientRef.current.setImage(garmentBlobRef.current, {
+ prompt: lastPromptRef.current,
+ enhance: false,
+ });
+ }, [clientRef]);
+
+ const handleClearUpload = useCallback(() => {
+ if (uploadedImageRef.current) {
+ URL.revokeObjectURL(uploadedImageRef.current);
+ uploadedImageRef.current = null;
+ }
+ setUploadedImage(null);
+ setIsUploadActive(false);
+ }, []);
+
+ const handlePromptSubmit = useCallback(() => {
+ if (!clientRef.current || !garmentBlobRef.current) return;
+ lastPromptRef.current = prompt;
+ clientRef.current.setImage(garmentBlobRef.current, {
+ prompt,
+ enhance: false,
+ });
+ }, [clientRef, prompt]);
+
+ return (
+
+ );
+}
diff --git a/examples/full-featured/components/CombinedView.tsx b/examples/full-featured/components/CombinedView.tsx
new file mode 100644
index 0000000..7523463
--- /dev/null
+++ b/examples/full-featured/components/CombinedView.tsx
@@ -0,0 +1,200 @@
+"use client";
+
+import { useRef, useEffect } from "react";
+import { ConnectionStatus } from "@/hooks/useDecartRealtime";
+
+interface CombinedViewProps {
+ localStream: MediaStream | null;
+ status: ConnectionStatus;
+ error: string | null;
+ personPresent: boolean;
+ detectionReady: boolean;
+ prompt: string;
+ processingStatus: string | null;
+ onPromptChange: (prompt: string) => void;
+ onPromptSubmit: () => void;
+ onRemoteStream: (ref: React.RefObject) => void;
+ onLocalVideo?: (ref: React.RefObject) => void;
+}
+
+function getStatusLabel(
+ detectionReady: boolean,
+ personPresent: boolean,
+ status: ConnectionStatus,
+ processingStatus: string | null
+): { text: string; className: string; pulse: boolean } {
+ if (!detectionReady) {
+ return {
+ text: "Loading detection...",
+ className: "bg-black/60 text-white",
+ pulse: false,
+ };
+ }
+
+ if (processingStatus) {
+ return {
+ text: processingStatus,
+ className: "bg-blue-500/90 text-white",
+ pulse: true,
+ };
+ }
+
+ if (status === "generating") {
+ return {
+ text: "Live",
+ className: "bg-green-500/90 text-white",
+ pulse: true,
+ };
+ }
+
+ if (status === "connecting") {
+ return {
+ text: "Person detected - connecting...",
+ className: "bg-blue-500/90 text-white",
+ pulse: true,
+ };
+ }
+
+ if (status === "connected") {
+ return {
+ text: "Connected - click a product to try on",
+ className: "bg-blue-500/90 text-white",
+ pulse: false,
+ };
+ }
+
+ if (personPresent) {
+ return {
+ text: "Person detected",
+ className: "bg-blue-500/90 text-white",
+ pulse: true,
+ };
+ }
+
+ return {
+ text: "Scanning...",
+ className: "bg-black/60 text-white",
+ pulse: false,
+ };
+}
+
+export function CombinedView({
+ localStream,
+ status,
+ error,
+ personPresent,
+ detectionReady,
+ prompt,
+ processingStatus,
+ onPromptChange,
+ onPromptSubmit,
+ onRemoteStream,
+ onLocalVideo,
+}: CombinedViewProps) {
+ const localVideoRef = useRef(null);
+ const remoteVideoRef = useRef(null);
+
+ useEffect(() => {
+ if (localVideoRef.current && localStream) {
+ localVideoRef.current.srcObject = localStream;
+ }
+ }, [localStream]);
+
+ useEffect(() => {
+ onRemoteStream(remoteVideoRef);
+ }, [onRemoteStream]);
+
+ useEffect(() => {
+ onLocalVideo?.(localVideoRef);
+ }, [onLocalVideo]);
+
+ const isGenerating = status === "generating";
+ const label = getStatusLabel(detectionReady, personPresent, status, processingStatus);
+
+ return (
+
+
+ {/* Local camera feed */}
+
+
+ {/* Remote AI stream */}
+
+
+ {/* Status badge */}
+
+
+ {label.pulse && (
+
+ )}
+ {label.text}
+
+
+
+ {/* Processing overlay */}
+ {processingStatus && (
+
+ )}
+
+ {/* Hint text when scanning */}
+ {detectionReady && !personPresent && status !== "generating" && (
+
+
+ Step in front of the camera to start
+
+
+ )}
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+ {/* Prompt editor */}
+ {prompt && (
+
+
+ onPromptChange(e.target.value)}
+ onKeyDown={(e) => e.key === "Enter" && onPromptSubmit()}
+ className="flex-1 text-sm bg-gray-800 border border-gray-700 text-white rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500/50"
+ placeholder="Edit prompt..."
+ />
+
+ Apply
+
+
+
+ )}
+
+ );
+}
diff --git a/examples/full-featured/components/ProductSidebar.tsx b/examples/full-featured/components/ProductSidebar.tsx
new file mode 100644
index 0000000..b4ad147
--- /dev/null
+++ b/examples/full-featured/components/ProductSidebar.tsx
@@ -0,0 +1,140 @@
+"use client";
+
+import { useRef } from "react";
+import { PRODUCTS, Product } from "@/lib/products";
+import Image from "next/image";
+
+interface ProductSidebarProps {
+ activeProduct: Product | null;
+ uploadedImage: string | null;
+ isUploadActive: boolean;
+ onSelectProduct: (product: Product) => void;
+ onUploadGarment: (file: File) => void;
+ onReactivateUpload: () => void;
+ onClearUpload: () => void;
+}
+
+export function ProductSidebar({
+ activeProduct,
+ uploadedImage,
+ isUploadActive,
+ onSelectProduct,
+ onUploadGarment,
+ onReactivateUpload,
+ onClearUpload,
+}: ProductSidebarProps) {
+ const fileInputRef = useRef(null);
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (file) {
+ onUploadGarment(file);
+ e.target.value = "";
+ }
+ };
+
+ return (
+
+ );
+}
diff --git a/examples/full-featured/hooks/useCamera.ts b/examples/full-featured/hooks/useCamera.ts
new file mode 100644
index 0000000..e561e07
--- /dev/null
+++ b/examples/full-featured/hooks/useCamera.ts
@@ -0,0 +1,36 @@
+"use client";
+
+import { useState, useCallback, useRef } from "react";
+
+export function useCamera() {
+ const [stream, setStream] = useState(null);
+ const [error, setError] = useState(null);
+ const streamRef = useRef(null);
+
+ const startCamera = useCallback(async () => {
+ try {
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
+ audio: false,
+ video: { facingMode: "user" },
+ });
+ streamRef.current = mediaStream;
+ setStream(mediaStream);
+ setError(null);
+ return mediaStream;
+ } catch (err) {
+ const msg = err instanceof Error ? err.message : "Camera access denied";
+ setError(msg);
+ return null;
+ }
+ }, []);
+
+ const stopCamera = useCallback(() => {
+ if (streamRef.current) {
+ streamRef.current.getTracks().forEach((t) => t.stop());
+ streamRef.current = null;
+ setStream(null);
+ }
+ }, []);
+
+ return { stream, error, startCamera, stopCamera };
+}
diff --git a/examples/full-featured/hooks/useDecartRealtime.ts b/examples/full-featured/hooks/useDecartRealtime.ts
new file mode 100644
index 0000000..828b8de
--- /dev/null
+++ b/examples/full-featured/hooks/useDecartRealtime.ts
@@ -0,0 +1,76 @@
+"use client";
+
+import { useState, useCallback, useRef } from "react";
+import { createDecartClient, models } from "@decartai/sdk";
+
+type RealtimeClient = Awaited<
+ ReturnType["realtime"]["connect"]>
+>;
+
+export type ConnectionStatus =
+ | "idle"
+ | "connecting"
+ | "connected"
+ | "generating"
+ | "reconnecting"
+ | "disconnected"
+ | "error";
+
+interface ConnectOptions {
+ apiKey: string;
+ stream: MediaStream;
+ prompt?: string;
+ onRemoteStream: (stream: MediaStream) => void;
+}
+
+export function useDecartRealtime() {
+ const [status, setStatus] = useState("idle");
+ const [error, setError] = useState(null);
+ const clientRef = useRef(null);
+
+ const connect = useCallback(async (options: ConnectOptions) => {
+ const { apiKey, stream, prompt, onRemoteStream } = options;
+ setStatus("connecting");
+ setError(null);
+
+ try {
+ const client = createDecartClient({ apiKey });
+ const model = models.realtime("lucy_2_rt");
+
+ const rtClient = await client.realtime.connect(stream, {
+ model,
+ onRemoteStream,
+ ...(prompt && {
+ initialState: { prompt: { text: prompt, enhance: false } },
+ }),
+ });
+
+ rtClient.on("connectionChange", (state) => {
+ setStatus(state);
+ });
+
+ rtClient.on("error", (err) => {
+ setError(err.message);
+ setStatus("error");
+ });
+
+ clientRef.current = rtClient;
+ return rtClient;
+ } catch (err) {
+ const msg = err instanceof Error ? err.message : "Connection failed";
+ setError(msg);
+ setStatus("error");
+ return null;
+ }
+ }, []);
+
+ const disconnect = useCallback(() => {
+ if (clientRef.current) {
+ clientRef.current.disconnect();
+ clientRef.current = null;
+ }
+ setStatus("disconnected");
+ }, []);
+
+ return { status, error, connect, disconnect, clientRef };
+}
diff --git a/examples/full-featured/hooks/usePersonDetection.ts b/examples/full-featured/hooks/usePersonDetection.ts
new file mode 100644
index 0000000..6edf7af
--- /dev/null
+++ b/examples/full-featured/hooks/usePersonDetection.ts
@@ -0,0 +1,91 @@
+"use client";
+
+import { useState, useEffect, useRef } from "react";
+import { PoseLandmarker, FilesetResolver } from "@mediapipe/tasks-vision";
+
+const DETECTION_INTERVAL_MS = 1000;
+const MISS_THRESHOLD = 3;
+
+export function usePersonDetection(
+ videoElement: HTMLVideoElement | null
+) {
+ const [personPresent, setPersonPresent] = useState(false);
+ const [isReady, setIsReady] = useState(false);
+ const landmarkerRef = useRef(null);
+ const missCountRef = useRef(0);
+ const intervalRef = useRef | null>(null);
+
+ // Initialize PoseLandmarker
+ useEffect(() => {
+ let cancelled = false;
+
+ async function init() {
+ const vision = await FilesetResolver.forVisionTasks(
+ "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
+ );
+ if (cancelled) return;
+
+ const landmarker = await PoseLandmarker.createFromOptions(vision, {
+ baseOptions: {
+ modelAssetPath:
+ "https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",
+ delegate: "GPU",
+ },
+ runningMode: "VIDEO",
+ numPoses: 1,
+ });
+ if (cancelled) return;
+
+ landmarkerRef.current = landmarker;
+ setIsReady(true);
+ }
+
+ init();
+
+ return () => {
+ cancelled = true;
+ landmarkerRef.current?.close();
+ landmarkerRef.current = null;
+ };
+ }, []);
+
+ // Poll video for person detection
+ useEffect(() => {
+ if (!isReady || !videoElement) return;
+
+ function detect() {
+ if (
+ !landmarkerRef.current ||
+ !videoElement ||
+ videoElement.readyState < 2
+ )
+ return;
+
+ const result = landmarkerRef.current.detectForVideo(
+ videoElement,
+ performance.now()
+ );
+
+ if (result.landmarks.length > 0) {
+ missCountRef.current = 0;
+ setPersonPresent(true);
+ } else {
+ missCountRef.current++;
+ if (missCountRef.current >= MISS_THRESHOLD) {
+ setPersonPresent(false);
+ }
+ }
+ }
+
+ intervalRef.current = setInterval(detect, DETECTION_INTERVAL_MS);
+
+ return () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = null;
+ }
+ };
+ }, [isReady, videoElement]);
+
+ return { personPresent, isReady };
+}
diff --git a/examples/full-featured/lib/enhance-prompt.ts b/examples/full-featured/lib/enhance-prompt.ts
new file mode 100644
index 0000000..002f964
--- /dev/null
+++ b/examples/full-featured/lib/enhance-prompt.ts
@@ -0,0 +1,26 @@
+import { captureVideoFrame } from "./image-utils";
+
+/**
+ * Generates a try-on prompt from a garment image using GPT-4o-mini.
+ * Optionally captures a frame from the camera to provide context about
+ * what the person is currently wearing.
+ */
+export async function enhancePrompt(
+ garmentBlob: Blob,
+ localVideo?: HTMLVideoElement | null
+): Promise {
+ const formData = new FormData();
+ formData.append("image", garmentBlob);
+
+ if (localVideo && localVideo.videoWidth > 0) {
+ const personFrame = await captureVideoFrame(localVideo);
+ formData.append("personFrame", personFrame);
+ }
+
+ const res = await fetch("/api/enhance-prompt", {
+ method: "POST",
+ body: formData,
+ });
+ const data = await res.json();
+ return data.prompt || null;
+}
diff --git a/examples/full-featured/lib/image-utils.ts b/examples/full-featured/lib/image-utils.ts
new file mode 100644
index 0000000..e694410
--- /dev/null
+++ b/examples/full-featured/lib/image-utils.ts
@@ -0,0 +1,87 @@
+export async function urlToImageBlob(url: string): Promise {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.crossOrigin = "anonymous";
+ img.onload = () => {
+ const canvas = document.createElement("canvas");
+ canvas.width = img.naturalWidth || 512;
+ canvas.height = img.naturalHeight || 512;
+ const ctx = canvas.getContext("2d")!;
+
+ ctx.fillStyle = "#FFFFFF";
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
+
+ canvas.toBlob(
+ (blob) => {
+ if (blob) resolve(blob);
+ else reject(new Error("Failed to convert image"));
+ },
+ "image/jpeg",
+ 0.9
+ );
+ };
+ img.onerror = () => reject(new Error("Failed to load image"));
+ img.src = url;
+ });
+}
+
+export async function resizeImageBlob(
+ blob: Blob,
+ maxSize = 1024
+): Promise {
+ const img = await loadImage(blob);
+ const { naturalWidth: w, naturalHeight: h } = img;
+ if (w <= maxSize && h <= maxSize) return blob;
+
+ const scale = maxSize / Math.max(w, h);
+ const canvas = document.createElement("canvas");
+ canvas.width = Math.round(w * scale);
+ canvas.height = Math.round(h * scale);
+ const ctx = canvas.getContext("2d")!;
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
+
+ return new Promise((resolve, reject) => {
+ canvas.toBlob(
+ (b) => (b ? resolve(b) : reject(new Error("Failed to resize image"))),
+ "image/jpeg",
+ 0.8
+ );
+ });
+}
+
+export function captureVideoFrame(
+ video: HTMLVideoElement,
+ maxSize = 320
+): Promise {
+ const { videoWidth: w, videoHeight: h } = video;
+ const scale = maxSize / Math.max(w, h);
+ const canvas = document.createElement("canvas");
+ canvas.width = Math.round(w * scale);
+ canvas.height = Math.round(h * scale);
+ const ctx = canvas.getContext("2d")!;
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+
+ return new Promise((resolve, reject) => {
+ canvas.toBlob(
+ (b) => (b ? resolve(b) : reject(new Error("Failed to capture frame"))),
+ "image/jpeg",
+ 0.7
+ );
+ });
+}
+
+export function loadImage(blob: Blob): Promise {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ img.onload = () => {
+ URL.revokeObjectURL(img.src);
+ resolve(img);
+ };
+ img.onerror = () => {
+ URL.revokeObjectURL(img.src);
+ reject(new Error("Failed to load image"));
+ };
+ img.src = URL.createObjectURL(blob);
+ });
+}
diff --git a/examples/full-featured/lib/products.ts b/examples/full-featured/lib/products.ts
new file mode 100644
index 0000000..e8a4d64
--- /dev/null
+++ b/examples/full-featured/lib/products.ts
@@ -0,0 +1,58 @@
+export interface Product {
+ name: string;
+ image: string;
+ price: number;
+}
+
+export const PRODUCTS: Product[] = [
+ {
+ name: "Decart Beanie",
+ image: "/products/decart-beanie.png",
+ price: 35,
+ },
+ {
+ name: "Decart Bomber Jacket",
+ image: "/products/decart-bomber.png",
+ price: 149,
+ },
+ {
+ name: "Decart Cap",
+ image: "/products/decart-cap.png",
+ price: 29,
+ },
+ {
+ name: "Decart Crewneck",
+ image: "/products/decart-crewneck.png",
+ price: 89,
+ },
+ {
+ name: "Decart Hoodie",
+ image: "/products/decart-hoodie.png",
+ price: 99,
+ },
+ {
+ name: "Decart Polo",
+ image: "/products/decart-polo.png",
+ price: 69,
+ },
+ {
+ name: "Decart T-Shirt",
+ image: "/products/decart-tshirt.png",
+ price: 45,
+ },
+ {
+ name: "Decart Zip-Up Hoodie",
+ image: "/products/decart-zipup.png",
+ price: 109,
+ },
+ {
+ name: "Decart Rain Jacket",
+ image: "/products/decart-rainjacket.png",
+ price: 129,
+ },
+ {
+ name: "Decart Blazer",
+ image: "/products/decart-blazer.png",
+ price: 199,
+ },
+];
diff --git a/examples/full-featured/next.config.ts b/examples/full-featured/next.config.ts
new file mode 100644
index 0000000..cb651cd
--- /dev/null
+++ b/examples/full-featured/next.config.ts
@@ -0,0 +1,5 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {};
+
+export default nextConfig;
diff --git a/examples/full-featured/package-lock.json b/examples/full-featured/package-lock.json
new file mode 100644
index 0000000..7ad669f
--- /dev/null
+++ b/examples/full-featured/package-lock.json
@@ -0,0 +1,1709 @@
+{
+ "name": "decart-tryon-full-featured",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "decart-tryon-full-featured",
+ "version": "1.0.0",
+ "dependencies": {
+ "@decartai/sdk": "^0.0.52",
+ "@mediapipe/tasks-vision": "^0.10.18",
+ "next": "15.1.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@decartai/sdk": {
+ "version": "0.0.52",
+ "resolved": "https://registry.npmjs.org/@decartai/sdk/-/sdk-0.0.52.tgz",
+ "integrity": "sha512-fsNE5/n37H2NFPTDUSoLXSHmWwrela1YD+Z6uSDYNggtjGvUOb5TbMoEtM2mWys3c85cGaBA2ZpXoJIR1vTGgw==",
+ "license": "MIT",
+ "dependencies": {
+ "mitt": "^3.0.1",
+ "p-retry": "^6.2.1",
+ "zod": "^4.0.17"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+ "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+ "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+ "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+ "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+ "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+ "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+ "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+ "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+ "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+ "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+ "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.0.5"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+ "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+ "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+ "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+ "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+ "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+ "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.2.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+ "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+ "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mediapipe/tasks-vision": {
+ "version": "0.10.32",
+ "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.32.tgz",
+ "integrity": "sha512-3tiAZnmKloYnRXYoO3dKltTUGnqeCwzC4lV03uY0vCsE+aveJTyEVQyZHOlQGQNsjK+gRHzkf9q08C99Qm2K0Q==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@next/env": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.0.tgz",
+ "integrity": "sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.0.tgz",
+ "integrity": "sha512-ZU8d7xxpX14uIaFC3nsr4L++5ZS/AkWDm1PzPO6gD9xWhFkOj2hzSbSIxoncsnlJXB1CbLOfGVN4Zk9tg83PUw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.0.tgz",
+ "integrity": "sha512-DQ3RiUoW2XC9FcSM4ffpfndq1EsLV0fj0/UY33i7eklW5akPUCo6OX2qkcLXZ3jyPdo4sf2flwAED3AAq3Om2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.0.tgz",
+ "integrity": "sha512-M+vhTovRS2F//LMx9KtxbkWk627l5Q7AqXWWWrfIzNIaUFiz2/NkOFkxCFyNyGACi5YbA8aekzCLtbDyfF/v5Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.0.tgz",
+ "integrity": "sha512-Qn6vOuwaTCx3pNwygpSGtdIu0TfS1KiaYLYXLH5zq1scoTXdwYfdZtwvJTpB1WrLgiQE2Ne2kt8MZok3HlFqmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.0.tgz",
+ "integrity": "sha512-yeNh9ofMqzOZ5yTOk+2rwncBzucc6a1lyqtg8xZv0rH5znyjxHOWsoUtSq4cUTeeBIiXXX51QOOe+VoCjdXJRw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.0.tgz",
+ "integrity": "sha512-t9IfNkHQs/uKgPoyEtU912MG6a1j7Had37cSUyLTKx9MnUpjj+ZDKw9OyqTI9OwIIv0wmkr1pkZy+3T5pxhJPg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.0.tgz",
+ "integrity": "sha512-WEAoHyG14t5sTavZa1c6BnOIEukll9iqFRTavqRVPfYmfegOAd5MaZfXgOGG6kGo1RduyGdTHD4+YZQSdsNZXg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.0.tgz",
+ "integrity": "sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz",
+ "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.19.0",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.31.1",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.2.1"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz",
+ "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.2.1",
+ "@tailwindcss/oxide-darwin-arm64": "4.2.1",
+ "@tailwindcss/oxide-darwin-x64": "4.2.1",
+ "@tailwindcss/oxide-freebsd-x64": "4.2.1",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.2.1",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.2.1",
+ "@tailwindcss/oxide-linux-x64-musl": "4.2.1",
+ "@tailwindcss/oxide-wasm32-wasi": "4.2.1",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.2.1"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz",
+ "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz",
+ "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz",
+ "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz",
+ "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz",
+ "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz",
+ "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz",
+ "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz",
+ "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz",
+ "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz",
+ "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.8.1",
+ "@emnapi/runtime": "^1.8.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.1",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz",
+ "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz",
+ "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.1.tgz",
+ "integrity": "sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.2.1",
+ "@tailwindcss/oxide": "4.2.1",
+ "postcss": "^8.5.6",
+ "tailwindcss": "4.2.1"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
+ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
+ "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
+ "license": "MIT"
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001774",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
+ "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.19.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
+ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/is-network-error": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz",
+ "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
+ "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.31.1",
+ "lightningcss-darwin-arm64": "1.31.1",
+ "lightningcss-darwin-x64": "1.31.1",
+ "lightningcss-freebsd-x64": "1.31.1",
+ "lightningcss-linux-arm-gnueabihf": "1.31.1",
+ "lightningcss-linux-arm64-gnu": "1.31.1",
+ "lightningcss-linux-arm64-musl": "1.31.1",
+ "lightningcss-linux-x64-gnu": "1.31.1",
+ "lightningcss-linux-x64-musl": "1.31.1",
+ "lightningcss-win32-arm64-msvc": "1.31.1",
+ "lightningcss-win32-x64-msvc": "1.31.1"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz",
+ "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz",
+ "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz",
+ "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz",
+ "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz",
+ "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz",
+ "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz",
+ "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz",
+ "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz",
+ "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz",
+ "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.31.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz",
+ "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.1.0.tgz",
+ "integrity": "sha512-QKhzt6Y8rgLNlj30izdMbxAwjHMFANnLwDwZ+WQh5sMhyt4lEBqDK9QpvWHtIM4rINKPoJ8aiRZKg5ULSybVHw==",
+ "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/CVE-2025-66478 for more details.",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.1.0",
+ "@swc/counter": "0.1.3",
+ "@swc/helpers": "0.5.15",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.1.0",
+ "@next/swc-darwin-x64": "15.1.0",
+ "@next/swc-linux-arm64-gnu": "15.1.0",
+ "@next/swc-linux-arm64-musl": "15.1.0",
+ "@next/swc-linux-x64-gnu": "15.1.0",
+ "@next/swc-linux-x64-musl": "15.1.0",
+ "@next/swc-win32-arm64-msvc": "15.1.0",
+ "@next/swc-win32-x64-msvc": "15.1.0",
+ "sharp": "^0.33.5"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/p-retry": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
+ "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.2",
+ "is-network-error": "^1.0.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.4",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
+ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.4",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
+ "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.4"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.6.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.33.5",
+ "@img/sharp-darwin-x64": "0.33.5",
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
+ "@img/sharp-libvips-linux-arm": "1.0.5",
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
+ "@img/sharp-libvips-linux-x64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+ "@img/sharp-linux-arm": "0.33.5",
+ "@img/sharp-linux-arm64": "0.33.5",
+ "@img/sharp-linux-s390x": "0.33.5",
+ "@img/sharp-linux-x64": "0.33.5",
+ "@img/sharp-linuxmusl-arm64": "0.33.5",
+ "@img/sharp-linuxmusl-x64": "0.33.5",
+ "@img/sharp-wasm32": "0.33.5",
+ "@img/sharp-win32-ia32": "0.33.5",
+ "@img/sharp-win32-x64": "0.33.5"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
+ "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/zod": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/examples/full-featured/package.json b/examples/full-featured/package.json
new file mode 100644
index 0000000..d3b3c28
--- /dev/null
+++ b/examples/full-featured/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "decart-tryon-full-featured",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start"
+ },
+ "dependencies": {
+ "@decartai/sdk": "^0.0.52",
+ "@mediapipe/tasks-vision": "^0.10.18",
+ "next": "15.1.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/examples/full-featured/postcss.config.mjs b/examples/full-featured/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/examples/full-featured/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/examples/full-featured/public/products/decart-beanie.png b/examples/full-featured/public/products/decart-beanie.png
new file mode 100644
index 0000000..6ce1dd3
Binary files /dev/null and b/examples/full-featured/public/products/decart-beanie.png differ
diff --git a/examples/full-featured/public/products/decart-blazer.png b/examples/full-featured/public/products/decart-blazer.png
new file mode 100644
index 0000000..47203e8
Binary files /dev/null and b/examples/full-featured/public/products/decart-blazer.png differ
diff --git a/examples/full-featured/public/products/decart-bomber.png b/examples/full-featured/public/products/decart-bomber.png
new file mode 100644
index 0000000..993a569
Binary files /dev/null and b/examples/full-featured/public/products/decart-bomber.png differ
diff --git a/examples/full-featured/public/products/decart-cap.png b/examples/full-featured/public/products/decart-cap.png
new file mode 100644
index 0000000..3540bb6
Binary files /dev/null and b/examples/full-featured/public/products/decart-cap.png differ
diff --git a/examples/full-featured/public/products/decart-crewneck.png b/examples/full-featured/public/products/decart-crewneck.png
new file mode 100644
index 0000000..74a023d
Binary files /dev/null and b/examples/full-featured/public/products/decart-crewneck.png differ
diff --git a/examples/full-featured/public/products/decart-hoodie.png b/examples/full-featured/public/products/decart-hoodie.png
new file mode 100644
index 0000000..b5ba1f7
Binary files /dev/null and b/examples/full-featured/public/products/decart-hoodie.png differ
diff --git a/examples/full-featured/public/products/decart-polo.png b/examples/full-featured/public/products/decart-polo.png
new file mode 100644
index 0000000..d1537c8
Binary files /dev/null and b/examples/full-featured/public/products/decart-polo.png differ
diff --git a/examples/full-featured/public/products/decart-rainjacket.png b/examples/full-featured/public/products/decart-rainjacket.png
new file mode 100644
index 0000000..b2c801b
Binary files /dev/null and b/examples/full-featured/public/products/decart-rainjacket.png differ
diff --git a/examples/full-featured/public/products/decart-tshirt.png b/examples/full-featured/public/products/decart-tshirt.png
new file mode 100644
index 0000000..bf33120
Binary files /dev/null and b/examples/full-featured/public/products/decart-tshirt.png differ
diff --git a/examples/full-featured/public/products/decart-zipup.png b/examples/full-featured/public/products/decart-zipup.png
new file mode 100644
index 0000000..81fd1b9
Binary files /dev/null and b/examples/full-featured/public/products/decart-zipup.png differ
diff --git a/examples/full-featured/tsconfig.json b/examples/full-featured/tsconfig.json
new file mode 100644
index 0000000..01b4ddb
--- /dev/null
+++ b/examples/full-featured/tsconfig.json
@@ -0,0 +1,40 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/examples/standalone/README.md b/examples/standalone/README.md
index 1d48846..2b416ad 100644
--- a/examples/standalone/README.md
+++ b/examples/standalone/README.md
@@ -1,6 +1,6 @@
# Standalone Virtual Try-On (with AI-generated prompts)
-> A dedicated try-on experience with AI-generated prompts. Products on the left, live camera in the center. Click a product and GPT-4o-mini generates the try-on prompt automatically from the garment image and the person's camera frame.
+> A dedicated try-on experience with AI-generated prompts. Products on the left, live camera in the center. Click a product and a vision LLM generates the try-on prompt automatically from the garment image and the person's camera frame. This example uses GPT-4o-mini, but any vision-capable LLM works (Claude, Gemini, etc.). A built-in Decart solution is coming soon.
Unlike the [e-commerce example](../ecommerce/) which uses hardcoded prompts, this example shows how to use the `/api/enhance-prompt` endpoint to generate prompts dynamically - useful when you don't know what garments users will upload.
@@ -117,4 +117,4 @@ This example uses Next.js + Tailwind, but the core Decart integration works with
| Variable | Required | Purpose |
|----------|----------|---------|
| `DECART_API_KEY` | Yes | Creates client tokens for realtime connections |
-| `OPENAI_API_KEY` | Yes | Powers `/api/enhance-prompt` for auto-generating prompts |
+| `OPENAI_API_KEY` | Yes | Powers `/api/enhance-prompt` for auto-generating prompts. Can be swapped for any vision-capable LLM. |