# Project export: Voxal CAD

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: Model the world in 3D and edit anything you envision like "digital clay" – no technical or software skills required.
- Devpost: https://devpost.com/software/voxal-cad
- GitHub: https://github.com/ishakan/TreeHacks2026
- Video: https://www.youtube.com/embed/a7FHJIAtmA8?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — XFG16 (2 commits)

## Devpost submission (written by the team)

### Inspiration

Engineers routinely spend hours using CAD software to build similar components, or alternatively, having to scavenge for appropriate models online and make countless adjustments. It's time — not only for engineers, but everyone — to be able explore CAD modeling in an accessible manner. We've waited long enough — it's time to do what Cursor couldn't. It's time for engineers to explore their own version of "vibe-coding" through Voxal. What It Does Voxal accepts text descriptions or images and generates fully editable 3D models in .glb format. Users can then refine these models through natural language commands such as "make it taller," "add rounded edges," or "change the texture" and watch their modifications happen in real-time. Voxal also isolates all dimensional parameters for components, so that users can manually adjust the models with high precision. For greater accuracy, we provide a workflow that takes in multiple perspectives of the same image; for convenience of importing media, we provide a workflow that segments objects from a larger image, so that users can select which ones to model in 3D. How We Built It Pipeline 1: Image to CAD + Natural Language Editing of CAD Two-stage pipeline using spatial clustering and Claude Sonnet 4.5: Stage 1 — GLB Analysis: Input: one or multi-image input via TRELLIS → .glb file Processing: parse raw vertex positions from GLB binary, compute bounding box, slice mesh into 20 Y-axis layers, cluster vertices on XZ plane via greedy connected-components Output: structural column map (center, radius, height range, narrow/wide labels) + pre-computed vertex selection masks Stage 2 — LLM Edit: Input: spatial analysis + user's natural language edit instruction Processing: single Claude Sonnet 4.5 call maps instruction to structural columns, generates self-contained Python script using compound vertex masks (height range + XZ proximity) Output: modified .glb with only targeted vertices transformed The edited GLB is rendered via Three.js on our model viewer WebUI. Pipeline 2: Blueprint-to-OpenSCAD Agent Pipeline Two-layer agentic workflow using the Claude Agent SDK: Layer 1 — Blueprint Agent: Input: text description Output: blueprint.html (inline-SVG orthographic views, CSS custom-property dimensions), blueprint_dimensions.json Refinement: resumes the same SDK session with user feedback Layer 2 — Coding Agent: Input: confirmed blueprint files + openscad_docs.json Output: model.scad (parametric OpenSCAD), parameters.json Refinement: resumes the same SDK session Finally, the OpenSCAD is compiled and displayed via three.js on our CAD studio WebUI. Pipeline 0: Preprocessing — Image Segmentation (Modal) Remote inference runs on Modal (H100 GPU): GroundingDINO (SwinT) — open-vocab object detection. Produces bounding boxes for each class prompt. SAM (ViT-H) — segment-anything. Takes each bbox as a prompt, outputs per-object masks. NMS deduplicates overlapping detections. Inputs an image with background and many objects; returns pairs of each segment with its class label, and returns the result. Challenges We Ran Into Building out the logic for using text to edit .glb models was the most challenging part. The initial idea was to turn .glb into a more "programmable CAD language" like OpenSCAD. However, this turned out to be much more difficult than expected. We tried pipelines that turn .glb into OpenSCAD before using natural language to prompt edits of OpenSCAD. This did not perform as well as expected. The initial plan for the OpenSCAD coding agent involved using various CV algorithms to extract contours from a source image. We experimented with Canny edge detection, bilateral filtering, several blurring algorithms, perspective correction, and depth perception from multiple views of the same object. The time-consuming process of trial-and-error with algorithm parameters, unfortunately, did not help the coding agent get a much better understanding of the geometry of the requested objects. There was some friction in learning how to use Modal for inferences, but it was worth it considering the $250 worth of credits and a significant reduction in inference speed. Differing Python environments requiring different Python versions made infra very difficult while hosting multiple Python backends. Accomplishments That We're Proud Of First public software to make TRELLIS generation results editable, allowing users to edit on top of 3D models once already generated. We enable end-to-end CAD generation and modelling for users with no experience in CAD or design. Convert text input to HTML/JSON file into .glb 3D file (context: for parts that require extreme precision, i.e., 3D-printed parts). Allowing high-precision pieces to be designed for users with little to no experience in CAD or design. With our robust set of methods, Voxal can excel in various creative pipelines: interior designers can generate and iterate on furniture pieces instantly, e-commerce platforms can produce product variations on demand, engineers can design hardware components just by typing prompts on a phone, and game developers can rapidly prototype environmental objects. Voxal responds to conversational requests, making 3D modeling as intuitive as describing what you want to a colleague. On the research side, we'd like to highlight Voxal's potential in generating domain-randomized training assets. Voxal's ability to mass-generate and control objects' dimensional parameters could bulk up the volume of reinforcement learning training data through micro-adjustments in virtually simulated environments. The boost in data could massively accelerate Physical AI research in high-precision domains including manufacturing, autonomous driving, agriculture, healthcare & surgery, and drone operations. Additionally, our coding agent pipeline closely resembles Adam, a startup from the Winter 2025 YC batch. Their open-sourced AI-powered CAD tool also involves prompting coding agents to write in OpenSCAD. Instead of direct code generation from text, we add an intermediate step where the agent designs a 2D blueprint with HTML. Having Claude think like a human engineer, we saw promising improvements. With extra refinements, we believe that our alternative approach can outperform Adam in the future. Not only do we have the 2D blueprint intermediary step for extra precision, but users are also able to edit the final 3D model output as well with the TRELLIS-generated model. What We Learned The biggest lesson learned was definitely coordinating polyglot systems. Managing different frameworks, languages, and runtime environments taught us invaluable lessons about API design, abstraction layers, and integration patterns. Working with the Claude SDK showed us how quickly AI can help developers master new tools — Claude literally taught us how to use Claude effectively. This meta-learning experience highlighted AI as a development partner, not just a tool. We deepened our understanding of: Agent SDK design and extensibility File I/O and agent skill composition Infrastructure orchestration across runtimes Real-time AI inference optimization The limits of computer vision for parametric reconstruction What's Next for Voxal CAD We believe that with more optimal visual aids, the OpenSCAD-based coding agent can produce more complex builds. We've not had enough time to explore various CV algorithms that extract critical features from images. Methods like Canny edge detection, bilateral filtering, perspective correction, spline smoothing, and symmetry detection can help a coding agent understand the exact curvatures and overall topology of a given object.

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.


## Detected evidence (automated analysis)

Indexed codebase: 114 recognized source files, 846 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- PostgreSQL (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 141)

```
.gitignore
.uploads/assets/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/f7ecf7f9-837c-4afe-968b-54a04cf086f2-Core.stl
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/0bd24e8e-b778-4528-b609-2a81d316517a.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/2de7f9a6-e376-4ad9-95dd-004f308bde3b.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/47dab38d-6440-45ba-8aa7-9cef4c87937f.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/4e46f994-1062-4d93-bd68-cb8d25cc6c30.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/6a265a8a-1a17-467d-8e43-14cfc8556e3b.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/73e079aa-495f-4dfa-90f3-734d8d680dae.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/83d13332-d9b7-43e6-b125-b895e3d3ab72.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/8941b0c4-9959-4e22-ad78-1ca3816f6f53.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/90c8c084-63f5-4f0e-8c38-a48c265eaea4.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/9564819a-6988-4a68-a415-dca50e1ccaf9.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/9e8aa563-c66c-4969-99ee-bd24f2f2967e.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/b7926305-9197-488c-995e-31d126e4a609.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/c64bd270-16a5-4964-9ed3-b3309131adb5.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/d9078d82-ebf2-447e-9e0c-9b5ff73e6ef1.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/deae6c47-09d0-46f5-900a-6082ee246e85.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/eb881deb-5b09-41d6-810d-fa3b5fe08495.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/ef1c115f-10d0-4be4-9a64-2dfbfdf7177a.glb
.uploads/trellis/zHOuWwt4WWh3HTEv5eQIxBl0lYwS4WFj/outputs/a57dc6c9-291b-4a3d-8cc2-e68129e18289.glb
app/(protected)/assets/[assetId]/actions.ts
app/(protected)/assets/[assetId]/page.tsx
app/(protected)/assets/components/asset-card-menu.tsx
app/(protected)/assets/components/new-asset-menu.tsx
app/(protected)/assets/image-to-3d/page.tsx
app/(protected)/assets/page.tsx
app/(protected)/dashboard/actions.ts
app/(protected)/dashboard/page.tsx
app/(protected)/generate/asset-editor/page.tsx
app/(protected)/generate/components/asset-editor-workbench.tsx
app/(protected)/generate/components/image-to-3d-workbench.tsx
app/(protected)/generate/components/text-to-3d-workbench.tsx
app/(protected)/generate/image-to-3d/page.tsx
app/(protected)/generate/text-to-3d/page.tsx
app/(protected)/layout.tsx
app/(protected)/projects/[projectId]/page.tsx
app/(protected)/projects/[projectId]/project-scene-preview.tsx
app/(protected)/projects/[projectId]/project-settings-form.tsx
app/(protected)/projects/components/new-project-button.tsx
app/(protected)/projects/components/project-uploader.tsx
app/(protected)/projects/page.tsx
app/(protected)/studio/[projectId]/page.tsx
app/(protected)/studio/components/cad-studio-workbench.tsx
app/(protected)/studio/components/studio-project-picker.tsx
app/(protected)/studio/page.tsx
app/api/assets/[assetId]/edit/route.ts
app/api/assets/[assetId]/file/route.ts
app/api/assets/[assetId]/route.ts
app/api/assets/[assetId]/thumbnail/route.ts
app/api/assets/route.ts
app/api/assets/search/route.ts
app/api/auth/[...all]/route.ts
app/api/blueprint/[...path]/route.ts
app/api/projects/[projectId]/assets/route.ts
app/api/projects/[projectId]/route.ts
app/api/projects/[projectId]/scene/route.ts
app/api/projects/route.ts
app/api/search/route.ts
app/api/segment/route.ts
app/api/trellis/download/[jobId]/route.ts
app/api/trellis/models/[filename]/route.ts
app/api/trellis/models/route.ts
app/api/trellis/progress/[jobId]/route.ts
app/api/trellis/upload/route.ts
app/auth/page.tsx
app/globals.css
app/layout.tsx
app/login/page.tsx
app/page.tsx
app/signup/page.tsx
components.json
components/app-shell-header.tsx
components/app-sidebar.tsx
components/asset-card-thumbnail.tsx
components/asset-editor.tsx
components/asset-preview.tsx
components/auth/auth-page.tsx
components/dashboard/project-stats-card.tsx
components/floating-card.tsx
components/model-thumbnail-generator.tsx
components/projects/project-card-menu.tsx
components/projects/project-card.tsx
components/sign-out-button.tsx
components/site-header.tsx
components/trellis-model-viewer.tsx
components/ui/alert-dialog.tsx
components/ui/badge.tsx
components/ui/button.tsx
components/ui/card.tsx
components/ui/combobox.tsx
components/ui/dialog.tsx
components/ui/dropdown-menu.tsx
components/ui/field.tsx
components/ui/input-group.tsx
components/ui/input.tsx
components/ui/label.tsx
components/ui/select.tsx
components/ui/separator.tsx
components/ui/sheet.tsx
components/ui/sidebar.tsx
components/ui/skeleton.tsx
components/ui/spinner.tsx
components/ui/textarea.tsx
components/ui/tooltip.tsx
eslint.config.mjs
hooks/use-mobile.ts
lib/asset-title.ts
lib/auth-client.ts
lib/auth.ts
lib/db.ts
lib/model-mime.ts
lib/projects.ts
lib/segmentation-url.ts
lib/session.ts
lib/trellis-jobs.ts
lib/trellis-url.ts
lib/trellis.ts
lib/utils.ts
next.config.ts
package.json
[21 more files omitted for size]
```

### Dependencies

- package.json: @base-ui/react@^1.2.0, @prisma/adapter-pg@^7.4.0, @prisma/client@^7.4.0, @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, @types/three@^0.182.0, better-auth@^1.3.34, class-variance-authority@^0.7.1, clsx@^2.1.1, dotenv@^17.2.3, eslint@^9, eslint-config-next@16.1.6, lucide-react@^0.564.0, next@16.1.6, pg@^8.18.0, prisma@^7.4.0, radix-ui@^1.4.3, react@19.2.3, react-dom@19.2.3, react-icons@^5.5.0, recharts@^3.7.0, shadcn@^3.8.4, tailwind-merge@^3.4.0, tailwindcss@^4, three@^0.182.0, tw-animate-css@^1.4.0, typescript@^5

### Recent commits (newest first)

- final
- cleanup before trellis update
- init
- Initial commit from Create Next App

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "treehacks",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "postinstall": "prisma generate",
    "prisma:generate": "prisma generate",
    "prisma:migrate": "prisma migrate dev",
    "prisma:studio": "prisma studio"
  },
  "dependencies": {
    "@base-ui/react": "^1.2.0",
    "@prisma/adapter-pg": "^7.4.0",
    "@prisma/client": "^7.4.0",
    "@types/three": "^0.182.0",
    "better-auth": "^1.3.34",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "dotenv": "^17.2.3",
    "lucide-react": "^0.564.0",
    "next": "16.1.6",
    "pg": "^8.18.0",
    "radix-ui": "^1.4.3",
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "react-icons": "^5.5.0",
    "recharts": "^3.7.0",
    "tailwind-merge": "^3.4.0",
    "three": "^0.182.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.1.6",
    "prisma": "^7.4.0",
    "shadcn": "^3.8.4",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.4.0",
    "typescript": "^5"
  }
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { TooltipProvider } from "@/components/ui/tooltip";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Voxal",
  description: "Voxal is the next generation of AI-powered CAD software.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" className="dark">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <TooltipProvider>{children}</TooltipProvider>
      </body>
    </html>
  );
}

```

### app/page.tsx

```typescript
import Link from "next/link";
import Image from "next/image";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getCurrentSession } from "@/lib/session";
import SiteHeader from "@/components/site-header";
import { ArrowRight } from "lucide-react";
import FloatingCard from "@/components/floating-card";

const partners = [
  {
    id: "treehacks",
    name: "TreeHacks",
    logoSrc: "/treehacks1.png",
  },
  {
    id: "microsoft",
    name: "Microsoft",
    logoSrc: "/microsoft.webp",
  },
  { id: "stanford", name: "Stanford", logoSrc: "/stanford1.png" },
  { id: "mit", name: "MIT", logoSrc: "/mit1.png" },
  { id: "anthropic", name: "Anthropic", logoSrc: "/anthropic.svg" },
  { id: "openai", name: "OpenAI", logoSrc: "/openai1.png" },
];

const features = [
  {
    title: "Generate complex CAD from prompts",
    description:
      "Describe a concept in plain language and produce editable, production-ready geometry in seconds.",
    metric: "10x faster ideation",
    tone: "from-sky-500/5 via-indigo-500/2 to-cyan-500/5",
  },
  {
    title: "Collaborate with live model sessions",
    description:
      "Review, annotate, and branch design decisions with teammates in real time, directly in-browser.",
    metric: "0 design handoff lag",
    tone: "from-fuchsia-500/5 via-purple-500/2 to-blue-500/5",
  },
  {
    title: "Automated manufacturability checks",
    description:
      "Run AI-driven tolerance and material checks before export so fabrication issues are caught early.",
    metric: "92% fewer revisions",
    tone: "from-emerald-500/5 via-teal-500/2 to-blue-500/5",
  },
  {
    title: "Secure asset pipeline",
    description:
      "Role-based access, versioned artifacts, and signed exports keep every model traceable and protected.",
    metric: "SOC2-ready workflow",
    tone: "from-violet-500/5 via-indigo-500/2 to-sky-500/5",
  },
];

export default async function Home() {
  const session = await getCurrentSession();

  return (
    <main className="relative isolate min-h-screen overflow-hidden bg-[#060816] text-white">
      <div className="pointer-events-none absolute inset-0">
        {/* <div className="hero-orb absolute -top-56 -left-56 h-[36rem] w-[36rem] rounded-full bg-[radial-gradient(circle_at_center,rgba(56,189,248,0.45),rgba(79,70,229,0.18)_42%,transparent_75%)] blur-3xl" /> */}
        {/* <div className="hero-wave absolute -right-32 -top-16 h-[34rem] w-[34rem] rounded-full bg-[conic-gradient(from_220deg_at_50%_50%,rgba(14,255,100,0.7),rgba(255,100,246,0.3),rgba(14,165,233,0.7),rgba(255,0,0,1))] opacity-80 blur-2xl" /> */}
        <Image
          src="/voxalLogoTransparent.png"
          alt="Voxal Logo"
          width={800}
          height={800}
          className="absolute -top-48 -right-24 opacity-20 blur-sm grayscale"
        />
      </div>
      <div className="pointer-events-none absolute top-18 bottom-0 left-[max(1rem,calc((100vw-80rem)/2))] z-10 w-px bg-white/20" />
      <div className="pointer-events-none absolute top-18 bottom-0 right-[max(1rem,calc((100vw-80rem)/2))] z-10 w-px bg-white/20" />

      <SiteHeader session={session} />

      <div className="relative mx-auto flex w-full max-w-7xl flex-col px-6 pb-24 pt-32 md:px-10 lg:px-12">
        <section className="grid items-center gap-14 lg:grid-cols-[1.08fr_0.92fr]">
          <div>
            <h1 className="border-l-[4px] border-cyan-300 -ml-12 pl-[44px] pb-1 max-w-[600px] text-5xl leading-[1.06] font-semibold tracking-tight text-balance md:text-[64px]">
              Revamped CAD for the AI era
            </h1>
            <p className="mt-6 max-w-[600px] text-lg leading-relaxed text-slate-300">
              Voxal is the AI-native CAD studio that turns your imagination into
              assets. Generate, manage, and edit 3D models with the power of AI.
            </p>

            <div className="mt-9 flex flex-wrap items-center gap-3">
              <Button
                asChild
                size="lg"
                className="bg-cyan-400 text-slate-950 hover:bg-cyan-300"
              >
                <Link href={session ? "/dashboard" : "/login"}>
                  {session ? "Open dashboard" : "Try Voxal"} <ArrowRight />
                </Link>
              </Button>
              <Button
                asChild
                size="lg"
                variant="outline"
                className="border-white/20 bg-white/5 text-white hover:bg-white/10"
              >
                <Link href={session ? "/dashboard" : "/login"}>
                  Book product demo
                </Link>
              </Button>
            </div>

            <div className="mt-12 grid grid-cols-2 gap-6 text-sm text-slate-300 sm:grid-cols-3">
              <div>
                <p className="text-2xl font-semibold text-cyan-200">4.8x</p>
                <p className="mt-1">Faster concept-to-prototype cycles</p>
              </div>
              <div>
                <p className="text-2xl font-semibold text-cyan-200">99.9%</p>
                <p className="mt-1">Pipeline reliability across teams</p>
              </div>
              <div>
                <p className="text-2xl font-semibold text-cyan-200">35k+</p>
                <p className="mt-1">Generated CAD assets in production</p>
              </div>
            </div>
          </div>
        </section>

        <section className="mt-14 -mx-6 px-6 py-4 md:-mx-10 md:px-10 lg:-mx-12 lg:px-12 border-y border-white/20">
          <div className="flex justify-center gap-20 text-sm text-slate-300">
            {partners.map((partner) => (
              <div
                key={partner.id}
                className="flex h-20 items-center justify-center rounded-lg"
              >
                <Image
                  src={partner.logoSrc}
                  alt={partner.name}
                  width={140}
                  height={40}
                
[truncated — 1718 more characters]
```

### app/login/page.tsx

```typescript
import { Suspense } from "react";
import { AuthPage } from "@/components/auth/auth-page";

export default function LoginPage() {
  return (
    <Suspense fallback={null}>
      <AuthPage mode="login" />
    </Suspense>
  );
}

```

### app/signup/page.tsx

```typescript
import { Suspense } from "react";
import { AuthPage } from "@/components/auth/auth-page";

export default function SignupPage() {
  return (
    <Suspense fallback={null}>
      <AuthPage mode="signup" />
    </Suspense>
  );
}

```

### app/auth/page.tsx

```typescript
import { redirect } from "next/navigation";

export default async function AuthAliasPage({
  searchParams,
}: {
  searchParams: Promise<{ next?: string }>;
}) {
  const { next } = await searchParams;
  const loginUrl = next ? `/login?next=${encodeURIComponent(next)}` : "/login";
  redirect(loginUrl);
}

```

### app/(protected)/layout.tsx

```typescript
import { AppShellHeader } from "@/components/app-shell-header";
import { AppSidebar } from "@/components/app-sidebar";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { requireSession } from "@/lib/session";

export const runtime = "nodejs";

export default async function ProtectedLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const session = await requireSession();

  return (
    <SidebarProvider>
      <AppSidebar user={{ name: session.user.name, email: session.user.email }} />
      <SidebarInset>
        <AppShellHeader />
        <div className="bg-muted/35 min-h-[calc(100svh-3.5rem)] p-4 lg:p-6 lg:pt-8">
          {children}
        </div>
      </SidebarInset>
    </SidebarProvider>
  );
}

```

### app/(protected)/studio/page.tsx

```typescript
import StudioProjectPicker from "@/app/(protected)/studio/components/studio-project-picker";
import { getProjectsForUser } from "@/lib/projects";
import { requireSession } from "@/lib/session";

export default async function StudioPage() {
  const session = await requireSession();
  const projects = await getProjectsForUser(session.user.id);

  return (
    <main className="mx-auto w-full max-w-6xl">
      <div className="space-y-2">
        <h1 className="text-3xl font-semibold">Studio</h1>
        <p className="text-muted-foreground text-sm">
          Choose a project from the combobox to open its scene.
        </p>
      </div>
      <div className="mt-6 max-w-xl">
        <StudioProjectPicker
          projects={projects.map((project) => ({
            id: project.id,
            name: project.name,
          }))}
        />
      </div>
      {projects.length === 0 ? (
        <p className="text-muted-foreground mt-6 text-sm">
          No projects found. Create one from Projects first.
        </p>
      ) : null}
    </main>
  );
}

```

### app/(protected)/projects/page.tsx

```typescript
import ProjectCard from "@/components/projects/project-card";
import NewProjectButton from "@/app/(protected)/projects/components/new-project-button";
import { getProjectsForUser } from "@/lib/projects";
import { requireSession } from "@/lib/session";

export default async function ProjectsPage() {
  const session = await requireSession();
  const projects = await getProjectsForUser(session.user.id);

  return (
    <main className="mx-auto w-full max-w-6xl">
      <div className="flex items-center justify-between">
        <h1 className="text-3xl font-semibold">Projects</h1>
        <NewProjectButton />
      </div>

      <div className="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3">
        {projects.length === 0 ? (
          <p className="text-muted-foreground rounded border border-dashed p-4 text-sm sm:col-span-2 xl:col-span-3">
            No projects yet. Create one from dashboard.
          </p>
        ) : (
          projects.map((project) => <ProjectCard key={project.id} project={project} />)
        )}
      </div>
    </main>
  );
}

```

### app/api/segment/route.ts

```typescript
import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { getSegmentationBaseUrl } from "@/lib/segmentation-url";

export const runtime = "nodejs";

export async function POST(request: Request) {
  const session = await auth.api.getSession({ headers: request.headers });

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  let baseUrl: string;
  try {
    baseUrl = getSegmentationBaseUrl();
  } catch (error) {
    return NextResponse.json(
      {
        error:
          error instanceof Error
            ? error.message
            : "SEGMENTATION_URL is not configured.",
      },
      { status: 500 },
    );
  }

  const incoming = await request.formData();
  const file = incoming.get("file");
  const classes = incoming.get("classes");

  if (!(file instanceof File)) {
    return NextResponse.json(
      { error: "Missing file field: file" },
      { status: 400 },
    );
  }

  const upstreamForm = new FormData();
  upstreamForm.append("file", file, file.name);
  if (typeof classes === "string") {
    upstreamForm.append("classes", classes);
  }

  const upstreamResponse = await fetch(`${baseUrl}/api/segment`, {
    method: "POST",
    body: upstreamForm,
  });

  const payloadText = await upstreamResponse.text();
  if (!upstreamResponse.ok) {
    return new Response(payloadText, {
      status: upstreamResponse.status,
      headers: {
        "Content-Type":
          upstreamResponse.headers.get("content-type") ?? "application/json",
      },
    });
  }

  return new Response(payloadText, {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}

```

[99 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]