Full Code of firecrawl/open-lovable for AI

main 69bd93bae7a9 cached
326 files
2.0 MB
465.2k tokens
453 symbols
1 requests
Download .txt
Showing preview only (2,107K chars total). Download the full file or copy to clipboard to get everything.
Repository: firecrawl/open-lovable
Branch: main
Commit: 69bd93bae7a9
Files: 326
Total size: 2.0 MB

Directory structure:
gitextract__mw591t9/

├── .cursor/
│   └── mcp.json
├── .env.example
├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── api/
│   │   ├── analyze-edit-intent/
│   │   │   └── route.ts
│   │   ├── apply-ai-code/
│   │   │   └── route.ts
│   │   ├── apply-ai-code-stream/
│   │   │   └── route.ts
│   │   ├── check-vite-errors/
│   │   │   └── route.ts
│   │   ├── clear-vite-errors-cache/
│   │   │   └── route.ts
│   │   ├── conversation-state/
│   │   │   └── route.ts
│   │   ├── create-ai-sandbox/
│   │   │   └── route.ts
│   │   ├── create-ai-sandbox-v2/
│   │   │   └── route.ts
│   │   ├── create-zip/
│   │   │   └── route.ts
│   │   ├── detect-and-install-packages/
│   │   │   └── route.ts
│   │   ├── extract-brand-styles/
│   │   │   └── route.ts
│   │   ├── generate-ai-code-stream/
│   │   │   └── route.ts
│   │   ├── get-sandbox-files/
│   │   │   └── route.ts
│   │   ├── install-packages/
│   │   │   └── route.ts
│   │   ├── install-packages-v2/
│   │   │   └── route.ts
│   │   ├── kill-sandbox/
│   │   │   └── route.ts
│   │   ├── monitor-vite-logs/
│   │   │   └── route.ts
│   │   ├── report-vite-error/
│   │   │   └── route.ts
│   │   ├── restart-vite/
│   │   │   └── route.ts
│   │   ├── run-command/
│   │   │   └── route.ts
│   │   ├── run-command-v2/
│   │   │   └── route.ts
│   │   ├── sandbox-logs/
│   │   │   └── route.ts
│   │   ├── sandbox-status/
│   │   │   └── route.ts
│   │   ├── scrape-screenshot/
│   │   │   └── route.ts
│   │   ├── scrape-url-enhanced/
│   │   │   └── route.ts
│   │   ├── scrape-website/
│   │   │   └── route.ts
│   │   └── search/
│   │       └── route.ts
│   ├── builder/
│   │   └── page.tsx
│   ├── generation/
│   │   └── page.tsx
│   ├── globals.css
│   ├── landing.tsx
│   ├── layout.tsx
│   └── page.tsx
├── atoms/
│   └── sheets.ts
├── colors.json
├── components/
│   ├── CodeApplicationProgress.tsx
│   ├── FirecrawlIcon.tsx
│   ├── FirecrawlLogo.tsx
│   ├── HMRErrorDetector.tsx
│   ├── HeroInput.tsx
│   ├── SandboxPreview.tsx
│   ├── app/
│   │   ├── (home)/
│   │   │   └── sections/
│   │   │       ├── ai-readiness/
│   │   │       │   ├── ControlPanel.tsx
│   │   │       │   ├── InlineResults.tsx
│   │   │       │   ├── MetricBars.tsx
│   │   │       │   ├── RadarChart.tsx
│   │   │       │   └── ScoreChart.tsx
│   │   │       ├── endpoints/
│   │   │       │   ├── EndpointsCrawl/
│   │   │       │   │   └── EndpointsCrawl.tsx
│   │   │       │   ├── EndpointsExtract/
│   │   │       │   │   └── EndpointsExtract.tsx
│   │   │       │   ├── EndpointsMap/
│   │   │       │   │   └── EndpointsMap.tsx
│   │   │       │   ├── EndpointsScrape/
│   │   │       │   │   └── EndpointsScrape.tsx
│   │   │       │   ├── EndpointsSearch/
│   │   │       │   │   └── EndpointsSearch.tsx
│   │   │       │   ├── Extract/
│   │   │       │   │   └── Extract.tsx
│   │   │       │   └── Mcp/
│   │   │       │       └── Mcp.tsx
│   │   │       ├── hero/
│   │   │       │   ├── Background/
│   │   │       │   │   ├── Background.tsx
│   │   │       │   │   ├── BackgroundOuterPiece.tsx
│   │   │       │   │   └── _svg/
│   │   │       │   │       └── CenterStar.tsx
│   │   │       │   ├── Badge/
│   │   │       │   │   └── Badge.tsx
│   │   │       │   ├── Hero.tsx
│   │   │       │   ├── Pixi/
│   │   │       │   │   ├── Pixi.tsx
│   │   │       │   │   └── tickers/
│   │   │       │   │       ├── ascii.ts
│   │   │       │   │       └── features/
│   │   │       │   │           ├── cell.ts
│   │   │       │   │           ├── cellReveal.ts
│   │   │       │   │           ├── components/
│   │   │       │   │           │   ├── AnimatedRect.ts
│   │   │       │   │           │   ├── BlinkingContainer.ts
│   │   │       │   │           │   └── Dot.ts
│   │   │       │   │           ├── crawl.ts
│   │   │       │   │           ├── index.ts
│   │   │       │   │           ├── mapping.ts
│   │   │       │   │           ├── scrape.ts
│   │   │       │   │           └── search.ts
│   │   │       │   └── Title/
│   │   │       │       └── Title.tsx
│   │   │       ├── hero-flame/
│   │   │       │   ├── HeroFlame.tsx
│   │   │       │   └── data.json
│   │   │       ├── hero-input/
│   │   │       │   ├── Button/
│   │   │       │   │   └── Button.tsx
│   │   │       │   ├── HeroInput.tsx
│   │   │       │   ├── Tabs/
│   │   │       │   │   ├── Mobile/
│   │   │       │   │   │   └── Mobile.tsx
│   │   │       │   │   └── Tabs.tsx
│   │   │       │   └── _svg/
│   │   │       │       ├── ArrowRight.tsx
│   │   │       │       └── Globe.tsx
│   │   │       └── hero-scraping/
│   │   │           ├── Code/
│   │   │           │   ├── Code.tsx
│   │   │           │   └── Loading/
│   │   │           │       ├── Loading.tsx
│   │   │           │       └── _svg/
│   │   │           │           └── Check.tsx
│   │   │           ├── HeroScraping.css
│   │   │           ├── HeroScraping.tsx
│   │   │           ├── Tag/
│   │   │           │   └── Tag.tsx
│   │   │           └── _svg/
│   │   │               ├── BrowserMobile.tsx
│   │   │               └── BrowserTab.tsx
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── home-page-components.md
│   │   └── generation/
│   │       ├── SidebarInput.tsx
│   │       └── SidebarQuickInput.tsx
│   ├── shared/
│   │   ├── Playground/
│   │   │   └── Context/
│   │   │       └── types.ts
│   │   ├── animated-dot-icon.tsx
│   │   ├── animated-height.tsx
│   │   ├── ascii-background.tsx
│   │   ├── ascii-flame-background.tsx
│   │   ├── button/
│   │   │   ├── Button.css
│   │   │   └── Button.tsx
│   │   ├── buttons/
│   │   │   ├── capsule-button.tsx
│   │   │   ├── fire-action-link.tsx
│   │   │   ├── index.ts
│   │   │   └── slate-button.tsx
│   │   ├── color-styles/
│   │   │   └── color-styles.tsx
│   │   ├── combobox/
│   │   │   └── combobox.tsx
│   │   ├── effects/
│   │   │   ├── .cursor/
│   │   │   │   └── rules/
│   │   │   │       └── flame-effects.md
│   │   │   ├── flame/
│   │   │   │   ├── Flame.tsx
│   │   │   │   ├── ascii-explosion.tsx
│   │   │   │   ├── auth-pulse/
│   │   │   │   │   ├── auth-pulse.tsx
│   │   │   │   │   └── pulse-data.json
│   │   │   │   ├── core-flame.json
│   │   │   │   ├── core-flame.tsx
│   │   │   │   ├── explosion-data.json
│   │   │   │   ├── flame-background.tsx
│   │   │   │   ├── hero-flame-data.json
│   │   │   │   ├── hero-flame.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── slate-grid/
│   │   │   │   │   ├── grid-data.json
│   │   │   │   │   └── slate-grid.tsx
│   │   │   │   ├── subtle-explosion.tsx
│   │   │   │   └── subtle-wave/
│   │   │   │       ├── subtle-wave.tsx
│   │   │   │       └── wave-data.json
│   │   │   ├── index.ts
│   │   │   └── subtle-ascii-animation.tsx
│   │   ├── firecrawl-icon/
│   │   │   ├── firecrawl-icon-static.tsx
│   │   │   └── firecrawl-icon.tsx
│   │   ├── header/
│   │   │   ├── BrandKit/
│   │   │   │   ├── BrandKit.tsx
│   │   │   │   └── _svg/
│   │   │   │       ├── Download.tsx
│   │   │   │       ├── Guidelines.tsx
│   │   │   │       └── Icon.tsx
│   │   │   ├── Dropdown/
│   │   │   │   ├── Content/
│   │   │   │   │   ├── Content.tsx
│   │   │   │   │   └── NavItemRow.tsx
│   │   │   │   ├── Github/
│   │   │   │   │   ├── Flame/
│   │   │   │   │   │   ├── Flame.tsx
│   │   │   │   │   │   └── data.json
│   │   │   │   │   └── Github.tsx
│   │   │   │   ├── Mobile/
│   │   │   │   │   ├── Item/
│   │   │   │   │   │   └── Item.tsx
│   │   │   │   │   └── Mobile.tsx
│   │   │   │   ├── Stories/
│   │   │   │   │   ├── Flame/
│   │   │   │   │   │   └── Flame.tsx
│   │   │   │   │   ├── Stories.tsx
│   │   │   │   │   └── _svg/
│   │   │   │   │       ├── ArrowUp.tsx
│   │   │   │   │       └── Replit.tsx
│   │   │   │   └── Wrapper/
│   │   │   │       └── Wrapper.tsx
│   │   │   ├── Github/
│   │   │   │   ├── GithubClient.tsx
│   │   │   │   └── _svg/
│   │   │   │       └── GithubIcon.tsx
│   │   │   ├── HeaderContext.tsx
│   │   │   ├── Nav/
│   │   │   │   ├── Item/
│   │   │   │   │   ├── Item.tsx
│   │   │   │   │   └── _svg/
│   │   │   │   │       └── ChevronDown.tsx
│   │   │   │   ├── Nav.tsx
│   │   │   │   ├── RenderEndpointIcon.tsx
│   │   │   │   └── _svg/
│   │   │   │       ├── Affiliate.tsx
│   │   │   │       ├── Api.tsx
│   │   │   │       ├── ArrowRight.tsx
│   │   │   │       ├── Careers.tsx
│   │   │   │       ├── Changelog.tsx
│   │   │   │       ├── Chats.tsx
│   │   │   │       ├── Lead.tsx
│   │   │   │       ├── MCP.tsx
│   │   │   │       ├── Platforms.tsx
│   │   │   │       ├── Research.tsx
│   │   │   │       ├── Student.tsx
│   │   │   │       └── Templates.tsx
│   │   │   ├── Toggle/
│   │   │   │   └── Toggle.tsx
│   │   │   ├── Wrapper/
│   │   │   │   └── Wrapper.tsx
│   │   │   └── _svg/
│   │   │       └── Logo.tsx
│   │   ├── hero-flame.tsx
│   │   ├── icons/
│   │   │   ├── GitHub.tsx
│   │   │   ├── Logo.tsx
│   │   │   ├── animated-chevron.tsx
│   │   │   ├── animated-icons.tsx
│   │   │   ├── arrow-animated.tsx
│   │   │   ├── check.tsx
│   │   │   ├── chevron-slide.tsx
│   │   │   ├── copied.tsx
│   │   │   ├── copy.tsx
│   │   │   ├── curve.tsx
│   │   │   ├── fingerprint-icon.tsx
│   │   │   ├── openai.tsx
│   │   │   ├── source-icon.tsx
│   │   │   ├── symbol-colored.tsx
│   │   │   ├── symbol-white.tsx
│   │   │   ├── tremor-placeholder.tsx
│   │   │   ├── wordmark-colored.tsx
│   │   │   └── wordmark-white.tsx
│   │   ├── image/
│   │   │   ├── Image.tsx
│   │   │   └── getImageSrc.ts
│   │   ├── layout/
│   │   │   ├── animated-height.tsx
│   │   │   ├── animated-width.tsx
│   │   │   ├── curvy-rect-divider.tsx
│   │   │   └── curvy-rect.tsx
│   │   ├── loading/
│   │   │   ├── Shimmer.tsx
│   │   │   └── usage-loading.tsx
│   │   ├── lockBody.tsx
│   │   ├── logo-cloud/
│   │   │   ├── index.ts
│   │   │   ├── logo-cloud.tsx
│   │   │   └── logo-cloud2/
│   │   │       ├── Logocloud.css
│   │   │       └── Logocloud.tsx
│   │   ├── notifications/
│   │   │   └── slack-notification.tsx
│   │   ├── pixi/
│   │   │   ├── Pixi.tsx
│   │   │   ├── PixiAssetManager.ts
│   │   │   └── utils.ts
│   │   ├── portal-to-body/
│   │   │   └── PortalToBody.tsx
│   │   ├── preview/
│   │   │   ├── json-error-highlighter.tsx
│   │   │   ├── live-preview-frame.tsx
│   │   │   ├── multiple-web-browsers.tsx
│   │   │   └── web-browser.tsx
│   │   ├── pylon.tsx
│   │   ├── search-params-provider/
│   │   │   └── search-params-provider.tsx
│   │   ├── section-head/
│   │   │   ├── SectionHead.css
│   │   │   └── SectionHead.tsx
│   │   ├── section-title/
│   │   │   └── SectionTitle.tsx
│   │   ├── tabs/
│   │   │   └── Tabs.tsx
│   │   ├── ui/
│   │   │   ├── app-dialog.tsx
│   │   │   ├── ascii-dot-loader.tsx
│   │   │   ├── dot-grid-loader.tsx
│   │   │   ├── empty-state.tsx
│   │   │   ├── index.ts
│   │   │   ├── loading-state.tsx
│   │   │   ├── mobile-sheet.tsx
│   │   │   └── stat-card.tsx
│   │   └── utils/
│   │       └── portal-to-body.tsx
│   └── ui/
│       ├── button.tsx
│       ├── checkbox.tsx
│       ├── code.tsx
│       ├── input.tsx
│       ├── label.tsx
│       ├── motion/
│       │   └── scramble-text.tsx
│       ├── select.tsx
│       ├── shadcn/
│       │   ├── accordion.tsx
│       │   ├── alert-dialog.tsx
│       │   ├── badge.tsx
│       │   ├── button.css
│       │   ├── button.tsx
│       │   ├── card.tsx
│       │   ├── checkbox.tsx
│       │   ├── collapsible.tsx
│       │   ├── combobox.tsx
│       │   ├── context-menu.tsx
│       │   ├── data-table.tsx
│       │   ├── dialog.tsx
│       │   ├── dropdown-menu.tsx
│       │   ├── form.tsx
│       │   ├── input.tsx
│       │   ├── label.tsx
│       │   ├── navigation-menu.tsx
│       │   ├── popover.tsx
│       │   ├── progress.tsx
│       │   ├── scroll-area.tsx
│       │   ├── select.tsx
│       │   ├── separator.tsx
│       │   ├── sheet.tsx
│       │   ├── slider.tsx
│       │   ├── switch.tsx
│       │   ├── tabs.tsx
│       │   ├── textarea.tsx
│       │   ├── toast.tsx
│       │   ├── toggle.tsx
│       │   ├── tooltip-radix.tsx
│       │   └── tooltip.tsx
│       ├── spinner.tsx
│       └── textarea.tsx
├── config/
│   └── app.config.ts
├── eslint.config.mjs
├── hooks/
│   ├── useDebouncedCallback.ts
│   ├── useDebouncedEffect.ts
│   └── useSwitchingCode.ts
├── lib/
│   ├── ai/
│   │   └── provider-manager.ts
│   ├── build-validator.ts
│   ├── context-selector.ts
│   ├── edit-examples.ts
│   ├── edit-intent-analyzer.ts
│   ├── file-parser.ts
│   ├── file-search-executor.ts
│   ├── icons.ts
│   ├── morph-fast-apply.ts
│   ├── sandbox/
│   │   ├── factory.ts
│   │   ├── providers/
│   │   │   ├── e2b-provider.ts
│   │   │   └── vercel-provider.ts
│   │   ├── sandbox-manager.ts
│   │   └── types.ts
│   └── utils.ts
├── next.config.ts
├── package.json
├── packages/
│   └── create-open-lovable/
│       ├── index.js
│       ├── lib/
│       │   ├── installer.js
│       │   └── prompts.js
│       ├── package.json
│       └── templates/
│           ├── e2b/
│           │   ├── .env.example
│           │   └── README.md
│           └── vercel/
│               ├── .env.example
│               └── README.md
├── postcss.config.mjs
├── public/
│   ├── compressor.json
│   └── firecrawl-logo
├── styles/
│   ├── additional-styles/
│   │   ├── custom-fonts.css
│   │   ├── theme.css
│   │   └── utility-patterns.css
│   ├── chrome-bug.css
│   ├── colors.json
│   ├── components/
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── component-styles.md
│   │   ├── button.css
│   │   ├── code.css
│   │   └── index.css
│   ├── design-system/
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── design-system.md
│   │   ├── animations.css
│   │   ├── base/
│   │   │   ├── body.css
│   │   │   ├── layout.css
│   │   │   └── reset.css
│   │   ├── colors.css
│   │   ├── fonts.css
│   │   ├── typography.css
│   │   └── utilities.css
│   ├── fire.css
│   ├── inside-border-fix.css
│   └── main.css
├── tailwind.config.ts
├── tsconfig.json
├── types/
│   ├── conversation.ts
│   ├── file-manifest.ts
│   └── sandbox.ts
└── utils/
    ├── cn.ts
    ├── init-canvas.ts
    ├── set-timeout-on-visible.ts
    └── sleep.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .cursor/mcp.json
================================================
{
  "mcpServers": {
    "dev3000": {
      "type": "http",
      "url": "http://localhost:3684/mcp"
    }
  }
}


================================================
FILE: .env.example
================================================
# Required
FIRECRAWL_API_KEY=your_firecrawl_api_key  # Get from https://firecrawl.dev (Web scraping)

# =================================================================================
# SANDBOX PROVIDER - Choose Option 1 OR 2
# =================================================================================

# Option 1: Vercel Sandbox (recommended - default)
# Set SANDBOX_PROVIDER=vercel and choose authentication method below
SANDBOX_PROVIDER=vercel

# Vercel Authentication - Choose method a OR b
# Method a: OIDC Token (recommended for development)
# Run `vercel link` then `vercel env pull` to get VERCEL_OIDC_TOKEN automatically
VERCEL_OIDC_TOKEN=auto_generated_by_vercel_env_pull

# Method b: Personal Access Token (for production or when OIDC unavailable)
# VERCEL_TEAM_ID=team_xxxxxxxxx      # Your Vercel team ID 
# VERCEL_PROJECT_ID=prj_xxxxxxxxx    # Your Vercel project ID
# VERCEL_TOKEN=vercel_xxxxxxxxxxxx   # Personal access token from Vercel dashboard

# Get yours at https://console.groq.com
GROQ_API_KEY=your_groq_api_key_here

=======
# Option 2: E2B Sandbox
# Set SANDBOX_PROVIDER=e2b and configure E2B_API_KEY below
# SANDBOX_PROVIDER=e2b
# E2B_API_KEY=your_e2b_api_key  # Get from https://e2b.dev

# =================================================================================
# AI PROVIDERS - Need at least one
# =================================================================================

# Vercel AI Gateway (recommended - provides access to multiple models)
AI_GATEWAY_API_KEY=your_ai_gateway_api_key  # Get from https://vercel.com/dashboard/ai-gateway/api-keys

# Individual provider keys (used when AI_GATEWAY_API_KEY is not set)
ANTHROPIC_API_KEY=your_anthropic_api_key  # Get from https://console.anthropic.com
OPENAI_API_KEY=your_openai_api_key  # Get from https://platform.openai.com (GPT-5)
GEMINI_API_KEY=your_gemini_api_key  # Get from https://aistudio.google.com/app/apikey
GROQ_API_KEY=your_groq_api_key  # Get from https://console.groq.com (Fast inference - Kimi K2 recommended)

# Optional Morph Fast Apply
# Get yours at https://morphllm.com/
MORPH_API_KEY=your_fast_apply_key


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
**/node_modules/
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*
.env.local
!.env.example

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# E2B template builds
*.tar.gz
e2b-template-*

# IDE
.vscode/
.idea/

# Temporary files
*.tmp
*.temp
repomix-output.txt
bun.lockb
.env*.local


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2024

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# Open Lovable

Chat with AI to build React apps instantly. An example app made by the [Firecrawl](https://firecrawl.dev/?ref=open-lovable-github) team. For a complete cloud solution, check out [Lovable.dev](https://lovable.dev/) ❤️.

<img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExbmZtaHFleGRsMTNlaWNydGdianI4NGQ4dHhyZjB0d2VkcjRyeXBucCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ZFVLWMa6dVskQX0qu1/giphy.gif" alt="Open Lovable Demo" width="100%"/>

## Setup

1. **Clone & Install**
```bash
git clone https://github.com/firecrawl/open-lovable.git
cd open-lovable
pnpm install  # or npm install / yarn install
```

2. **Add `.env.local`**

```env
# =================================================================
# REQUIRED
# =================================================================
FIRECRAWL_API_KEY=your_firecrawl_api_key    # https://firecrawl.dev

# =================================================================
# AI PROVIDER - Choose your LLM
# =================================================================
GEMINI_API_KEY=your_gemini_api_key        # https://aistudio.google.com/app/apikey
ANTHROPIC_API_KEY=your_anthropic_api_key  # https://console.anthropic.com
OPENAI_API_KEY=your_openai_api_key        # https://platform.openai.com
GROQ_API_KEY=your_groq_api_key            # https://console.groq.com

# =================================================================
# FAST APPLY (Optional - for faster edits)
# =================================================================
MORPH_API_KEY=your_morphllm_api_key    # https://morphllm.com/dashboard

# =================================================================
# SANDBOX PROVIDER - Choose ONE: Vercel (default) or E2B
# =================================================================
SANDBOX_PROVIDER=vercel  # or 'e2b'

# Option 1: Vercel Sandbox (default)
# Choose one authentication method:

# Method A: OIDC Token (recommended for development)
# Run `vercel link` then `vercel env pull` to get VERCEL_OIDC_TOKEN automatically
VERCEL_OIDC_TOKEN=auto_generated_by_vercel_env_pull

# Method B: Personal Access Token (for production or when OIDC unavailable)
# VERCEL_TEAM_ID=team_xxxxxxxxx      # Your Vercel team ID 
# VERCEL_PROJECT_ID=prj_xxxxxxxxx    # Your Vercel project ID
# VERCEL_TOKEN=vercel_xxxxxxxxxxxx   # Personal access token from Vercel dashboard

# Option 2: E2B Sandbox
# E2B_API_KEY=your_e2b_api_key      # https://e2b.dev
```

3. **Run**
```bash
pnpm dev  # or npm run dev / yarn dev
```

Open [http://localhost:3000](http://localhost:3000)

## License

MIT

================================================
FILE: app/api/analyze-edit-intent/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';
import { createGroq } from '@ai-sdk/groq';
import { createAnthropic } from '@ai-sdk/anthropic';
import { createOpenAI } from '@ai-sdk/openai';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { generateObject } from 'ai';
import { z } from 'zod';
// import type { FileManifest } from '@/types/file-manifest'; // Type is used implicitly through manifest parameter

// Check if we're using Vercel AI Gateway
const isUsingAIGateway = !!process.env.AI_GATEWAY_API_KEY;
const aiGatewayBaseURL = 'https://ai-gateway.vercel.sh/v1';

const groq = createGroq({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GROQ_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
});

const anthropic = createAnthropic({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.ANTHROPIC_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : (process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com/v1'),
});

const openai = createOpenAI({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.OPENAI_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : process.env.OPENAI_BASE_URL,
});

const googleGenerativeAI = createGoogleGenerativeAI({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GEMINI_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
});

// Schema for the AI's search plan - not file selection!
const searchPlanSchema = z.object({
  editType: z.enum([
    'UPDATE_COMPONENT',
    'ADD_FEATURE', 
    'FIX_ISSUE',
    'UPDATE_STYLE',
    'REFACTOR',
    'ADD_DEPENDENCY',
    'REMOVE_ELEMENT'
  ]).describe('The type of edit being requested'),
  
  reasoning: z.string().describe('Explanation of the search strategy'),
  
  searchTerms: z.array(z.string()).describe('Specific text to search for (case-insensitive). Be VERY specific - exact button text, class names, etc.'),
  
  regexPatterns: z.array(z.string()).optional().describe('Regex patterns for finding code structures (e.g., "className=[\\"\\\'].*header.*[\\"\\\']")'),
  
  fileTypesToSearch: z.array(z.string()).default(['.jsx', '.tsx', '.js', '.ts']).describe('File extensions to search'),
  
  expectedMatches: z.number().min(1).max(10).default(1).describe('Expected number of matches (helps validate search worked)'),
  
  fallbackSearch: z.object({
    terms: z.array(z.string()),
    patterns: z.array(z.string()).optional()
  }).optional().describe('Backup search if primary fails')
});

export async function POST(request: NextRequest) {
  try {
    const { prompt, manifest, model = 'openai/gpt-oss-20b' } = await request.json();
    
    console.log('[analyze-edit-intent] Request received');
    console.log('[analyze-edit-intent] Prompt:', prompt);
    console.log('[analyze-edit-intent] Model:', model);
    console.log('[analyze-edit-intent] Manifest files count:', manifest?.files ? Object.keys(manifest.files).length : 0);
    
    if (!prompt || !manifest) {
      return NextResponse.json({
        error: 'prompt and manifest are required'
      }, { status: 400 });
    }
    
    // Create a summary of available files for the AI
    const validFiles = Object.entries(manifest.files as Record<string, any>)
      .filter(([path]) => {
        // Filter out invalid paths
        return path.includes('.') && !path.match(/\/\d+$/);
      });
    
    const fileSummary = validFiles
      .map(([path, info]: [string, any]) => {
        const componentName = info.componentInfo?.name || path.split('/').pop();
        // const hasImports = info.imports?.length > 0; // Kept for future use
        const childComponents = info.componentInfo?.childComponents?.join(', ') || 'none';
        return `- ${path} (${componentName}, renders: ${childComponents})`;
      })
      .join('\n');
    
    console.log('[analyze-edit-intent] Valid files found:', validFiles.length);
    
    if (validFiles.length === 0) {
      console.error('[analyze-edit-intent] No valid files found in manifest');
      return NextResponse.json({
        success: false,
        error: 'No valid files found in manifest'
      }, { status: 400 });
    }
    
    console.log('[analyze-edit-intent] Analyzing prompt:', prompt);
    console.log('[analyze-edit-intent] File summary preview:', fileSummary.split('\n').slice(0, 5).join('\n'));
    
    // Select the appropriate AI model based on the request
    let aiModel;
    if (model.startsWith('anthropic/')) {
      aiModel = anthropic(model.replace('anthropic/', ''));
    } else if (model.startsWith('openai/')) {
      if (model.includes('gpt-oss')) {
        aiModel = groq(model);
      } else {
        aiModel = openai(model.replace('openai/', ''));
      }
    } else if (model.startsWith('google/')) {
      aiModel = googleGenerativeAI(model.replace('google/', ''));
    } else {
      // Default to groq if model format is unclear
      aiModel = groq(model);
    }
    
    console.log('[analyze-edit-intent] Using AI model:', model);
    
    // Use AI to create a search plan
    const result = await generateObject({
      model: aiModel,
      schema: searchPlanSchema,
      messages: [
        {
          role: 'system',
          content: `You are an expert at planning code searches. Your job is to create a search strategy to find the exact code that needs to be edited.

DO NOT GUESS which files to edit. Instead, provide specific search terms that will locate the code.

SEARCH STRATEGY RULES:
1. For text changes (e.g., "change 'Start Deploying' to 'Go Now'"):
   - Search for the EXACT text: "Start Deploying"
   
2. For style changes (e.g., "make header black"):
   - Search for component names: "Header", "<header"
   - Search for class names: "header", "navbar"
   - Search for className attributes containing relevant words
   
3. For removing elements (e.g., "remove the deploy button"):
   - Search for the button text or aria-label
   - Search for relevant IDs or data-testids
   
4. For navigation/header issues:
   - Search for: "navigation", "nav", "Header", "navbar"
   - Look for Link components or href attributes
   
5. Be SPECIFIC:
   - Use exact capitalization for user-visible text
   - Include multiple search terms for redundancy
   - Add regex patterns for structural searches

Current project structure for context:
${fileSummary}`
        },
        {
          role: 'user',
          content: `User request: "${prompt}"

Create a search plan to find the exact code that needs to be modified. Include specific search terms and patterns.`
        }
      ]
    });
    
    console.log('[analyze-edit-intent] Search plan created:', {
      editType: result.object.editType,
      searchTerms: result.object.searchTerms,
      patterns: result.object.regexPatterns?.length || 0,
      reasoning: result.object.reasoning
    });
    
    // Return the search plan, not file matches
    return NextResponse.json({
      success: true,
      searchPlan: result.object
    });
    
  } catch (error) {
    console.error('[analyze-edit-intent] Error:', error);
    return NextResponse.json({
      success: false,
      error: (error as Error).message
    }, { status: 500 });
  }
}

================================================
FILE: app/api/apply-ai-code/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';
import { parseMorphEdits, applyMorphEditToFile } from '@/lib/morph-fast-apply';
import type { SandboxState } from '@/types/sandbox';
import type { ConversationState } from '@/types/conversation';

declare global {
  var conversationState: ConversationState | null;
}

interface ParsedResponse {
  explanation: string;
  template: string;
  files: Array<{ path: string; content: string }>;
  packages: string[];
  commands: string[];
  structure: string | null;
}

function parseAIResponse(response: string): ParsedResponse {
  const sections = {
    files: [] as Array<{ path: string; content: string }>,
    commands: [] as string[],
    packages: [] as string[],
    structure: null as string | null,
    explanation: '',
    template: ''
  };

  // Parse file sections - handle duplicates and prefer complete versions
  const fileMap = new Map<string, { content: string; isComplete: boolean }>();
  
  const fileRegex = /<file path="([^"]+)">([\s\S]*?)(?:<\/file>|$)/g;
  let match;
  while ((match = fileRegex.exec(response)) !== null) {
    const filePath = match[1];
    const content = match[2].trim();
    const hasClosingTag = response.substring(match.index, match.index + match[0].length).includes('</file>');
    
    // Check if this file already exists in our map
    const existing = fileMap.get(filePath);
    
    // Decide whether to keep this version
    let shouldReplace = false;
    if (!existing) {
      shouldReplace = true; // First occurrence
    } else if (!existing.isComplete && hasClosingTag) {
      shouldReplace = true; // Replace incomplete with complete
      console.log(`[parseAIResponse] Replacing incomplete ${filePath} with complete version`);
    } else if (existing.isComplete && hasClosingTag && content.length > existing.content.length) {
      shouldReplace = true; // Replace with longer complete version
      console.log(`[parseAIResponse] Replacing ${filePath} with longer complete version`);
    } else if (!existing.isComplete && !hasClosingTag && content.length > existing.content.length) {
      shouldReplace = true; // Both incomplete, keep longer one
    }
    
    if (shouldReplace) {
      // Additional validation: reject obviously broken content
      if (content.includes('...') && !content.includes('...props') && !content.includes('...rest')) {
        console.warn(`[parseAIResponse] Warning: ${filePath} contains ellipsis, may be truncated`);
        // Still use it if it's the only version we have
        if (!existing) {
          fileMap.set(filePath, { content, isComplete: hasClosingTag });
        }
      } else {
        fileMap.set(filePath, { content, isComplete: hasClosingTag });
      }
    }
  }
  
  // Convert map to array for sections.files
  for (const [path, { content, isComplete }] of fileMap.entries()) {
    if (!isComplete) {
      console.log(`[parseAIResponse] Warning: File ${path} appears to be truncated (no closing tag)`);
    }
    
    sections.files.push({
      path,
      content
    });
  }

  // Parse commands
  const cmdRegex = /<command>(.*?)<\/command>/g;
  while ((match = cmdRegex.exec(response)) !== null) {
    sections.commands.push(match[1].trim());
  }

  // Parse packages - support both <package> and <packages> tags
  const pkgRegex = /<package>(.*?)<\/package>/g;
  while ((match = pkgRegex.exec(response)) !== null) {
    sections.packages.push(match[1].trim());
  }
  
  // Also parse <packages> tag with multiple packages
  const packagesRegex = /<packages>([\s\S]*?)<\/packages>/;
  const packagesMatch = response.match(packagesRegex);
  if (packagesMatch) {
    const packagesContent = packagesMatch[1].trim();
    // Split by newlines or commas
    const packagesList = packagesContent.split(/[\n,]+/)
      .map(pkg => pkg.trim())
      .filter(pkg => pkg.length > 0);
    sections.packages.push(...packagesList);
  }

  // Parse structure
  const structureMatch = /<structure>([\s\S]*?)<\/structure>/;
  const structResult = response.match(structureMatch);
  if (structResult) {
    sections.structure = structResult[1].trim();
  }

  // Parse explanation
  const explanationMatch = /<explanation>([\s\S]*?)<\/explanation>/;
  const explResult = response.match(explanationMatch);
  if (explResult) {
    sections.explanation = explResult[1].trim();
  }

  // Parse template
  const templateMatch = /<template>(.*?)<\/template>/;
  const templResult = response.match(templateMatch);
  if (templResult) {
    sections.template = templResult[1].trim();
  }

  return sections;
}

declare global {
  var activeSandbox: any;
  var activeSandboxProvider: any;
  var existingFiles: Set<string>;
  var sandboxState: SandboxState;
}

export async function POST(request: NextRequest) {
  try {
    const { response, isEdit = false, packages = [] } = await request.json();
    
    if (!response) {
      return NextResponse.json({
        error: 'response is required'
      }, { status: 400 });
    }
    
    // Parse the AI response
    const parsed = parseAIResponse(response);
    const morphEnabled = Boolean(isEdit && process.env.MORPH_API_KEY);
    const morphEdits = morphEnabled ? parseMorphEdits(response) : [];
    console.log('[apply-ai-code] Morph Fast Apply mode:', morphEnabled);
    if (morphEnabled) {
      console.log('[apply-ai-code] Morph edits found:', morphEdits.length);
    }
    
    // Initialize existingFiles if not already
    if (!global.existingFiles) {
      global.existingFiles = new Set<string>();
    }
    
    // Get the active sandbox or provider
    const sandbox = global.activeSandbox || global.activeSandboxProvider;
    
    // If no active sandbox, just return parsed results
    if (!sandbox) {
      return NextResponse.json({
        success: true,
        results: {
          filesCreated: parsed.files.map(f => f.path),
          packagesInstalled: parsed.packages,
          commandsExecuted: parsed.commands,
          errors: []
        },
        explanation: parsed.explanation,
        structure: parsed.structure,
        parsedFiles: parsed.files,
        message: `Parsed ${parsed.files.length} files successfully. Create a sandbox to apply them.`
      });
    }
    
    // Verify sandbox is ready before applying code
    console.log('[apply-ai-code] Verifying sandbox is ready...');
    
    // For Vercel sandboxes, check if Vite is running
    if (sandbox.constructor?.name === 'VercelProvider' || sandbox.getSandboxInfo?.()?.provider === 'vercel') {
      console.log('[apply-ai-code] Detected Vercel sandbox, checking Vite status...');
      try {
        // Check if Vite process is running
        const checkResult = await sandbox.runCommand('pgrep -f vite');
        if (!checkResult || !checkResult.stdout) {
          console.log('[apply-ai-code] Vite not running, starting it...');
          // Start Vite if not running
          await sandbox.runCommand('sh -c "cd /vercel/sandbox && nohup npm run dev > /tmp/vite.log 2>&1 &"');
          // Wait for Vite to start
          await new Promise(resolve => setTimeout(resolve, 5000));
          console.log('[apply-ai-code] Vite started, proceeding with code application');
        } else {
          console.log('[apply-ai-code] Vite is already running');
        }
      } catch (e) {
        console.log('[apply-ai-code] Could not check Vite status, proceeding anyway:', e);
      }
    }
    
    // Apply to active sandbox
    console.log('[apply-ai-code] Applying code to sandbox...');
    console.log('[apply-ai-code] Is edit mode:', isEdit);
    console.log('[apply-ai-code] Files to write:', parsed.files.map(f => f.path));
    console.log('[apply-ai-code] Existing files:', Array.from(global.existingFiles));
    if (morphEnabled) {
      console.log('[apply-ai-code] Morph Fast Apply enabled');
      if (morphEdits.length > 0) {
        console.log('[apply-ai-code] Parsed Morph edits:', morphEdits.map(e => e.targetFile));
      } else {
        console.log('[apply-ai-code] No <edit> blocks found in response');
      }
    }
    
    const results = {
      filesCreated: [] as string[],
      filesUpdated: [] as string[],
      packagesInstalled: [] as string[],
      packagesAlreadyInstalled: [] as string[],
      packagesFailed: [] as string[],
      commandsExecuted: [] as string[],
      errors: [] as string[]
    };
    
    // Combine packages from tool calls and parsed XML tags
    const allPackages = [...packages.filter((pkg: any) => pkg && typeof pkg === 'string'), ...parsed.packages];
    const uniquePackages = [...new Set(allPackages)]; // Remove duplicates
    
    if (uniquePackages.length > 0) {
      console.log('[apply-ai-code] Installing packages from XML tags and tool calls:', uniquePackages);
      
      try {
        const installResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/install-packages`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ packages: uniquePackages })
        });
        
        if (installResponse.ok) {
          const installResult = await installResponse.json();
          console.log('[apply-ai-code] Package installation result:', installResult);
          
          if (installResult.installed && installResult.installed.length > 0) {
            results.packagesInstalled = installResult.installed;
          }
          if (installResult.failed && installResult.failed.length > 0) {
            results.packagesFailed = installResult.failed;
          }
        }
      } catch (error) {
        console.error('[apply-ai-code] Error installing packages:', error);
      }
    } else {
      // Fallback to detecting packages from code
      console.log('[apply-ai-code] No packages provided, detecting from generated code...');
      console.log('[apply-ai-code] Number of files to scan:', parsed.files.length);
      
      // Filter out config files first
      const configFiles = ['tailwind.config.js', 'vite.config.js', 'package.json', 'package-lock.json', 'tsconfig.json', 'postcss.config.js'];
      const filteredFilesForDetection = parsed.files.filter(file => {
        const fileName = file.path.split('/').pop() || '';
        return !configFiles.includes(fileName);
      });
      
      // Build files object for package detection
      const filesForPackageDetection: Record<string, string> = {};
      for (const file of filteredFilesForDetection) {
        filesForPackageDetection[file.path] = file.content;
        // Log if heroicons is found
        if (file.content.includes('heroicons')) {
          console.log(`[apply-ai-code] Found heroicons import in ${file.path}`);
        }
      }
      
      try {
        console.log('[apply-ai-code] Calling detect-and-install-packages...');
        const packageResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/detect-and-install-packages`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ files: filesForPackageDetection })
        });
        
        console.log('[apply-ai-code] Package detection response status:', packageResponse.status);
        
        if (packageResponse.ok) {
          const packageResult = await packageResponse.json();
          console.log('[apply-ai-code] Package installation result:', JSON.stringify(packageResult, null, 2));
        
        if (packageResult.packagesInstalled && packageResult.packagesInstalled.length > 0) {
          results.packagesInstalled = packageResult.packagesInstalled;
          console.log(`[apply-ai-code] Installed packages: ${packageResult.packagesInstalled.join(', ')}`);
        }
        
        if (packageResult.packagesAlreadyInstalled && packageResult.packagesAlreadyInstalled.length > 0) {
          results.packagesAlreadyInstalled = packageResult.packagesAlreadyInstalled;
          console.log(`[apply-ai-code] Already installed: ${packageResult.packagesAlreadyInstalled.join(', ')}`);
        }
        
        if (packageResult.packagesFailed && packageResult.packagesFailed.length > 0) {
          results.packagesFailed = packageResult.packagesFailed;
          console.error(`[apply-ai-code] Failed to install packages: ${packageResult.packagesFailed.join(', ')}`);
          results.errors.push(`Failed to install packages: ${packageResult.packagesFailed.join(', ')}`);
        }
        
        // Force Vite restart after package installation
        if (results.packagesInstalled.length > 0) {
          console.log('[apply-ai-code] Packages were installed, forcing Vite restart...');
          
          try {
            // Call the restart-vite endpoint
            const restartResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/restart-vite`, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' }
            });
            
            if (restartResponse.ok) {
              const restartResult = await restartResponse.json();
              console.log('[apply-ai-code] Vite restart result:', restartResult.message);
            } else {
              console.error('[apply-ai-code] Failed to restart Vite:', await restartResponse.text());
            }
          } catch (e) {
            console.error('[apply-ai-code] Error calling restart-vite:', e);
          }
          
          // Additional delay to ensure files can be written after restart
          await new Promise(resolve => setTimeout(resolve, 1000));
        }
        } else {
          console.error('[apply-ai-code] Package detection/installation failed:', await packageResponse.text());
        }
      } catch (error) {
        console.error('[apply-ai-code] Error detecting/installing packages:', error);
        // Continue with file writing even if package installation fails
      }
    }
    
    // Attempt Morph Fast Apply for edits before file creation
    const morphUpdatedPaths = new Set<string>();

    if (morphEnabled && morphEdits.length > 0) {
      if (!global.activeSandbox) {
        console.warn('[apply-ai-code] Morph edits found but no active sandbox; skipping Morph application');
      } else {
        console.log(`[apply-ai-code] Applying ${morphEdits.length} fast edits via Morph...`);
        for (const edit of morphEdits) {
          try {
            const result = await applyMorphEditToFile({
              sandbox: global.activeSandbox,
              targetPath: edit.targetFile,
              instructions: edit.instructions,
              updateSnippet: edit.update
            });

            if (result.success && result.normalizedPath) {
              morphUpdatedPaths.add(result.normalizedPath);
              results.filesUpdated.push(result.normalizedPath);
              console.log('[apply-ai-code] Morph applied to', result.normalizedPath);
            } else {
              const msg = result.error || 'Unknown Morph error';
              console.error('[apply-ai-code] Morph apply failed:', msg);
              results.errors.push(`Morph apply failed for ${edit.targetFile}: ${msg}`);
            }
          } catch (e) {
            console.error('[apply-ai-code] Morph apply exception:', e);
            results.errors.push(`Morph apply exception for ${edit.targetFile}: ${(e as Error).message}`);
          }
        }
      }
    }
    if (morphEnabled && morphEdits.length === 0) {
      console.warn('[apply-ai-code] Morph enabled but no <edit> blocks found; falling back to full-file flow');
    }

    // Filter out config files that shouldn't be created
    const configFiles = ['tailwind.config.js', 'vite.config.js', 'package.json', 'package-lock.json', 'tsconfig.json', 'postcss.config.js'];
    let filteredFiles = parsed.files.filter(file => {
      const fileName = file.path.split('/').pop() || '';
      if (configFiles.includes(fileName)) {
        console.warn(`[apply-ai-code] Skipping config file: ${file.path} - already exists in template`);
        return false;
      }
      return true;
    });

    // Avoid overwriting files already updated by Morph
    if (morphUpdatedPaths.size > 0) {
      filteredFiles = filteredFiles.filter(file => {
        let normalizedPath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
        const fileName = normalizedPath.split('/').pop() || '';
        if (!normalizedPath.startsWith('src/') &&
            !normalizedPath.startsWith('public/') &&
            normalizedPath !== 'index.html' &&
            !configFiles.includes(fileName)) {
          normalizedPath = 'src/' + normalizedPath;
        }
        return !morphUpdatedPaths.has(normalizedPath);
      });
    }
    
    // Create or update files AFTER package installation
    for (const file of filteredFiles) {
      try {
        // Normalize the file path
        let normalizedPath = file.path;
        // Remove leading slash if present
        if (normalizedPath.startsWith('/')) {
          normalizedPath = normalizedPath.substring(1);
        }
        // Ensure src/ prefix for component files
        if (!normalizedPath.startsWith('src/') && 
            !normalizedPath.startsWith('public/') && 
            normalizedPath !== 'index.html' && 
            normalizedPath !== 'package.json' &&
            normalizedPath !== 'vite.config.js' &&
            normalizedPath !== 'tailwind.config.js' &&
            normalizedPath !== 'postcss.config.js') {
          normalizedPath = 'src/' + normalizedPath;
        }
        
        const fullPath = `/home/user/app/${normalizedPath}`;
        const isUpdate = global.existingFiles.has(normalizedPath);
        
        // Remove any CSS imports from JSX/JS files (we're using Tailwind)
        let fileContent = file.content;
        if (file.path.endsWith('.jsx') || file.path.endsWith('.js') || file.path.endsWith('.tsx') || file.path.endsWith('.ts')) {
          fileContent = fileContent.replace(/import\s+['"]\.\/[^'"]+\.css['"];?\s*\n?/g, '');
        }
        
        // Fix common Tailwind CSS errors in CSS files
        if (file.path.endsWith('.css')) {
          // Replace shadow-3xl with shadow-2xl (shadow-3xl doesn't exist)
          fileContent = fileContent.replace(/shadow-3xl/g, 'shadow-2xl');
          // Replace any other non-existent shadow utilities
          fileContent = fileContent.replace(/shadow-4xl/g, 'shadow-2xl');
          fileContent = fileContent.replace(/shadow-5xl/g, 'shadow-2xl');
        }
        
        console.log(`[apply-ai-code] Writing file using E2B files API: ${fullPath}`);
        
        try {
          // Check if we're using provider pattern (v2) or direct sandbox (v1)
          if (sandbox.writeFile) {
            // V2: Provider pattern (Vercel/E2B provider)
            await sandbox.writeFile(file.path, fileContent);
          } else if (sandbox.files?.write) {
            // V1: Direct E2B sandbox
            await sandbox.files.write(fullPath, fileContent);
          } else {
            throw new Error('Unsupported sandbox type');
          }
          console.log(`[apply-ai-code] Successfully wrote file: ${fullPath}`);
          
          // Update file cache
          if (global.sandboxState?.fileCache) {
            global.sandboxState.fileCache.files[normalizedPath] = {
              content: fileContent,
              lastModified: Date.now()
            };
            console.log(`[apply-ai-code] Updated file cache for: ${normalizedPath}`);
          }
          
        } catch (writeError) {
          console.error(`[apply-ai-code] E2B file write error:`, writeError);
          throw writeError as Error;
        }
        
        
        if (isUpdate) {
          results.filesUpdated.push(normalizedPath);
        } else {
          results.filesCreated.push(normalizedPath);
          global.existingFiles.add(normalizedPath);
        }
      } catch (error) {
        results.errors.push(`Failed to create ${file.path}: ${(error as Error).message}`);
      }
    }
    
    // Only create App.jsx if it's not an edit and doesn't exist
    const appFileInParsed = parsed.files.some(f => {
      const normalized = f.path.replace(/^\//, '').replace(/^src\//, '');
      return normalized === 'App.jsx' || normalized === 'App.tsx';
    });
    
    const appFileExists = global.existingFiles.has('src/App.jsx') || 
                         global.existingFiles.has('src/App.tsx') ||
                         global.existingFiles.has('App.jsx') ||
                         global.existingFiles.has('App.tsx');
    
    if (!isEdit && !appFileInParsed && !appFileExists && parsed.files.length > 0) {
      // Find all component files
      const componentFiles = parsed.files.filter(f => 
        (f.path.endsWith('.jsx') || f.path.endsWith('.tsx')) &&
        f.path.includes('component')
      );
      
      // Generate imports for components
      const imports = componentFiles
        .filter(f => !f.path.includes('App.') && !f.path.includes('main.') && !f.path.includes('index.'))
        .map(f => {
          const pathParts = f.path.split('/');
          const fileName = pathParts[pathParts.length - 1];
          const componentName = fileName.replace(/\.(jsx|tsx)$/, '');
          // Fix import path - components are in src/components/
          const importPath = f.path.startsWith('src/') 
            ? f.path.replace('src/', './').replace(/\.(jsx|tsx)$/, '')
            : './' + f.path.replace(/\.(jsx|tsx)$/, '');
          return `import ${componentName} from '${importPath}';`;
        })
        .join('\n');
      
      // Find the main component
      const mainComponent = componentFiles.find(f => {
        const name = f.path.toLowerCase();
        return name.includes('header') || 
               name.includes('hero') ||
               name.includes('layout') ||
               name.includes('main') ||
               name.includes('home');
      }) || componentFiles[0];
      
      const mainComponentName = mainComponent 
        ? mainComponent.path.split('/').pop()?.replace(/\.(jsx|tsx)$/, '') 
        : null;
      
      // Create App.jsx with better structure
      const appContent = `import React from 'react';
${imports}

function App() {
  return (
    <div className="min-h-screen bg-gray-900 text-white p-8">
      ${mainComponentName ? `<${mainComponentName} />` : '<div className="text-center">\n        <h1 className="text-4xl font-bold mb-4">Welcome to your React App</h1>\n        <p className="text-gray-400">Your components have been created but need to be added here.</p>\n      </div>'}
      {/* Generated components: ${componentFiles.map(f => f.path).join(', ')} */}
    </div>
  );
}

export default App;`;
      
      try {
        // Use provider pattern if available
        if (sandbox.writeFile) {
          await sandbox.writeFile('src/App.jsx', appContent);
        } else if (sandbox.writeFiles) {
          await sandbox.writeFiles([{
            path: 'src/App.jsx',
            content: Buffer.from(appContent)
          }]);
        }
        
        console.log('Auto-generated: src/App.jsx');
        results.filesCreated.push('src/App.jsx (auto-generated)');
      } catch (error) {
        results.errors.push(`Failed to create App.jsx: ${(error as Error).message}`);
      }
      
      // Don't auto-generate App.css - we're using Tailwind CSS
      
      // Only create index.css if it doesn't exist
      const indexCssInParsed = parsed.files.some(f => {
        const normalized = f.path.replace(/^\//, '').replace(/^src\//, '');
        return normalized === 'index.css' || f.path === 'src/index.css';
      });
      
      const indexCssExists = global.existingFiles.has('src/index.css') || 
                            global.existingFiles.has('index.css');
      
      if (!isEdit && !indexCssInParsed && !indexCssExists) {
        try {
          const indexCssContent = `@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;
  color-scheme: dark;
  
  color: rgba(255, 255, 255, 0.87);
  background-color: #0a0a0a;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-width: 320px;
  min-height: 100vh;
}`;

          // Use provider pattern if available
          if (sandbox.writeFile) {
            await sandbox.writeFile('src/index.css', indexCssContent);
          } else if (sandbox.writeFiles) {
            await sandbox.writeFiles([{
              path: 'src/index.css',
              content: Buffer.from(indexCssContent)
            }]);
          }
          
          console.log('Auto-generated: src/index.css');
          results.filesCreated.push('src/index.css (with Tailwind)');
        } catch (error) {
          console.error('Failed to create index.css:', error);
          results.errors.push('Failed to create index.css with Tailwind');
        }
      }
    }
    
    // Execute commands
    for (const cmd of parsed.commands) {
      try {
        // Parse command and arguments
        const commandParts = cmd.trim().split(/\s+/);
        const cmdName = commandParts[0];
        const args = commandParts.slice(1);
        
        // Execute command using sandbox
        let result;
        if (sandbox.runCommand && typeof sandbox.runCommand === 'function') {
          // Check if this is a provider pattern sandbox
          const testResult = await sandbox.runCommand(cmd);
          if (testResult && typeof testResult === 'object' && 'stdout' in testResult) {
            // Provider returns CommandResult directly
            result = testResult;
          } else {
            // Direct sandbox - expects object with cmd and args
            result = await sandbox.runCommand({
              cmd: cmdName,
              args
            });
          }
        }
        
        console.log(`Executed: ${cmd}`);
        
        // Handle result based on type
        let stdout = '';
        let stderr = '';
        
        if (result) {
          if (typeof result.stdout === 'string') {
            stdout = result.stdout;
            stderr = result.stderr || '';
          } else if (typeof result.stdout === 'function') {
            stdout = await result.stdout();
            stderr = await result.stderr();
          }
        }
        
        if (stdout) console.log(stdout);
        if (stderr) console.log(`Errors: ${stderr}`);
        
        results.commandsExecuted.push(cmd);
      } catch (error) {
        results.errors.push(`Failed to execute ${cmd}: ${(error as Error).message}`);
      }
    }
    
    // Check for missing imports in App.jsx
    const missingImports: string[] = [];
    const appFile = parsed.files.find(f => 
      f.path === 'src/App.jsx' || f.path === 'App.jsx'
    );
    
    if (appFile) {
      // Extract imports from App.jsx
      const importRegex = /import\s+(?:\w+|\{[^}]+\})\s+from\s+['"]([^'"]+)['"]/g;
      let match;
      const imports: string[] = [];
      
      while ((match = importRegex.exec(appFile.content)) !== null) {
        const importPath = match[1];
        if (importPath.startsWith('./') || importPath.startsWith('../')) {
          imports.push(importPath);
        }
      }
      
      // Check if all imported files exist
      for (const imp of imports) {
        // Skip CSS imports for this check
        if (imp.endsWith('.css')) continue;
        
        // Convert import path to expected file paths
        const basePath = imp.replace('./', 'src/');
        const possiblePaths = [
          basePath + '.jsx',
          basePath + '.js',
          basePath + '/index.jsx',
          basePath + '/index.js'
        ];
        
        const fileExists = parsed.files.some(f => 
          possiblePaths.some(path => f.path === path)
        );
        
        if (!fileExists) {
          missingImports.push(imp);
        }
      }
    }
    
    // Prepare response
    const responseData: any = {
      success: true,
      results,
      explanation: parsed.explanation,
      structure: parsed.structure,
      message: `Applied ${results.filesCreated.length} files successfully`
    };
    
    // Handle missing imports automatically
    if (missingImports.length > 0) {
      console.warn('[apply-ai-code] Missing imports detected:', missingImports);
      
      // Automatically generate missing components
      try {
        console.log('[apply-ai-code] Auto-generating missing components...');
        
        const autoCompleteResponse = await fetch(
          `${request.nextUrl.origin}/api/auto-complete-components`,
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              missingImports,
              model: 'claude-sonnet-4-20250514'
            })
          }
        );
        
        const autoCompleteData = await autoCompleteResponse.json();
        
        if (autoCompleteData.success) {
          responseData.autoCompleted = true;
          responseData.autoCompletedComponents = autoCompleteData.components;
          responseData.message = `Applied ${results.filesCreated.length} files + auto-generated ${autoCompleteData.files} missing components`;
          
          // Add auto-completed files to results
          results.filesCreated.push(...autoCompleteData.components);
        } else {
          // If auto-complete fails, still warn the user
          responseData.warning = `Missing ${missingImports.length} imported components: ${missingImports.join(', ')}`;
          responseData.missingImports = missingImports;
        }
      } catch (error) {
        console.error('[apply-ai-code] Auto-complete failed:', error);
        responseData.warning = `Missing ${missingImports.length} imported components: ${missingImports.join(', ')}`;
        responseData.missingImports = missingImports;
      }
    }
    
    // Track applied files in conversation state
    if (global.conversationState && results.filesCreated.length > 0) {
      // Update the last message metadata with edited files
      const messages = global.conversationState.context.messages;
      if (messages.length > 0) {
        const lastMessage = messages[messages.length - 1];
        if (lastMessage.role === 'user') {
          lastMessage.metadata = {
            ...lastMessage.metadata,
            editedFiles: results.filesCreated
          };
        }
      }
      
      // Track applied code in project evolution
      if (global.conversationState.context.projectEvolution) {
        global.conversationState.context.projectEvolution.majorChanges.push({
          timestamp: Date.now(),
          description: parsed.explanation || 'Code applied',
          filesAffected: results.filesCreated
        });
      }
      
      // Update last updated timestamp
      global.conversationState.lastUpdated = Date.now();
      
      console.log('[apply-ai-code] Updated conversation state with applied files:', results.filesCreated);
    }
    
    return NextResponse.json(responseData);
    
  } catch (error) {
    console.error('Apply AI code error:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Failed to parse AI code' },
      { status: 500 }
    );
  }
}

================================================
FILE: app/api/apply-ai-code-stream/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';
import { parseMorphEdits, applyMorphEditToFile } from '@/lib/morph-fast-apply';
// Sandbox import not needed - using global sandbox from sandbox-manager
import type { SandboxState } from '@/types/sandbox';
import type { ConversationState } from '@/types/conversation';
import { sandboxManager } from '@/lib/sandbox/sandbox-manager';

declare global {
  var conversationState: ConversationState | null;
  var activeSandboxProvider: any;
  var existingFiles: Set<string>;
  var sandboxState: SandboxState;
}

interface ParsedResponse {
  explanation: string;
  template: string;
  files: Array<{ path: string; content: string }>;
  packages: string[];
  commands: string[];
  structure: string | null;
}

function parseAIResponse(response: string): ParsedResponse {
  const sections = {
    files: [] as Array<{ path: string; content: string }>,
    commands: [] as string[],
    packages: [] as string[],
    structure: null as string | null,
    explanation: '',
    template: ''
  };

  // Function to extract packages from import statements
  function extractPackagesFromCode(content: string): string[] {
    const packages: string[] = [];
    // Match ES6 imports
    const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)(?:\s*,\s*(?:\{[^}]*\}|\*\s+as\s+\w+|\w+))*\s+from\s+)?['"]([^'"]+)['"]/g;
    let importMatch;

    while ((importMatch = importRegex.exec(content)) !== null) {
      const importPath = importMatch[1];
      // Skip relative imports and built-in React
      if (!importPath.startsWith('.') && !importPath.startsWith('/') &&
        importPath !== 'react' && importPath !== 'react-dom' &&
        !importPath.startsWith('@/')) {
        // Extract package name (handle scoped packages like @heroicons/react)
        const packageName = importPath.startsWith('@')
          ? importPath.split('/').slice(0, 2).join('/')
          : importPath.split('/')[0];

        if (!packages.includes(packageName)) {
          packages.push(packageName);

          // Log important packages for debugging
          if (packageName === 'react-router-dom' || packageName.includes('router') || packageName.includes('icon')) {
            console.log(`[apply-ai-code-stream] Detected package from imports: ${packageName}`);
          }
        }
      }
    }

    return packages;
  }

  // Parse file sections - handle duplicates and prefer complete versions
  const fileMap = new Map<string, { content: string; isComplete: boolean }>();

  // First pass: Find all file declarations
  const fileRegex = /<file path="([^"]+)">([\s\S]*?)(?:<\/file>|$)/g;
  let match;
  while ((match = fileRegex.exec(response)) !== null) {
    const filePath = match[1];
    const content = match[2].trim();
    const hasClosingTag = response.substring(match.index, match.index + match[0].length).includes('</file>');

    // Check if this file already exists in our map
    const existing = fileMap.get(filePath);

    // Decide whether to keep this version
    let shouldReplace = false;
    if (!existing) {
      shouldReplace = true; // First occurrence
    } else if (!existing.isComplete && hasClosingTag) {
      shouldReplace = true; // Replace incomplete with complete
      console.log(`[apply-ai-code-stream] Replacing incomplete ${filePath} with complete version`);
    } else if (existing.isComplete && hasClosingTag && content.length > existing.content.length) {
      shouldReplace = true; // Replace with longer complete version
      console.log(`[apply-ai-code-stream] Replacing ${filePath} with longer complete version`);
    } else if (!existing.isComplete && !hasClosingTag && content.length > existing.content.length) {
      shouldReplace = true; // Both incomplete, keep longer one
    }

    if (shouldReplace) {
      // Additional validation: reject obviously broken content
      if (content.includes('...') && !content.includes('...props') && !content.includes('...rest')) {
        console.warn(`[apply-ai-code-stream] Warning: ${filePath} contains ellipsis, may be truncated`);
        // Still use it if it's the only version we have
        if (!existing) {
          fileMap.set(filePath, { content, isComplete: hasClosingTag });
        }
      } else {
        fileMap.set(filePath, { content, isComplete: hasClosingTag });
      }
    }
  }

  // Convert map to array for sections.files
  for (const [path, { content, isComplete }] of fileMap.entries()) {
    if (!isComplete) {
      console.log(`[apply-ai-code-stream] Warning: File ${path} appears to be truncated (no closing tag)`);
    }

    sections.files.push({
      path,
      content
    });

    // Extract packages from file content
    const filePackages = extractPackagesFromCode(content);
    for (const pkg of filePackages) {
      if (!sections.packages.includes(pkg)) {
        sections.packages.push(pkg);
        console.log(`[apply-ai-code-stream] 📦 Package detected from imports: ${pkg}`);
      }
    }
  }

  // Also parse markdown code blocks with file paths
  const markdownFileRegex = /```(?:file )?path="([^"]+)"\n([\s\S]*?)```/g;
  while ((match = markdownFileRegex.exec(response)) !== null) {
    const filePath = match[1];
    const content = match[2].trim();
    sections.files.push({
      path: filePath,
      content: content
    });

    // Extract packages from file content
    const filePackages = extractPackagesFromCode(content);
    for (const pkg of filePackages) {
      if (!sections.packages.includes(pkg)) {
        sections.packages.push(pkg);
        console.log(`[apply-ai-code-stream] 📦 Package detected from imports: ${pkg}`);
      }
    }
  }

  // Parse plain text format like "Generated Files: Header.jsx, index.css"
  const generatedFilesMatch = response.match(/Generated Files?:\s*([^\n]+)/i);
  if (generatedFilesMatch) {
    // Split by comma first, then trim whitespace, to preserve filenames with dots
    const filesList = generatedFilesMatch[1]
      .split(',')
      .map(f => f.trim())
      .filter(f => f.endsWith('.jsx') || f.endsWith('.js') || f.endsWith('.tsx') || f.endsWith('.ts') || f.endsWith('.css') || f.endsWith('.json') || f.endsWith('.html'));
    console.log(`[apply-ai-code-stream] Detected generated files from plain text: ${filesList.join(', ')}`);

    // Try to extract the actual file content if it follows
    for (const fileName of filesList) {
      // Look for the file content after the file name
      const fileContentRegex = new RegExp(`${fileName}[\\s\\S]*?(?:import[\\s\\S]+?)(?=Generated Files:|Applying code|$)`, 'i');
      const fileContentMatch = response.match(fileContentRegex);
      if (fileContentMatch) {
        // Extract just the code part (starting from import statements)
        const codeMatch = fileContentMatch[0].match(/^(import[\s\S]+)$/m);
        if (codeMatch) {
          const filePath = fileName.includes('/') ? fileName : `src/components/${fileName}`;
          sections.files.push({
            path: filePath,
            content: codeMatch[1].trim()
          });
          console.log(`[apply-ai-code-stream] Extracted content for ${filePath}`);

          // Extract packages from this file
          const filePackages = extractPackagesFromCode(codeMatch[1]);
          for (const pkg of filePackages) {
            if (!sections.packages.includes(pkg)) {
              sections.packages.push(pkg);
              console.log(`[apply-ai-code-stream] Package detected from imports: ${pkg}`);
            }
          }
        }
      }
    }
  }

  // Also try to parse if the response contains raw JSX/JS code blocks
  const codeBlockRegex = /```(?:jsx?|tsx?|javascript|typescript)?\n([\s\S]*?)```/g;
  while ((match = codeBlockRegex.exec(response)) !== null) {
    const content = match[1].trim();
    // Try to detect the file name from comments or context
    const fileNameMatch = content.match(/\/\/\s*(?:File:|Component:)\s*([^\n]+)/);
    if (fileNameMatch) {
      const fileName = fileNameMatch[1].trim();
      const filePath = fileName.includes('/') ? fileName : `src/components/${fileName}`;

      // Don't add duplicate files
      if (!sections.files.some(f => f.path === filePath)) {
        sections.files.push({
          path: filePath,
          content: content
        });

        // Extract packages
        const filePackages = extractPackagesFromCode(content);
        for (const pkg of filePackages) {
          if (!sections.packages.includes(pkg)) {
            sections.packages.push(pkg);
          }
        }
      }
    }
  }

  // Parse commands
  const cmdRegex = /<command>(.*?)<\/command>/g;
  while ((match = cmdRegex.exec(response)) !== null) {
    sections.commands.push(match[1].trim());
  }

  // Parse packages - support both <package> and <packages> tags
  const pkgRegex = /<package>(.*?)<\/package>/g;
  while ((match = pkgRegex.exec(response)) !== null) {
    sections.packages.push(match[1].trim());
  }

  // Also parse <packages> tag with multiple packages
  const packagesRegex = /<packages>([\s\S]*?)<\/packages>/;
  const packagesMatch = response.match(packagesRegex);
  if (packagesMatch) {
    const packagesContent = packagesMatch[1].trim();
    // Split by newlines or commas
    const packagesList = packagesContent.split(/[\n,]+/)
      .map(pkg => pkg.trim())
      .filter(pkg => pkg.length > 0);
    sections.packages.push(...packagesList);
  }

  // Parse structure
  const structureMatch = /<structure>([\s\S]*?)<\/structure>/;
  const structResult = response.match(structureMatch);
  if (structResult) {
    sections.structure = structResult[1].trim();
  }

  // Parse explanation
  const explanationMatch = /<explanation>([\s\S]*?)<\/explanation>/;
  const explResult = response.match(explanationMatch);
  if (explResult) {
    sections.explanation = explResult[1].trim();
  }

  // Parse template
  const templateMatch = /<template>(.*?)<\/template>/;
  const templResult = response.match(templateMatch);
  if (templResult) {
    sections.template = templResult[1].trim();
  }

  return sections;
}

export async function POST(request: NextRequest) {
  try {
    const { response, isEdit = false, packages = [], sandboxId } = await request.json();

    if (!response) {
      return NextResponse.json({
        error: 'response is required'
      }, { status: 400 });
    }

    // Debug log the response
    console.log('[apply-ai-code-stream] Received response to parse:');
    console.log('[apply-ai-code-stream] Response length:', response.length);
    console.log('[apply-ai-code-stream] Response preview:', response.substring(0, 500));
    console.log('[apply-ai-code-stream] isEdit:', isEdit);
    console.log('[apply-ai-code-stream] packages:', packages);

    // Parse the AI response
    const parsed = parseAIResponse(response);
    const morphEnabled = Boolean(isEdit && process.env.MORPH_API_KEY);
    const morphEdits = morphEnabled ? parseMorphEdits(response) : [];
    console.log('[apply-ai-code-stream] Morph Fast Apply mode:', morphEnabled);
    if (morphEnabled) {
      console.log('[apply-ai-code-stream] Morph edits found:', morphEdits.length);
    }
    
    // Log what was parsed
    console.log('[apply-ai-code-stream] Parsed result:');
    console.log('[apply-ai-code-stream] Files found:', parsed.files.length);
    if (parsed.files.length > 0) {
      parsed.files.forEach(f => {
        console.log(`[apply-ai-code-stream] - ${f.path} (${f.content.length} chars)`);
      });
    }
    console.log('[apply-ai-code-stream] Packages found:', parsed.packages);

    // Initialize existingFiles if not already
    if (!global.existingFiles) {
      global.existingFiles = new Set<string>();
    }

    // Try to get provider from sandbox manager first
    let provider = sandboxId ? sandboxManager.getProvider(sandboxId) : sandboxManager.getActiveProvider();

    // Fall back to global state if not found in manager
    if (!provider) {
      provider = global.activeSandboxProvider;
    }

    // If we have a sandboxId but no provider, try to get or create one
    if (!provider && sandboxId) {
      console.log(`[apply-ai-code-stream] No provider found for sandbox ${sandboxId}, attempting to get or create...`);

      try {
        provider = await sandboxManager.getOrCreateProvider(sandboxId);

        // If we got a new provider (not reconnected), we need to create a new sandbox
        if (!provider.getSandboxInfo()) {
          console.log(`[apply-ai-code-stream] Creating new sandbox since reconnection failed for ${sandboxId}`);
          await provider.createSandbox();
          await provider.setupViteApp();
          sandboxManager.registerSandbox(sandboxId, provider);
        }

        // Update legacy global state
        global.activeSandboxProvider = provider;
        console.log(`[apply-ai-code-stream] Successfully got provider for sandbox ${sandboxId}`);
      } catch (providerError) {
        console.error(`[apply-ai-code-stream] Failed to get or create provider for sandbox ${sandboxId}:`, providerError);
        return NextResponse.json({
          success: false,
          error: `Failed to create sandbox provider for ${sandboxId}. The sandbox may have expired.`,
          results: {
            filesCreated: [],
            packagesInstalled: [],
            commandsExecuted: [],
            errors: [`Sandbox provider creation failed: ${(providerError as Error).message}`]
          },
          explanation: parsed.explanation,
          structure: parsed.structure,
          parsedFiles: parsed.files,
          message: `Parsed ${parsed.files.length} files but couldn't apply them - sandbox reconnection failed.`
        }, { status: 500 });
      }
    }

    // If we still don't have a provider, create a new one
    if (!provider) {
      console.log(`[apply-ai-code-stream] No active provider found, creating new sandbox...`);
      try {
        const { SandboxFactory } = await import('@/lib/sandbox/factory');
        provider = SandboxFactory.create();
        const sandboxInfo = await provider.createSandbox();
        await provider.setupViteApp();

        // Register with sandbox manager
        sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);

        // Store in legacy global state
        global.activeSandboxProvider = provider;
        global.sandboxData = {
          sandboxId: sandboxInfo.sandboxId,
          url: sandboxInfo.url
        };

        console.log(`[apply-ai-code-stream] Created new sandbox successfully`);
      } catch (createError) {
        console.error(`[apply-ai-code-stream] Failed to create new sandbox:`, createError);
        return NextResponse.json({
          success: false,
          error: `Failed to create new sandbox: ${createError instanceof Error ? createError.message : 'Unknown error'}`,
          results: {
            filesCreated: [],
            packagesInstalled: [],
            commandsExecuted: [],
            errors: [`Sandbox creation failed: ${createError instanceof Error ? createError.message : 'Unknown error'}`]
          },
          explanation: parsed.explanation,
          structure: parsed.structure,
          parsedFiles: parsed.files,
          message: `Parsed ${parsed.files.length} files but couldn't apply them - sandbox creation failed.`
        }, { status: 500 });
      }
    }

    // Create a response stream for real-time updates
    const encoder = new TextEncoder();
    const stream = new TransformStream();
    const writer = stream.writable.getWriter();

    // Function to send progress updates
    const sendProgress = async (data: any) => {
      const message = `data: ${JSON.stringify(data)}\n\n`;
      await writer.write(encoder.encode(message));
    };

    // Start processing in background (pass provider and request to the async function)
    (async (providerInstance, req) => {
      const results = {
        filesCreated: [] as string[],
        filesUpdated: [] as string[],
        packagesInstalled: [] as string[],
        packagesAlreadyInstalled: [] as string[],
        packagesFailed: [] as string[],
        commandsExecuted: [] as string[],
        errors: [] as string[]
      };

      try {
        await sendProgress({
          type: 'start',
          message: 'Starting code application...',
          totalSteps: 3
        });
        if (morphEnabled) {
          await sendProgress({ type: 'info', message: 'Morph Fast Apply enabled' });
          await sendProgress({ type: 'info', message: `Parsed ${morphEdits.length} Morph edits` });
          if (morphEdits.length === 0) {
            console.warn('[apply-ai-code-stream] Morph enabled but no <edit> blocks found; falling back to full-file flow');
            await sendProgress({ type: 'warning', message: 'Morph enabled but no <edit> blocks found; falling back to full-file flow' });
          }
        }
        
        // Step 1: Install packages
        const packagesArray = Array.isArray(packages) ? packages : [];
        const parsedPackages = Array.isArray(parsed.packages) ? parsed.packages : [];

        // Combine and deduplicate packages
        const allPackages = [...packagesArray.filter(pkg => pkg && typeof pkg === 'string'), ...parsedPackages];

        // Use Set to remove duplicates, then filter out pre-installed packages
        const uniquePackages = [...new Set(allPackages)]
          .filter(pkg => pkg && typeof pkg === 'string' && pkg.trim() !== '') // Remove empty strings
          .filter(pkg => pkg !== 'react' && pkg !== 'react-dom'); // Filter pre-installed

        // Log if we found duplicates
        if (allPackages.length !== uniquePackages.length) {
          console.log(`[apply-ai-code-stream] Removed ${allPackages.length - uniquePackages.length} duplicate packages`);
          console.log(`[apply-ai-code-stream] Original packages:`, allPackages);
          console.log(`[apply-ai-code-stream] Deduplicated packages:`, uniquePackages);
        }

        if (uniquePackages.length > 0) {
          await sendProgress({
            type: 'step',
            step: 1,
            message: `Installing ${uniquePackages.length} packages...`,
            packages: uniquePackages
          });

          // Use streaming package installation
          try {
            // Construct the API URL properly for both dev and production
            const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
            const host = req.headers.get('host') || 'localhost:3000';
            const apiUrl = `${protocol}://${host}/api/install-packages`;

            const installResponse = await fetch(apiUrl, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({
                packages: uniquePackages,
                sandboxId: sandboxId || providerInstance.getSandboxInfo()?.sandboxId
              })
            });

            if (installResponse.ok && installResponse.body) {
              const reader = installResponse.body.getReader();
              const decoder = new TextDecoder();

              while (true) {
                const { done, value } = await reader.read();
                if (done) break;

                const chunk = decoder.decode(value);
                if (!chunk) continue;
                const lines = chunk.split('\n');

                for (const line of lines) {
                  if (line.startsWith('data: ')) {
                    try {
                      const data = JSON.parse(line.slice(6));

                      // Forward package installation progress
                      await sendProgress({
                        type: 'package-progress',
                        ...data
                      });

                      // Track results
                      if (data.type === 'success' && data.installedPackages) {
                        results.packagesInstalled = data.installedPackages;
                      }
                    } catch (parseError) {
                      console.debug('Error parsing terminal output:', parseError);
                    }
                  }
                }
              }
            }
          } catch (error) {
            console.error('[apply-ai-code-stream] Error installing packages:', error);
            await sendProgress({
              type: 'warning',
              message: `Package installation skipped (${(error as Error).message}). Continuing with file creation...`
            });
            results.errors.push(`Package installation failed: ${(error as Error).message}`);
          }
        } else {
          await sendProgress({
            type: 'step',
            step: 1,
            message: 'No additional packages to install, skipping...'
          });
        }

        // Step 2: Create/update files
        const filesArray = Array.isArray(parsed.files) ? parsed.files : [];
        await sendProgress({
          type: 'step',
          step: 2,
          message: `Creating ${filesArray.length} files...`
        });

        // Filter out config files that shouldn't be created
        const configFiles = ['tailwind.config.js', 'vite.config.js', 'package.json', 'package-lock.json', 'tsconfig.json', 'postcss.config.js'];
        let filteredFiles = filesArray.filter(file => {
          if (!file || typeof file !== 'object') return false;
          const fileName = (file.path || '').split('/').pop() || '';
          return !configFiles.includes(fileName);
        });

        // If Morph is enabled and we have edits, apply them before file writes
        const morphUpdatedPaths = new Set<string>();
        if (morphEnabled && morphEdits.length > 0) {
          const morphSandbox = (global as any).activeSandbox || providerInstance;
          if (!morphSandbox) {
            console.warn('[apply-ai-code-stream] No sandbox available to apply Morph edits');
            await sendProgress({ type: 'warning', message: 'No sandbox available to apply Morph edits' });
          } else {
            await sendProgress({ type: 'info', message: `Applying ${morphEdits.length} fast edits via Morph...` });
            for (const [idx, edit] of morphEdits.entries()) {
              try {
                await sendProgress({ type: 'file-progress', current: idx + 1, total: morphEdits.length, fileName: edit.targetFile, action: 'morph-applying' });
                const result = await applyMorphEditToFile({
                  sandbox: morphSandbox,
                  targetPath: edit.targetFile,
                  instructions: edit.instructions,
                  updateSnippet: edit.update
                });
                if (result.success && result.normalizedPath) {
                  console.log('[apply-ai-code-stream] Morph updated', result.normalizedPath);
                  morphUpdatedPaths.add(result.normalizedPath);
                  if (results.filesUpdated) results.filesUpdated.push(result.normalizedPath);
                  await sendProgress({ type: 'file-complete', fileName: result.normalizedPath, action: 'morph-updated' });
                } else {
                  const msg = result.error || 'Unknown Morph error';
                  console.error('[apply-ai-code-stream] Morph apply failed for', edit.targetFile, msg);
                  if (results.errors) results.errors.push(`Morph apply failed for ${edit.targetFile}: ${msg}`);
                  await sendProgress({ type: 'file-error', fileName: edit.targetFile, error: msg });
                }
              } catch (err) {
                const msg = (err as Error).message;
                console.error('[apply-ai-code-stream] Morph apply exception for', edit.targetFile, msg);
                if (results.errors) results.errors.push(`Morph apply exception for ${edit.targetFile}: ${msg}`);
                await sendProgress({ type: 'file-error', fileName: edit.targetFile, error: msg });
              }
            }
          }
        }

        // Avoid overwriting Morph-updated files in the file write loop
        if (morphUpdatedPaths.size > 0) {
          filteredFiles = filteredFiles.filter(file => {
            if (!file?.path) return true;
            let normalizedPath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
            const fileName = normalizedPath.split('/').pop() || '';
            if (!normalizedPath.startsWith('src/') &&
                !normalizedPath.startsWith('public/') &&
                normalizedPath !== 'index.html' &&
                !configFiles.includes(fileName)) {
              normalizedPath = 'src/' + normalizedPath;
            }
            return !morphUpdatedPaths.has(normalizedPath);
          });
        }
        
        for (const [index, file] of filteredFiles.entries()) {
          try {
            // Send progress for each file
            await sendProgress({
              type: 'file-progress',
              current: index + 1,
              total: filteredFiles.length,
              fileName: file.path,
              action: 'creating'
            });

            // Normalize the file path
            let normalizedPath = file.path;
            if (normalizedPath.startsWith('/')) {
              normalizedPath = normalizedPath.substring(1);
            }
            if (!normalizedPath.startsWith('src/') &&
              !normalizedPath.startsWith('public/') &&
              normalizedPath !== 'index.html' &&
              !configFiles.includes(normalizedPath.split('/').pop() || '')) {
              normalizedPath = 'src/' + normalizedPath;
            }

            const isUpdate = global.existingFiles.has(normalizedPath);

            // Remove any CSS imports from JSX/JS files (we're using Tailwind)
            let fileContent = file.content;
            if (file.path.endsWith('.jsx') || file.path.endsWith('.js') || file.path.endsWith('.tsx') || file.path.endsWith('.ts')) {
              fileContent = fileContent.replace(/import\s+['"]\.\/[^'"]+\.css['"];?\s*\n?/g, '');
            }

            // Fix common Tailwind CSS errors in CSS files
            if (file.path.endsWith('.css')) {
              // Replace shadow-3xl with shadow-2xl (shadow-3xl doesn't exist)
              fileContent = fileContent.replace(/shadow-3xl/g, 'shadow-2xl');
              // Replace any other non-existent shadow utilities
              fileContent = fileContent.replace(/shadow-4xl/g, 'shadow-2xl');
              fileContent = fileContent.replace(/shadow-5xl/g, 'shadow-2xl');
            }

            // Create directory if needed
            const dirPath = normalizedPath.includes('/') ? normalizedPath.substring(0, normalizedPath.lastIndexOf('/')) : '';
            if (dirPath) {
              await providerInstance.runCommand(`mkdir -p ${dirPath}`);
            }

            // Write the file using provider
            await providerInstance.writeFile(normalizedPath, fileContent);

            // Update file cache
            if (global.sandboxState?.fileCache) {
              global.sandboxState.fileCache.files[normalizedPath] = {
                content: fileContent,
                lastModified: Date.now()
              };
            }

            if (isUpdate) {
              if (results.filesUpdated) results.filesUpdated.push(normalizedPath);
            } else {
              if (results.filesCreated) results.filesCreated.push(normalizedPath);
              if (global.existingFiles) global.existingFiles.add(normalizedPath);
            }

            await sendProgress({
              type: 'file-complete',
              fileName: normalizedPath,
              action: isUpdate ? 'updated' : 'created'
            });
          } catch (error) {
            if (results.errors) {
              results.errors.push(`Failed to create ${file.path}: ${(error as Error).message}`);
            }
            await sendProgress({
              type: 'file-error',
              fileName: file.path,
              error: (error as Error).message
            });
          }
        }

        // Step 3: Execute commands
        const commandsArray = Array.isArray(parsed.commands) ? parsed.commands : [];
        if (commandsArray.length > 0) {
          await sendProgress({
            type: 'step',
            step: 3,
            message: `Executing ${commandsArray.length} commands...`
          });

          for (const [index, cmd] of commandsArray.entries()) {
            try {
              await sendProgress({
                type: 'command-progress',
                current: index + 1,
                total: parsed.commands.length,
                command: cmd,
                action: 'executing'
              });

              // Use provider runCommand
              const result = await providerInstance.runCommand(cmd);

              // Get command output from provider result
              const stdout = result.stdout;
              const stderr = result.stderr;

              if (stdout) {
                await sendProgress({
                  type: 'command-output',
                  command: cmd,
                  output: stdout,
                  stream: 'stdout'
                });
              }

              if (stderr) {
                await sendProgress({
                  type: 'command-output',
                  command: cmd,
                  output: stderr,
                  stream: 'stderr'
                });
              }

              if (results.commandsExecuted) {
                results.commandsExecuted.push(cmd);
              }

              await sendProgress({
                type: 'command-complete',
                command: cmd,
                exitCode: result.exitCode,
                success: result.exitCode === 0
              });
            } catch (error) {
              if (results.errors) {
                results.errors.push(`Failed to execute ${cmd}: ${(error as Error).message}`);
              }
              await sendProgress({
                type: 'command-error',
                command: cmd,
                error: (error as Error).message
              });
            }
          }
        }

        // Send final results
        await sendProgress({
          type: 'complete',
          results,
          explanation: parsed.explanation,
          structure: parsed.structure,
          message: `Successfully applied ${results.filesCreated.length} files`
        });

        // Track applied files in conversation state
        if (global.conversationState && results.filesCreated.length > 0) {
          const messages = global.conversationState.context.messages;
          if (messages.length > 0) {
            const lastMessage = messages[messages.length - 1];
            if (lastMessage.role === 'user') {
              lastMessage.metadata = {
                ...lastMessage.metadata,
                editedFiles: results.filesCreated
              };
            }
          }

          // Track applied code in project evolution
          if (global.conversationState.context.projectEvolution) {
            global.conversationState.context.projectEvolution.majorChanges.push({
              timestamp: Date.now(),
              description: parsed.explanation || 'Code applied',
              filesAffected: results.filesCreated || []
            });
          }

          global.conversationState.lastUpdated = Date.now();
        }

      } catch (error) {
        await sendProgress({
          type: 'error',
          error: (error as Error).message
        });
      } finally {
        await writer.close();
      }
    })(provider, request);

    // Return the stream
    return new Response(stream.readable, {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
      },
    });

  } catch (error) {
    console.error('Apply AI code stream error:', error);
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Failed to parse AI code' },
      { status: 500 }
    );
  }
}

================================================
FILE: app/api/check-vite-errors/route.ts
================================================
import { NextResponse } from 'next/server';

// Stub endpoint to prevent 404 errors
// This endpoint is being called but the source is unknown
// Returns empty errors array to satisfy any calling code
export async function GET() {
  return NextResponse.json({
    success: true,
    errors: [],
    message: 'No Vite errors detected'
  });
}

================================================
FILE: app/api/clear-vite-errors-cache/route.ts
================================================
import { NextResponse } from 'next/server';

declare global {
  var viteErrorsCache: { errors: any[], timestamp: number } | null;
}

export async function POST() {
  try {
    // Clear the cache
    global.viteErrorsCache = null;
    
    console.log('[clear-vite-errors-cache] Cache cleared');
    
    return NextResponse.json({
      success: true,
      message: 'Vite errors cache cleared'
    });
    
  } catch (error) {
    console.error('[clear-vite-errors-cache] Error:', error);
    return NextResponse.json({ 
      success: false, 
      error: (error as Error).message 
    }, { status: 500 });
  }
}

================================================
FILE: app/api/conversation-state/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';
import type { ConversationState } from '@/types/conversation';

declare global {
  var conversationState: ConversationState | null;
}

// GET: Retrieve current conversation state
export async function GET() {
  try {
    if (!global.conversationState) {
      return NextResponse.json({
        success: true,
        state: null,
        message: 'No active conversation'
      });
    }
    
    return NextResponse.json({
      success: true,
      state: global.conversationState
    });
  } catch (error) {
    console.error('[conversation-state] Error getting state:', error);
    return NextResponse.json({
      success: false,
      error: (error as Error).message
    }, { status: 500 });
  }
}

// POST: Reset or update conversation state
export async function POST(request: NextRequest) {
  try {
    const { action, data } = await request.json();
    
    switch (action) {
      case 'reset':
        global.conversationState = {
          conversationId: `conv-${Date.now()}`,
          startedAt: Date.now(),
          lastUpdated: Date.now(),
          context: {
            messages: [],
            edits: [],
            projectEvolution: { majorChanges: [] },
            userPreferences: {}
          }
        };
        
        console.log('[conversation-state] Reset conversation state');
        
        return NextResponse.json({
          success: true,
          message: 'Conversation state reset',
          state: global.conversationState
        });
        
      case 'clear-old':
        // Clear old conversation data but keep recent context
        if (!global.conversationState) {
          // Initialize conversation state if it doesn't exist
          global.conversationState = {
            conversationId: `conv-${Date.now()}`,
            startedAt: Date.now(),
            lastUpdated: Date.now(),
            context: {
              messages: [],
              edits: [],
              projectEvolution: { majorChanges: [] },
              userPreferences: {}
            }
          };
          
          console.log('[conversation-state] Initialized new conversation state for clear-old');
          
          return NextResponse.json({
            success: true,
            message: 'New conversation state initialized',
            state: global.conversationState
          });
        }
        
        // Keep only recent data
        global.conversationState.context.messages = global.conversationState.context.messages.slice(-5);
        global.conversationState.context.edits = global.conversationState.context.edits.slice(-3);
        global.conversationState.context.projectEvolution.majorChanges = 
          global.conversationState.context.projectEvolution.majorChanges.slice(-2);
        
        console.log('[conversation-state] Cleared old conversation data');
        
        return NextResponse.json({
          success: true,
          message: 'Old conversation data cleared',
          state: global.conversationState
        });
        
      case 'update':
        if (!global.conversationState) {
          return NextResponse.json({
            success: false,
            error: 'No active conversation to update'
          }, { status: 400 });
        }
        
        // Update specific fields if provided
        if (data) {
          if (data.currentTopic) {
            global.conversationState.context.currentTopic = data.currentTopic;
          }
          if (data.userPreferences) {
            global.conversationState.context.userPreferences = {
              ...global.conversationState.context.userPreferences,
              ...data.userPreferences
            };
          }
          
          global.conversationState.lastUpdated = Date.now();
        }
        
        return NextResponse.json({
          success: true,
          message: 'Conversation state updated',
          state: global.conversationState
        });
        
      default:
        return NextResponse.json({
          success: false,
          error: 'Invalid action. Use "reset" or "update"'
        }, { status: 400 });
    }
  } catch (error) {
    console.error('[conversation-state] Error:', error);
    return NextResponse.json({
      success: false,
      error: (error as Error).message
    }, { status: 500 });
  }
}

// DELETE: Clear conversation state
export async function DELETE() {
  try {
    global.conversationState = null;
    
    console.log('[conversation-state] Cleared conversation state');
    
    return NextResponse.json({
      success: true,
      message: 'Conversation state cleared'
    });
  } catch (error) {
    console.error('[conversation-state] Error clearing state:', error);
    return NextResponse.json({
      success: false,
      error: (error as Error).message
    }, { status: 500 });
  }
}

================================================
FILE: app/api/create-ai-sandbox/route.ts
================================================
import { NextResponse } from 'next/server';
import { Sandbox } from '@vercel/sandbox';
import type { SandboxState } from '@/types/sandbox';
import { appConfig } from '@/config/app.config';

// Store active sandbox globally
declare global {
  var activeSandbox: any;
  var sandboxData: any;
  var existingFiles: Set<string>;
  var sandboxState: SandboxState;
  var sandboxCreationInProgress: boolean;
  var sandboxCreationPromise: Promise<any> | null;
}

export async function POST() {
  // Check if sandbox creation is already in progress
  if (global.sandboxCreationInProgress && global.sandboxCreationPromise) {
    console.log('[create-ai-sandbox] Sandbox creation already in progress, waiting for existing creation...');
    try {
      const existingResult = await global.sandboxCreationPromise;
      console.log('[create-ai-sandbox] Returning existing sandbox creation result');
      return NextResponse.json(existingResult);
    } catch (error) {
      console.error('[create-ai-sandbox] Existing sandbox creation failed:', error);
      // Continue with new creation if the existing one failed
    }
  }

  // Check if we already have an active sandbox
  if (global.activeSandbox && global.sandboxData) {
    console.log('[create-ai-sandbox] Returning existing active sandbox');
    return NextResponse.json({
      success: true,
      sandboxId: global.sandboxData.sandboxId,
      url: global.sandboxData.url
    });
  }

  // Set the creation flag
  global.sandboxCreationInProgress = true;
  
  // Create the promise that other requests can await
  global.sandboxCreationPromise = createSandboxInternal();
  
  try {
    const result = await global.sandboxCreationPromise;
    return NextResponse.json(result);
  } catch (error) {
    console.error('[create-ai-sandbox] Sandbox creation failed:', error);
    return NextResponse.json(
      { 
        error: error instanceof Error ? error.message : 'Failed to create sandbox',
        details: error instanceof Error ? error.stack : undefined
      },
      { status: 500 }
    );
  } finally {
    global.sandboxCreationInProgress = false;
    global.sandboxCreationPromise = null;
  }
}

async function createSandboxInternal() {
  let sandbox: any = null;

  try {
    console.log('[create-ai-sandbox] Creating Vercel sandbox...');
    
    // Kill existing sandbox if any
    if (global.activeSandbox) {
      console.log('[create-ai-sandbox] Stopping existing sandbox...');
      try {
        await global.activeSandbox.stop();
      } catch (e) {
        console.error('Failed to stop existing sandbox:', e);
      }
      global.activeSandbox = null;
      global.sandboxData = null;
    }
    
    // Clear existing files tracking
    if (global.existingFiles) {
      global.existingFiles.clear();
    } else {
      global.existingFiles = new Set<string>();
    }

    // Create Vercel sandbox with flexible authentication
    console.log(`[create-ai-sandbox] Creating Vercel sandbox with ${appConfig.vercelSandbox.timeoutMinutes} minute timeout...`);
    
    // Prepare sandbox configuration
    const sandboxConfig: any = {
      timeout: appConfig.vercelSandbox.timeoutMs,
      runtime: appConfig.vercelSandbox.runtime,
      ports: [appConfig.vercelSandbox.devPort]
    };
    
    // Add authentication parameters if using personal access token
    if (process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID) {
      console.log('[create-ai-sandbox] Using personal access token authentication');
      sandboxConfig.teamId = process.env.VERCEL_TEAM_ID;
      sandboxConfig.projectId = process.env.VERCEL_PROJECT_ID;
      sandboxConfig.token = process.env.VERCEL_TOKEN;
    } else if (process.env.VERCEL_OIDC_TOKEN) {
      console.log('[create-ai-sandbox] Using OIDC token authentication');
    } else {
      console.log('[create-ai-sandbox] No authentication found - relying on default Vercel authentication');
    }
    
    sandbox = await Sandbox.create(sandboxConfig);
    
    const sandboxId = sandbox.sandboxId;
    console.log(`[create-ai-sandbox] Sandbox created: ${sandboxId}`);

    // Set up a basic Vite React app
    console.log('[create-ai-sandbox] Setting up Vite React app...');
    
    // First, change to the working directory
    await sandbox.runCommand('pwd');
    // workDir is defined in appConfig - not needed here
    
    // Get the sandbox URL using the correct Vercel Sandbox API
    const sandboxUrl = sandbox.domain(appConfig.vercelSandbox.devPort);
    
    // Extract the hostname from the sandbox URL for Vite config
    const sandboxHostname = new URL(sandboxUrl).hostname;
    console.log(`[create-ai-sandbox] Sandbox hostname: ${sandboxHostname}`);

    // Create the Vite config content with the proper hostname (using string concatenation)
    const viteConfigContent = `import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// Vercel Sandbox compatible Vite configuration
export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',
    port: ${appConfig.vercelSandbox.devPort},
    strictPort: true,
    hmr: true,
    allowedHosts: [
      'localhost',
      '127.0.0.1',
      '` + sandboxHostname + `', // Allow the Vercel Sandbox domain
      '.vercel.run', // Allow all Vercel sandbox domains
      '.vercel-sandbox.dev' // Fallback pattern
    ]
  }
})`;

    // Create the project files (now we have the sandbox hostname)
    const projectFiles = [
      {
        path: 'package.json',
        content: Buffer.from(JSON.stringify({
          "name": "sandbox-app",
          "version": "1.0.0",
          "type": "module",
          "scripts": {
            "dev": "vite --host --port 3000",
            "build": "vite build",
            "preview": "vite preview"
          },
          "dependencies": {
            "react": "^18.2.0",
            "react-dom": "^18.2.0"
          },
          "devDependencies": {
            "@vitejs/plugin-react": "^4.0.0",
            "vite": "^4.3.9",
            "tailwindcss": "^3.3.0",
            "postcss": "^8.4.31",
            "autoprefixer": "^10.4.16"
          }
        }, null, 2))
      },
      {
        path: 'vite.config.js',
        content: Buffer.from(viteConfigContent)
      },
      {
        path: 'tailwind.config.js',
        content: Buffer.from(`/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}`)
      },
      {
        path: 'postcss.config.js',
        content: Buffer.from(`export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}`)
      },
      {
        path: 'index.html',
        content: Buffer.from(`<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Sandbox App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>`)
      },
      {
        path: 'src/main.jsx',
        content: Buffer.from(`import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)`)
      },
      {
        path: 'src/App.jsx',
        content: Buffer.from(`function App() {
  return (
    <div className="min-h-screen bg-gray-900 text-white flex items-center justify-center p-4">
      <div className="text-center max-w-2xl">
        <h1 className="text-4xl font-bold mb-4 bg-gradient-to-r from-blue-500 to-purple-600 bg-clip-text text-transparent">
          Sandbox Ready
        </h1>
        <p className="text-lg text-gray-400">
          Start building your React app with Vite and Tailwind CSS!
        </p>
      </div>
    </div>
  )
}

export default App`)
      },
      {
        path: 'src/index.css',
        content: Buffer.from(`@tailwind base;
@tailwind components;
@tailwind utilities;

/* Force Tailwind to load */
@layer base {
  :root {
    font-synthesis: none;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    -webkit-text-size-adjust: 100%;
  }
  
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
  background-color: rgb(17 24 39);
}`)
      }
    ];

    // Create directory structure first
    await sandbox.runCommand({
      cmd: 'mkdir',
      args: ['-p', 'src']
    });
    
    // Write all files
    await sandbox.writeFiles(projectFiles);
    console.log('[create-ai-sandbox] ✓ Project files created');
    
    // Install dependencies
    console.log('[create-ai-sandbox] Installing dependencies...');
    const installResult = await sandbox.runCommand({
      cmd: 'npm',
      args: ['install', '--loglevel', 'info']
    });
    if (installResult.exitCode === 0) {
      console.log('[create-ai-sandbox] ✓ Dependencies installed successfully');
    } else {
      console.log('[create-ai-sandbox] ⚠ Warning: npm install had issues but continuing...');
    }
    
    // Start Vite dev server in detached mode
    console.log('[create-ai-sandbox] Starting Vite dev server...');
    const viteProcess = await sandbox.runCommand({
      cmd: 'npm',
      args: ['run', 'dev'],
      detached: true
    });
    
    console.log('[create-ai-sandbox] ✓ Vite dev server started');
    
    // Wait for Vite to be fully ready
    await new Promise(resolve => setTimeout(resolve, appConfig.vercelSandbox.devServerStartupDelay));

    // Store sandbox globally
    global.activeSandbox = sandbox;
    global.sandboxData = {
      sandboxId,
      url: sandboxUrl,
      viteProcess
    };
    
    // Initialize sandbox state
    global.sandboxState = {
      fileCache: {
        files: {},
        lastSync: Date.now(),
        sandboxId
      },
      sandbox,
      sandboxData: {
        sandboxId,
        url: sandboxUrl
      }
    };
    
    // Track initial files
    global.existingFiles.add('src/App.jsx');
    global.existingFiles.add('src/main.jsx');
    global.existingFiles.add('src/index.css');
    global.existingFiles.add('index.html');
    global.existingFiles.add('package.json');
    global.existingFiles.add('vite.config.js');
    global.existingFiles.add('tailwind.config.js');
    global.existingFiles.add('postcss.config.js');
    
    console.log('[create-ai-sandbox] Sandbox ready at:', sandboxUrl);
    
    const result = {
      success: true,
      sandboxId,
      url: sandboxUrl,
      message: 'Vercel sandbox created and Vite React app initialized'
    };
    
    // Store the result for reuse
    global.sandboxData = {
      ...global.sandboxData,
      ...result
    };
    
    return result;

  } catch (error) {
    console.error('[create-ai-sandbox] Error:', error);
    
    // Clean up on error
    if (sandbox) {
      try {
        await sandbox.stop();
      } catch (e) {
        console.error('Failed to stop sandbox on error:', e);
      }
    }
    
    // Clear global state on error
    global.activeSandbox = null;
    global.sandboxData = null;
    
    throw error; // Throw to be caught by the outer handler
  }
}

================================================
FILE: app/api/create-ai-sandbox-v2/route.ts
================================================
import { NextResponse } from 'next/server';
import { SandboxFactory } from '@/lib/sandbox/factory';
// SandboxProvider type is used through SandboxFactory
import type { SandboxState } from '@/types/sandbox';
import { sandboxManager } from '@/lib/sandbox/sandbox-manager';

// Store active sandbox globally
declare global {
  var activeSandboxProvider: any;
  var sandboxData: any;
  var existingFiles: Set<string>;
  var sandboxState: SandboxState;
}

export async function POST() {
  try {
    console.log('[create-ai-sandbox-v2] Creating sandbox...');
    
    // Clean up all existing sandboxes
    console.log('[create-ai-sandbox-v2] Cleaning up existing sandboxes...');
    await sandboxManager.terminateAll();
    
    // Also clean up legacy global state
    if (global.activeSandboxProvider) {
      try {
        await global.activeSandboxProvider.terminate();
      } catch (e) {
        console.error('Failed to terminate legacy global sandbox:', e);
      }
      global.activeSandboxProvider = null;
    }
    
    // Clear existing files tracking
    if (global.existingFiles) {
      global.existingFiles.clear();
    } else {
      global.existingFiles = new Set<string>();
    }

    // Create new sandbox using factory
    const provider = SandboxFactory.create();
    const sandboxInfo = await provider.createSandbox();
    
    console.log('[create-ai-sandbox-v2] Setting up Vite React app...');
    await provider.setupViteApp();
    
    // Register with sandbox manager
    sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);
    
    // Also store in legacy global state for backward compatibility
    global.activeSandboxProvider = provider;
    global.sandboxData = {
      sandboxId: sandboxInfo.sandboxId,
      url: sandboxInfo.url
    };
    
    // Initialize sandbox state
    global.sandboxState = {
      fileCache: {
        files: {},
        lastSync: Date.now(),
        sandboxId: sandboxInfo.sandboxId
      },
      sandbox: provider, // Store the provider instead of raw sandbox
      sandboxData: {
        sandboxId: sandboxInfo.sandboxId,
        url: sandboxInfo.url
      }
    };
    
    console.log('[create-ai-sandbox-v2] Sandbox ready at:', sandboxInfo.url);
    
    return NextResponse.json({
      success: true,
      sandboxId: sandboxInfo.sandboxId,
      url: sandboxInfo.url,
      provider: sandboxInfo.provider,
      message: 'Sandbox created and Vite React app initialized'
    });

  } catch (error) {
    console.error('[create-ai-sandbox-v2] Error:', error);
    
    // Clean up on error
    await sandboxManager.terminateAll();
    if (global.activeSandboxProvider) {
      try {
        await global.activeSandboxProvider.terminate();
      } catch (e) {
        console.error('Failed to terminate sandbox on error:', e);
      }
      global.activeSandboxProvider = null;
    }
    
    return NextResponse.json(
      { 
        error: error instanceof Error ? error.message : 'Failed to create sandbox',
        details: error instanceof Error ? error.stack : undefined
      },
      { status: 500 }
    );
  }
}

================================================
FILE: app/api/create-zip/route.ts
================================================
import { NextResponse } from 'next/server';

declare global {
  var activeSandbox: any;
}

export async function POST() {
  try {
    if (!global.activeSandbox) {
      return NextResponse.json({ 
        success: false, 
        error: 'No active sandbox' 
      }, { status: 400 });
    }
    
    console.log('[create-zip] Creating project zip...');
    
    // Create zip file in sandbox using standard commands
    const zipResult = await global.activeSandbox.runCommand({
      cmd: 'bash',
      args: ['-c', `zip -r /tmp/project.zip . -x "node_modules/*" ".git/*" ".next/*" "dist/*" "build/*" "*.log"`]
    });
    
    if (zipResult.exitCode !== 0) {
      const error = await zipResult.stderr();
      throw new Error(`Failed to create zip: ${error}`);
    }
    
    const sizeResult = await global.activeSandbox.runCommand({
      cmd: 'bash',
      args: ['-c', `ls -la /tmp/project.zip | awk '{print $5}'`]
    });
    
    const fileSize = await sizeResult.stdout();
    console.log(`[create-zip] Created project.zip (${fileSize.trim()} bytes)`);
    
    // Read the zip file and convert to base64
    const readResult = await global.activeSandbox.runCommand({
      cmd: 'base64',
      args: ['/tmp/project.zip']
    });
    
    if (readResult.exitCode !== 0) {
      const error = await readResult.stderr();
      throw new Error(`Failed to read zip file: ${error}`);
    }
    
    const base64Content = (await readResult.stdout()).trim();
    
    // Create a data URL for download
    const dataUrl = `data:application/zip;base64,${base64Content}`;
    
    return NextResponse.json({
      success: true,
      dataUrl,
      fileName: 'vercel-sandbox-project.zip',
      message: 'Zip file created successfully'
    });
    
  } catch (error) {
    console.error('[create-zip] Error:', error);
    return NextResponse.json(
      { 
        success: false, 
        error: (error as Error).message 
      }, 
      { status: 500 }
    );
  }
}

================================================
FILE: app/api/detect-and-install-packages/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';

declare global {
  var activeSandbox: any;
}

export async function POST(request: NextRequest) {
  try {
    const { files } = await request.json();
    
    if (!files || typeof files !== 'object') {
      return NextResponse.json({ 
        success: false, 
        error: 'Files object is required' 
      }, { status: 400 });
    }

    if (!global.activeSandbox) {
      return NextResponse.json({
        success: false,
        error: 'No active sandbox'
      }, { status: 404 });
    }

    console.log('[detect-and-install-packages] Processing files:', Object.keys(files));

    // Extract all import statements from the files
    const imports = new Set<string>();
    const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s*,?\s*)*(?:from\s+)?['"]([^'"]+)['"]/g;
    const requireRegex = /require\s*\(['"]([^'"]+)['"]\)/g;

    for (const [filePath, content] of Object.entries(files)) {
      if (typeof content !== 'string') continue;
      
      // Skip non-JS/JSX/TS/TSX files
      if (!filePath.match(/\.(jsx?|tsx?)$/)) continue;

      // Find ES6 imports
      let match;
      while ((match = importRegex.exec(content)) !== null) {
        imports.add(match[1]);
      }

      // Find CommonJS requires
      while ((match = requireRegex.exec(content)) !== null) {
        imports.add(match[1]);
      }
    }

    console.log('[detect-and-install-packages] Found imports:', Array.from(imports));
    
    // Log specific heroicons imports
    const heroiconImports = Array.from(imports).filter(imp => imp.includes('heroicons'));
    if (heroiconImports.length > 0) {
      console.log('[detect-and-install-packages] Heroicon imports:', heroiconImports);
    }

    // Filter out relative imports and built-in modules
    const packages = Array.from(imports).filter(imp => {
      // Skip relative imports
      if (imp.startsWith('.') || imp.startsWith('/')) return false;
      
      // Skip built-in Node modules
      const builtins = ['fs', 'path', 'http', 'https', 'crypto', 'stream', 'util', 'os', 'url', 'querystring', 'child_process'];
      if (builtins.includes(imp)) return false;
      
      return true;
    });

    // Extract just the package names (without subpaths)
    const packageNames = packages.map(pkg => {
      if (pkg.startsWith('@')) {
        // Scoped package: @scope/package or @scope/package/subpath
        const parts = pkg.split('/');
        return parts.slice(0, 2).join('/');
      } else {
        // Regular package: package or package/subpath
        return pkg.split('/')[0];
      }
    });

    // Remove duplicates
    const uniquePackages = [...new Set(packageNames)];

    console.log('[detect-and-install-packages] Packages to install:', uniquePackages);

    if (uniquePackages.length === 0) {
      return NextResponse.json({
        success: true,
        packagesInstalled: [],
        message: 'No new packages to install'
      });
    }

    // Check which packages are already installed
    const installed: string[] = [];
    const missing: string[] = [];
    
    for (const packageName of uniquePackages) {
      try {
        const checkResult = await global.activeSandbox.runCommand({
          cmd: 'test',
          args: ['-d', `node_modules/${packageName}`]
        });
        
        if (checkResult.exitCode === 0) {
          installed.push(packageName);
        } else {
          missing.push(packageName);
        }
      } catch (checkError) {
        // If test command fails, assume package is missing
        console.debug(`Package check failed for ${packageName}:`, checkError);
        missing.push(packageName);
      }
    }

    console.log('[detect-and-install-packages] Package status:', { installed, missing });

    if (missing.length === 0) {
      return NextResponse.json({
        success: true,
        packagesInstalled: [],
        packagesAlreadyInstalled: installed,
        message: 'All packages already installed'
      });
    }

    // Install missing packages
    console.log('[detect-and-install-packages] Installing packages:', missing);
    
    const installResult = await global.activeSandbox.runCommand({
      cmd: 'npm',
      args: ['install', '--save', ...missing]
    });

    const stdout = await installResult.stdout();
    const stderr = await installResult.stderr();
    
    console.log('[detect-and-install-packages] Install stdout:', stdout);
    if (stderr) {
      console.log('[detect-and-install-packages] Install stderr:', stderr);
    }

    // Verify installation
    const finalInstalled: string[] = [];
    const failed: string[] = [];

    for (const packageName of missing) {
      try {
        const verifyResult = await global.activeSandbox.runCommand({
          cmd: 'test',
          args: ['-d', `node_modules/${packageName}`]
        });
        
        if (verifyResult.exitCode === 0) {
          finalInstalled.push(packageName);
          console.log(`✓ Verified installation of ${packageName}`);
        } else {
          failed.push(packageName);
          console.log(`✗ Failed to verify installation of ${packageName}`);
        }
      } catch (error) {
        failed.push(packageName);
        console.log(`✗ Error verifying ${packageName}:`, error);
      }
    }

    if (failed.length > 0) {
      console.error('[detect-and-install-packages] Failed to install:', failed);
    }

    return NextResponse.json({
      success: true,
      packagesInstalled: finalInstalled,
      packagesFailed: failed,
      packagesAlreadyInstalled: installed,
      message: `Installed ${finalInstalled.length} packages`,
      logs: stdout
    });

  } catch (error) {
    console.error('[detect-and-install-packages] Error:', error);
    return NextResponse.json({
      success: false,
      error: (error as Error).message
    }, { status: 500 });
  }
}

================================================
FILE: app/api/extract-brand-styles/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const url = body.url;
    const prompt = body.prompt;

    console.log('[extract-brand-styles] Extracting brand styles for:', url);
    console.log('[extract-brand-styles] User prompt:', prompt);

    // Call Firecrawl API to extract branding information
    const FIRECRAWL_API_KEY = process.env.FIRECRAWL_API_KEY;

    if (!FIRECRAWL_API_KEY) {
      console.error('[extract-brand-styles] No Firecrawl API key found');
      throw new Error('Firecrawl API key not configured');
    }

    console.log('[extract-brand-styles] Calling Firecrawl branding API for:', url);

    const firecrawlResponse = await fetch('https://api.firecrawl.dev/v2/scrape', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${FIRECRAWL_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        url: url,
        formats: ['branding'],
      }),
    });

    if (!firecrawlResponse.ok) {
      const errorText = await firecrawlResponse.text();
      console.error('[extract-brand-styles] Firecrawl API error:', firecrawlResponse.status, errorText);
      throw new Error(`Firecrawl API returned ${firecrawlResponse.status}`);
    }

    const firecrawlData = await firecrawlResponse.json();
    console.log('[extract-brand-styles] Firecrawl response received successfully');

    // Extract branding data from response
    const brandingData = firecrawlData.data?.branding || firecrawlData.branding;

    if (!brandingData) {
      console.error('[extract-brand-styles] No branding data in Firecrawl response');
      console.log('[extract-brand-styles] Response structure:', JSON.stringify(firecrawlData, null, 2));
      throw new Error('No branding data in Firecrawl response');
    }

    console.log('[extract-brand-styles] Successfully extracted branding data');

    // Return the branding data
    return NextResponse.json({
      success: true,
      url,
      styleName: brandingData.name || url,
      guidelines: brandingData,
    });

  } catch (error) {
    console.error('[extract-brand-styles] Error occurred:', error);
    return NextResponse.json(
      {
        success: false,
        error: error instanceof Error ? error.message : 'Failed to extract brand styles'
      },
      { status: 500 }
    );
  }
}


================================================
FILE: app/api/generate-ai-code-stream/route.ts
================================================
import { NextRequest, NextResponse } from 'next/server';
import { createGroq } from '@ai-sdk/groq';
import { createAnthropic } from '@ai-sdk/anthropic';
import { createOpenAI } from '@ai-sdk/openai';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { streamText } from 'ai';
import type { SandboxState } from '@/types/sandbox';
import { selectFilesForEdit, getFileContents, formatFilesForAI } from '@/lib/context-selector';
import { executeSearchPlan, formatSearchResultsForAI, selectTargetFile } from '@/lib/file-search-executor';
import { FileManifest } from '@/types/file-manifest';
import type { ConversationState, ConversationMessage, ConversationEdit } from '@/types/conversation';
import { appConfig } from '@/config/app.config';

// Force dynamic route to enable streaming
export const dynamic = 'force-dynamic';

// Check if we're using Vercel AI Gateway
const isUsingAIGateway = !!process.env.AI_GATEWAY_API_KEY;
const aiGatewayBaseURL = 'https://ai-gateway.vercel.sh/v1';

console.log('[generate-ai-code-stream] AI Gateway config:', {
  isUsingAIGateway,
  hasGroqKey: !!process.env.GROQ_API_KEY,
  hasAIGatewayKey: !!process.env.AI_GATEWAY_API_KEY
});

const groq = createGroq({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GROQ_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
});

const anthropic = createAnthropic({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.ANTHROPIC_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : (process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com/v1'),
});

const googleGenerativeAI = createGoogleGenerativeAI({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.GEMINI_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : undefined,
});

const openai = createOpenAI({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? process.env.OPENAI_API_KEY,
  baseURL: isUsingAIGateway ? aiGatewayBaseURL : process.env.OPENAI_BASE_URL,
});

// Helper function to analyze user preferences from conversation history
function analyzeUserPreferences(messages: ConversationMessage[]): {
  commonPatterns: string[];
  preferredEditStyle: 'targeted' | 'comprehensive';
} {
  const userMessages = messages.filter(m => m.role === 'user');
  const patterns: string[] = [];
  
  // Count edit-related keywords
  let targetedEditCount = 0;
  let comprehensiveEditCount = 0;
  
  userMessages.forEach(msg => {
    const content = msg.content.toLowerCase();
    
    // Check for targeted edit patterns
    if (content.match(/\b(update|change|fix|modify|edit|remove|delete)\s+(\w+\s+)?(\w+)\b/)) {
      targetedEditCount++;
    }
    
    // Check for comprehensive edit patterns
    if (content.match(/\b(rebuild|recreate|redesign|overhaul|refactor)\b/)) {
      comprehensiveEditCount++;
    }
    
    // Extract common request patterns
    if (content.includes('hero')) patterns.push('hero section edits');
    if (content.includes('header')) patterns.push('header modifications');
    if (content.includes('color') || content.includes('style')) patterns.push('styling changes');
    if (content.includes('button')) patterns.push('button updates');
    if (content.includes('animation')) patterns.push('animation requests');
  });
  
  return {
    commonPatterns: [...new Set(patterns)].slice(0, 3), // Top 3 unique patterns
    preferredEditStyle: targetedEditCount > comprehensiveEditCount ? 'targeted' : 'comprehensive'
  };
}

declare global {
  var sandboxState: SandboxState;
  var conversationState: ConversationState | null;
}

export async function POST(request: NextRequest) {
  try {
    const { prompt, model = 'openai/gpt-oss-20b', context, isEdit = false } = await request.json();
    
    console.log('[generate-ai-code-stream] Received request:');
    console.log('[generate-ai-code-stream] - prompt:', prompt);
    console.log('[generate-ai-code-stream] - isEdit:', isEdit);
    console.log('[generate-ai-code-stream] - context.sandboxId:', context?.sandboxId);
    console.log('[generate-ai-code-stream] - context.currentFiles:', context?.currentFiles ? Object.keys(context.currentFiles) : 'none');
    console.log('[generate-ai-code-stream] - currentFiles count:', context?.currentFiles ? Object.keys(context.currentFiles).length : 0);
    
    // Initialize conversation state if not exists
    if (!global.conversationState) {
      global.conversationState = {
        conversationId: `conv-${Date.now()}`,
        startedAt: Date.now(),
        lastUpdated: Date.now(),
        context: {
          messages: [],
          edits: [],
          projectEvolution: { majorChanges: [] },
          userPreferences: {}
        }
      };
    }
    
    // Add user message to conversation history
    const userMessage: ConversationMessage = {
      id: `msg-${Date.now()}`,
      role: 'user',
      content: prompt,
      timestamp: Date.now(),
      metadata: {
        sandboxId: context?.sandboxId
      }
    };
    global.conversationState.context.messages.push(userMessage);
    
    // Clean up old messages to prevent unbounded growth
    if (global.conversationState.context.messages.length > 20) {
      // Keep only the last 15 messages
      global.conversationState.context.messages = global.conversationState.context.messages.slice(-15);
      console.log('[generate-ai-code-stream] Trimmed conversation history to prevent context overflow');
    }
    
    // Clean up old edits
    if (global.conversationState.context.edits.length > 10) {
      global.conversationState.context.edits = global.conversationState.context.edits.slice(-8);
    }
    
    // Debug: Show a sample of actual file content
    if (context?.currentFiles && Object.keys(context.currentFiles).length > 0) {
      const firstFile = Object.entries(context.currentFiles)[0];
      console.log('[generate-ai-code-stream] - sample file:', firstFile[0]);
      console.log('[generate-ai-code-stream] - sample content preview:', 
        typeof firstFile[1] === 'string' ? firstFile[1].substring(0, 100) + '...' : 'not a string');
    }
    
    if (!prompt) {
      return NextResponse.json({ 
        success: false, 
        error: 'Prompt is required' 
      }, { status: 400 });
    }
    
    // Create a stream for real-time updates
    const encoder = new TextEncoder();
    const stream = new TransformStream();
    const writer = stream.writable.getWriter();
    
    // Function to send progress updates with flushing
    const sendProgress = async (data: any) => {
      const message = `data: ${JSON.stringify(data)}\n\n`;
      try {
        await writer.write(encoder.encode(message));
        // Force flush by writing a keep-alive comment
        if (data.type === 'stream' || data.type === 'conversation') {
          await writer.write(encoder.encode(': keepalive\n\n'));
        }
      } catch (error) {
        console.error('[generate-ai-code-stream] Error writing to stream:', error);
      }
    };
    
    // Start processing in background
    (async () => {
      try {
        // Send initial status
        await sendProgress({ type: 'status', message: 'Initializing AI...' });
        
        // No keep-alive needed - sandbox provisioned for 10 minutes
        
        // Check if we have a file manifest for edit mode
        let editContext = null;
        let enhancedSystemPrompt = '';
        
        if (isEdit) {
          console.log('[generate-ai-code-stream] Edit mode detected - starting agentic search workflow');
          console.log('[generate-ai-code-stream] Has fileCache:', !!global.sandboxState?.fileCache);
          console.log('[generate-ai-code-stream] Has manifest:', !!global.sandboxState?.fileCache?.manifest);
          
          const manifest: FileManifest | undefined = global.sandboxState?.fileCache?.manifest;
          
          if (manifest) {
            await sendProgress({ type: 'status', message: '🔍 Creating search plan...' });
            
            const fileContents = global.sandboxState.fileCache?.files || {};
            console.log('[generate-ai-code-stream] Files available for search:', Object.keys(fileContents).length);
            
            // STEP 1: Get search plan from AI
            try {
              const intentResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/analyze-edit-intent`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ prompt, manifest, model })
              });
              
              if (intentResponse.ok) {
                const { searchPlan } = await intentResponse.json();
                console.log('[generate-ai-code-stream] Search plan received:', searchPlan);
                
                await sendProgress({ 
                  type: 'status', 
                  message: `🔎 Searching for: "${searchPlan.searchTerms.join('", "')}"`
                });
                
                // STEP 2: Execute the search plan
                const searchExecution = executeSearchPlan(searchPlan, 
                  Object.fromEntries(
                    Object.entries(fileContents).map(([path, data]) => [
                      path.startsWith('/') ? path : `/home/user/app/${path}`,
                      data.content
                    ])
                  )
                );
                
                console.log('[generate-ai-code-stream] Search execution:', {
                  success: searchExecution.success,
                  resultsCount: searchExecution.results.length,
                  filesSearched: searchExecution.filesSearched,
                  time: searchExecution.executionTime + 'ms'
                });
                
                if (searchExecution.success && searchExecution.results.length > 0) {
                  // STEP 3: Select the best target file
                  const target = selectTargetFile(searchExecution.results, searchPlan.editType);
                  
                  if (target) {
                    await sendProgress({ 
                      type: 'status', 
                      message: `✅ Found code in ${target.filePath.split('/').pop()} at line ${target.lineNumber}`
                    });
                    
                    console.log('[generate-ai-code-stream] Target selected:', target);
                    
                    // Create surgical edit context with exact location
                    // normalizedPath would be: target.filePath.replace('/home/user/app/', '');
                    // fileContent available but not used in current implementation
                    // const fileContent = fileContents[normalizedPath]?.content || '';
                    
                    // Build enhanced context with search results
                    enhancedSystemPrompt = `
${formatSearchResultsForAI(searchExecution.results)}

SURGICAL EDIT INSTRUCTIONS:
You have been given the EXACT location of the code to edit.
- File: ${target.filePath}
- Line: ${target.lineNumber}
- Reason: ${target.reason}

Make ONLY the change requested by the user. Do not modify any other code.
User request: "${prompt}"`;
                    
                    // Set up edit context with just this one file
                    editContext = {
                      primaryFiles: [target.filePath],
                      contextFiles: [],
                      systemPrompt: enhancedSystemPrompt,
                      editIntent: {
                        type: searchPlan.editType,
                        description: searchPlan.reasoning,
                        targetFiles: [target.filePath],
                        confidence: 0.95, // High confidence since we found exact location
                        searchTerms: searchPlan.searchTerms
                      }
                    };
                    
                    console.log('[generate-ai-code-stream] Surgical edit context created');
                  }
                } else {
                  // Search failed - fall back to old behavior but inform user
                  console.warn('[generate-ai-code-stream] Search found no results, falling back to broader context');
                  await sendProgress({ 
                    type: 'status', 
                    message: '⚠️ Could not find exact match, using broader search...'
                  });
                }
              } else {
                console.error('[generate-ai-code-stream] Failed to get search plan');
              }
            } catch (error) {
              console.error('[generate-ai-code-stream] Error in agentic search workflow:', error);
              await sendProgress({ 
                type: 'status', 
                message: '⚠️ Search workflow error, falling back to keyword method...'
              });
              // Fall back to old method on any error if we have a manifest
              if (manifest) {
                editContext = selectFilesForEdit(prompt, manifest);
              }
            }
          } else {
            // Fall back to old method if AI analysis fails
            console.warn('[generate-ai-code-stream] AI intent analysis failed, falling back to keyword method');
            if (manifest) {
              editContext = selectFilesForEdit(prompt, manifest);
            } else {
              console.log('[generate-ai-code-stream] No manifest available for fallback');
              await sendProgress({ 
                type: 'status', 
                message: '⚠️ No file manifest available, will use broad context'
              });
            }
          }
          
          // If we got an edit context from any method, use its system prompt
          if (editContext) {
            enhancedSystemPrompt = editContext.systemPrompt;
            
            await sendProgress({ 
              type: 'status', 
              message: `Identified edit type: ${editContext.editIntent?.description || 'Code modification'}`
            });
          } else if (!manifest) {
            console.log('[generate-ai-code-stream] WARNING: No manifest available for edit mode!');
            
            // Try to fetch files from sandbox if we have one
            if (global.activeSandbox) {
              await sendProgress({ type: 'status', message: 'Fetching current files from sandbox...' });
              
              try {
                // Fetch files directly from sandbox
                const filesResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/get-sandbox-files`, {
                  method: 'GET',
                  headers: { 'Content-Type': 'application/json' }
                });
                
                if (filesResponse.ok) {
                  const filesData = await filesResponse.json();
                  
                  if (filesData.success && filesData.manifest) {
                    console.log('[generate-ai-code-stream] Successfully fetched manifest from sandbox');
                    const manifest = filesData.manifest;
                    
                    // Now try to analyze edit intent with the fetched manifest
                    try {
                      const intentResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/analyze-edit-intent`, {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ prompt, manifest, model })
                      });
                      
                      if (intentResponse.ok) {
                        const { searchPlan } = await intentResponse.json();
                        console.log('[generate-ai-code-stream] Search plan received (after fetch):', searchPlan);
                        
                        // For now, fall back to keyword search since we don't have file contents for search execution
                        // This path happens when no manifest was initially available
                        let targetFiles: any[] = [];
                        if (!searchPlan || searchPlan.searchTerms.length === 0) {
                          console.warn('[generate-ai-code-stream] No target files after fetch, searching for relevant files');
                          
                          const promptLower = prompt.toLowerCase();
                          const allFilePaths = Object.keys(manifest.files);
                          
                          // Look for component names mentioned in the prompt
                          if (promptLower.includes('hero')) {
                            targetFiles = allFilePaths.filter(p => p.toLowerCase().includes('hero'));
                          } else if (promptLower.includes('header')) {
                            targetFiles = allFilePaths.filter(p => p.toLowerCase().includes('header'));
                          } else if (promptLower.includes('footer')) {
                            targetFiles = allFilePaths.filter(p => p.toLowerCase().includes('footer'));
                          } else if (promptLower.includes('nav')) {
                            targetFiles = allFilePaths.filter(p => p.toLowerCase().includes('nav'));
                          } else if (promptLower.includes('button')) {
                            targetFiles = allFilePaths.filter(p => p.toLowerCase().includes('button'));
                          }
                          
                          if (targetFiles.length > 0) {
                            console.log('[generate-ai-code-stream] Found target files by keyword search after fetch:', targetFiles);
                          }
                        }
                        
                        const allFiles = Object.keys(manifest.files)
                          .filter(path => !targetFiles.includes(path));
                        
                        editContext = {
                          primaryFiles: targetFiles,
                          contextFiles: allFiles,
                          systemPrompt: `
You are an expert senior software engineer performing a surgical, context-aware code modification. Your primary directive is **precision and preservation**.

Think of yourself as a surgeon making a precise incision, not a construction worker demolishing a wall.

## Search-Based Edit
Search Terms: ${searchPlan?.searchTerms?.join(', ') || 'keyword-based'}
Edit Type: ${searchPlan?.editType || 'UPDATE_COMPONENT'}
Reasoning: ${searchPlan?.reasoning || 'Modifying based on user request'}

Files to Edit: ${targetFiles.join(', ') || 'To be determined'}
User Request: "${prompt}"

## Your Mandatory Thought Process (Execute Internally):
Before writing ANY code, you MUST follow these steps:

1. **Understand Intent:**
   - What is the user's core goal? (adding feature, fixing bug, changing style?)
   - Does the conversation history provide extra clues?

2. **Locate the Code:**
   - First examine the Primary Files provided
   - Check the "ALL PROJECT FILES" list to find the EXACT file name
   - "nav" might be Navigation.tsx, NavBar.tsx, Nav.tsx, or Header.tsx
   - DO NOT create a new file if a similar one exists!

3. **Plan the Changes (Mental Diff):**
   - What is the *minimal* set of changes required?
   - Which exact lines need to be added, modified, or deleted?
   - Will this require new packages?

4. **Verify Preservation:**
   - What existing code, props, state, and logic must NOT be touched?
   - How can I make my change without disrupting surrounding code?

5. **Construct the Final Code:**
   - Only after completing steps above, generate the final code
   - Provide the ENTIRE file content with modifications integrated

## Critical Rules & Constraints:

**PRESERVATION IS KEY:** You MUST NOT rewrite entire components or files. Integrate your changes into the existing code. Preserve all existing logic, props, state, and comments not directly related to the user's request.

**MINIMALISM:** Only output files you have actually changed. If a file doesn't need modification, don't include it.

**COMPLETENESS:** Each file must be COMPLETE from first line to last:
- NEVER TRUNCATE - Include EVERY line
- NO ellipsis (...) to skip content
- ALL imports, functions, JSX, and closing tags must be present
- The file MUST be runnable

**SURGICAL PRECISION:**
- Change ONLY what's explicitly requested
- If user says "change background to green", change ONLY the background class
- 99% of the original code should remain untouched
- NO refactoring, reformatting, or "improvements" unless requested

**NO CONVERSATION:** Your output must contain ONLY the code. No explanations or apologies.

## EXAMPLES:

### CORRECT APPROACH for "change hero background to blue":
<thinking>
I need to change the background color of the Hero component. Looking at the file, I see the main div has 'bg-gray-900'. I will change ONLY this to 'bg-blue-500' and leave everything else exactly as is.
</thinking>

Then return the EXACT same file with only 'bg-gray-900' changed to 'bg-blue-500'.

### WRONG APPROACH (DO NOT DO THIS):
- Rewriting the Hero component from scratch
- Changing the structure or reorganizing imports
- Adding or removing unrelated code
- Reformatting or "cleaning up" the code

Remember: You are a SURGEON making a precise incision, not an artist repainting the canvas!`,
                          editIntent: {
                            type: searchPlan?.editType || 'UPDATE_COMPONENT',
                            targetFiles: targetFiles,
                            confidence: searchPlan ? 0.85 : 0.6,
                            description: searchPlan?.reasoning || 'Keyword-based file selection',
                            suggestedContext: []
                          }
                        };
                        
                        enhancedSystemPrompt = editContext.systemPrompt;
                        
                        await sendProgress({ 
                          type: 'status', 
                          message: `Identified edit type: ${editContext.editIntent.description}`
                        });
                      }
                    } catch (error) {
                      console.error('[generate-ai-code-stream] Error analyzing intent after fetch:', error);
                    }
                  } else {
                    console.error('[generate-ai-code-stream] Failed to get manifest from sandbox files');
                  }
                } else {
                  console.error('[generate-ai-code-stream] Failed to fetch sandbox files:', filesResponse.status);
                }
              } catch (error) {
                console.error('[generate-ai-code-stream] Error fetching sandbox files:', error);
                await sendProgress({ 
                  type: 'warning', 
                  message: 'Could not analyze existing files for targeted edits. Proceeding with general edit mode.'
                });
              }
            } else {
              console.log('[generate-ai-code-stream] No active sandbox to fetch files from');
              await sendProgress({ 
                type: 'warning', 
                message: 'No existing files found. Consider generating initial code first.'
              });
            }
          }
        }
        
        // Build conversation context for system prompt
        let conversationContext = '';
        if (global.conversationState && global.conversationState.context.messages.length > 1) {
          console.log('[generate-ai-code-stream] Building conversation context');
          console.log('[generate-ai-code-stream] Total messages:', global.conversationState.context.messages.length);
          console.log('[generate-ai-code-stream] Total edits:', global.conversationState.context.edits.length);
          
          conversationContext = `\n\n## Conversation History (Recent)\n`;
          
          // Include only the last 3 edits to save context
          const recentEdits = global.conversationState.context.edits.slice(-3);
          if (recentEdits.length > 0) {
            console.log('[generate-ai-code-stream] Including', recentEdits.length, 'recent edits in context');
            conversationContext += `\n### Recent Edits:\n`;
            recentEdits.forEach(edit => {
              conversationContext += `- "${edit.userRequest}" → ${edit.editType} (${edit.targetFiles.map(f => f.split('/').pop()).join(', ')})\n`;
            });
          }
          
          // Include recently created files - CRITICAL for preventing duplicates
          const recentMsgs = global.conversationState.context.messages.slice(-5);
          const recentlyCreatedFiles: string[] = [];
          recentMsgs.forEach(msg => {
            if (msg.metadata?.editedFiles) {
              recentlyCreatedFiles.push(...msg.metadata.editedFiles);
            }
          });
          
          if (recentlyCreatedFiles.length > 0) {
            const uniqueFiles = [...new Set(recentlyCreatedFiles)];
            conversationContext += `\n### 🚨 RECENTLY CREATED/EDITED FILES (DO NOT RECREATE THESE):\n`;
            uniqueFiles.forEach(file => {
              conversationContext += `- ${file}\n`;
            });
            conversationContext += `\nIf the user mentions any of these components, UPDATE the existing file!\n`;
          }
          
          // Include only last 5 messages for context (reduced from 10)
          const recentMessages = recentMsgs;
          if (recentMessages.length > 2) { // More than just current message
            conversationContext += `\n### Recent Messages:\n`;
            recentMessages.slice(0, -1).forEach(msg => { // Exclude current message
              if (msg.role === 'user') {
                const truncatedContent = msg.content.length > 100 ? msg.content.substring(0, 100) + '...' : msg.content;
                conversationContext += `- "${truncatedContent}"\n`;
              }
            });
          }
          
          // Include only last 2 major changes
          const majorChanges = global.conversationState.context.projectEvolution.majorChanges.slice(-2);
          if (majorChanges.length > 0) {
            conversationContext += `\n### Recent Changes:\n`;
            majorChanges.forEach(change => {
              conversationContext += `- ${change.description}\n`;
            });
          }
          
          // Keep user preferences - they're concise
          const userPrefs = analyzeUserPreferences(global.conversationState.context.messages);
          if (userPrefs.commonPatterns.length > 0) {
            conversationContext += `\n### User Preferences:\n`;
            conversationContext += `- Edit style: ${userPrefs.preferredEditStyle}\n`;
          }
          
          // Limit total conversation context length
          if (conversationContext.length > 2000) {
            conversationContext = conversationContext.substring(0, 2000) + '\n[Context truncated to prevent length errors]';
          }
        }
        
        // Build system prompt with conversation awareness
        let systemPrompt = `You are an expert React developer with perfect memory of the conversation. You maintain context across messages and remember scraped websites, generated components, and applied code. Generate clean, modern React code for Vite applications.
${conversationContext}

🚨 CRITICAL RULES - YOUR MOST IMPORTANT INSTRUCTIONS:
1. **DO EXACTLY WHAT IS ASKED - NOTHING MORE, NOTHING LESS**
   - Don't add features not requested
   - Don't fix unrelated issues
   - Don't improve things not mentioned
2. **CHECK App.jsx FIRST** - ALWAYS see what components exist before creating new ones
3. **NAVIGATION LIVES IN Header.jsx** - Don't create Nav.jsx if Header exists with nav
4. **USE STANDARD TAILWIND CLASSES ONLY**:
   - ✅ CORRECT: bg-white, text-black, bg-blue-500, bg-gray-100, text-gray-900
   - ❌ WRONG: bg-background, text-foreground, bg-primary, bg-muted, text-secondary
   - Use ONLY classes from the official Tailwind CSS documentation
5. **FILE COUNT LIMITS**:
   - Simple style/text change = 1 file ONLY
   - New component = 2 files MAX (component + parent)
   - If >3 files, YOU'RE DOING TOO MUCH
6. **DO NOT CREATE SVGs FROM SCRATCH**:
   - NEVER generate custom SVG code unless explicitly asked
   - Use existing icon libraries (lucide-react, heroicons, etc.)
   - Or use placeholder elements/text if icons are not critical
   - Only create custom SVGs when user specifically requests "create an SVG" or "draw an SVG"

COMPONENT RELATIONSHIPS (CHECK THESE FIRST):
- Navigation usually lives INSIDE Header.jsx, not separate Nav.jsx
- Logo is typically in Header, not standalone
- Footer often contains nav links already
- Menu/Hamburger is part of Header, not separate

PACKAGE USAGE RULES:
- DO NOT use react-router-dom unless user explicitly asks for routing
- For simple nav links in a single-page app, use scroll-to-section or href="#"
- Only add routing if building a multi-page application
- Common packages are auto-installed from your imports

WEBSITE CLONING REQUIREMENTS:
When recreating/cloning a website, you MUST include:
1. **Header with Navigation** - Usually Header.jsx containing nav
2. **Hero Section** - The main landing area (Hero.jsx)
3. **Main Content Sections** - Features, Services, About, etc.
4. **Footer** - Contact info, links, copyright (Footer.jsx)
5. **App.jsx** - Main app component that imports and uses all components

${isEdit ? `CRITICAL: THIS IS AN EDIT TO AN EXISTING APPLICATION

YOU MUST FOLLOW THESE EDIT RULES:
0. NEVER create tailwind.config.js, vite.config.js, package.json, or any other config files - they already exist!
1. DO NOT regenerate the entire application
2. DO NOT create files that already exist (like App.jsx, index.css, tailwind.config.js)
3. ONLY edit the EXACT files needed for the requested change - NO MORE, NO LESS
4. If the user says "update the header", ONLY edit the Header component - DO NOT touch Footer, Hero, or any other components
5. If the user says "change the color", ONLY edit the relevant style or component file - DO NOT "improve" other parts
6. If you're unsure which file to edit, choose the SINGLE most specific one related to the request
7. IMPORTANT: When adding new components or libraries:
   - Create the new component file
   - UPDATE ONLY the parent component that will use it
   - Example: Adding a Newsletter component means:
     * Create Newsletter.jsx
     * Update ONLY the file that will use it (e.g., Footer.jsx OR App.jsx) - NOT both
8. When adding npm packages:
   - Import them ONLY in the files where they're actually used
   - The system will auto-install missing packages

CRITICAL FILE MODIFICATION RULES - VIOLATION = FAILURE:
- **NEVER TRUNCATE FILES** - Always return COMPLETE files with ALL content
- **NO ELLIPSIS (...)** - Include every single line of code, no skipping
- Files MUST be complete and runnable - include ALL imports, functions, JSX, and closing tags
- Count the files you're about to generate
- If the user asked to change ONE thing, you should generate ONE file (or at most two if adding a new component)
- DO NOT "fix" or "improve" files that weren't mentioned in the request
- DO NOT update multiple components when only one was requested
- DO NOT add features the user didn't ask for
- RESIST the urge to be "helpful" by updating related files

CRITICAL: DO NOT REDESIGN OR REIMAGINE COMPONENTS
- "update" means make a small change, NOT redesign the entire component
- "change X to Y" means ONLY change X to Y, nothing else
- "fix" means repair what's broken, NOT rewrite everything
- "remove X" means delete X from the existing file, NOT create a new file
- "delete X" means remove X from where it currently exists
- Preserve ALL existing functionality and design unless explicitly asked to change it

NEVER CREATE NEW FILES WHEN THE USER ASKS TO REMOVE/DELETE SOMETHING
If the user says "remove X", you must:
1. Find which existing file contains X
2. Edit that file to remove X
3. DO NOT create any new files

${editContext ? `
TARGETED EDIT MODE ACTIVE
- Edit Type: ${editContext.editIntent.type}
- Confidence: ${editContext.editIntent.confidence}
- Files to Edit: ${editContext.primaryFiles.join(', ')}

🚨 CRITICAL RULE - VIOLATION WILL RESULT IN FAILURE 🚨
YOU MUST ***ONLY*** GENERATE THE FILES LISTED ABOVE!

ABSOLUTE REQUIREMENTS:
1. COUNT the files in "Files to Edit" - that's EXACTLY how many files you must generate
2. If "Files to Edit" shows ONE file, generate ONLY that ONE file
3. DO NOT generate App.jsx unless it's EXPLICITLY listed in "Files to Edit"
4. DO NOT generate ANY components that aren't listed in "Files to Edit"
5. DO NOT "helpfully" update related files
6. DO NOT fix unrelated issues you notice
7. DO NOT improve code quality in files not being edited
8. DO NOT add bonus features

EXAMPLE VIOLATIONS (THESE ARE FAILURES):
❌ User says "update the hero" → You update Hero, Header, Footer, and App.jsx
❌ User says "change header color" → You redesign the entire header
❌ User says "fix the button" → You update multiple components
❌ Files to Edit shows "Hero.jsx" → You also generate App.jsx "to integrate it"
❌ Files to Edit shows "Header.jsx" → You also update Footer.jsx "for consistency"

CORRECT BEHAVIOR (THIS IS SUCCESS):
✅ User says "update the hero" → You ONLY edit Hero.jsx with the requested change
✅ User says "change header color" → You ONLY change the color in Header.jsx
✅ User says "fix the button" → You ONLY fix the specific button issue
✅ Files to Edit shows "Hero.jsx" → You generate ONLY Hero.jsx
✅ Files to Edit shows "Header.jsx, Nav.jsx" → You generate EXACTLY 2 files: Header.jsx and Nav.jsx

THE AI INTENT ANALYZER HAS ALREADY DETERMINED THE FILES.
DO NOT SECOND-GUESS IT.
DO NOT ADD MORE FILES.
ONLY OUTPUT THE EXACT FILES LISTED IN "Files to Edit".
` : ''}

VIOLATION OF THESE RULES WILL RESULT IN FAILURE!
` : ''}

CRITICAL INCREMENTAL UPDATE RULES:
- When the user asks for additions or modifications (like "add a videos page", "create a new component", "update the header"):
  - DO NOT regenerate the entire application
  - DO NOT recreate files that already exist unless explicitly asked
  - ONLY create/modify the specific files needed for the requested change
  - Preserve all existing functionality and files
  - If adding a new page/route, integrate it with the existing routing system
  - Reference existing components and styles rather than duplicating them
  - NEVER recreate config files (tailwind.config.js, vite.config.js, package.json, etc.)

IMPORTANT: When the user asks for edits or modifications:
- You have access to the current file contents in the context
- Make targeted changes to existing files rather than regenerating everything
- Preserve the existing structure and only modify what's requested
- If you need to see a specific file that's not in context, mention it

IMPORTANT: You have access to the full conversation context including:
- Previously scraped websites and their content
- Components already generated and applied
- The current project being worked on
- Recent conversation history
- Any Vite errors that need to be resolved

When the user references "the app", "the website", or "the site" without specifics, refer to:
1. The most recently scraped website in the context
2. The current project name in the context
3. The files currently in the sandbox

If you see scraped websites in the context, you're working on a clone/recreation of that site.

CRITICAL UI/UX RULES:
- NEVER use emojis in any code, text, console logs, or UI elements
- ALWAYS ensure responsive design using proper Tailwind classes (sm:, md:, lg:, xl:)
- ALWAYS use proper mobile-first responsive design patterns
- NEVER hardcode pixel widths - use relative units and responsive classes
- ALWAYS test that the layout works on mobile devices (320px and up)
- ALWAYS make sections full-width by default - avoid max-w-7xl or similar constraints
- For full-width layouts: use className="w-full" or no width constraint at all
- Only add max-width constraints when explicitly needed for readability (like blog posts)
- Prefer system fonts and clean typography
- Ensure all interactive elements have proper hover/focus states
- Use proper semantic HTML elements for accessibility

CRITICAL STYLING RULES - MUST FOLLOW:
- NEVER use inline styles with style={{ }} in JSX
- NEVER use <style jsx> tags or any CSS-in-JS solutions
- NEVER create App.css, Component.css, or any component-specific CSS files
- NEVER import './App.css' or any CSS files except index.css
- ALWAYS use Tailwind CSS classes for ALL styling
- ONLY create src/index.css with the @tailwind directives
- The ONLY CSS file should be src/index.css with:
  @tailwind base;
  @tailwind components;
  @tailwind utilities;
- Use Tailwind's full utility set: spacing, colors, typography, flexbox, grid, animations, etc.
- ALWAYS add smooth transitions and animations where appropriate:
  - Use transition-all, transition-colors, transition-opacity for hover states
  - Use animate-fade-in, animate-pulse, animate-bounce for engaging UI elements
  - Add hover:scale-105 or hover:scale-110 for interactive elements
  - Use transform and transition utilities for smooth interactions
- For complex layouts, combine Tailwind utilities rather than writing custom CSS
- NEVER use non-standard Tailwind classes like "border-border", "bg-background", "text-foreground", etc.
- Use standard Tailwind classes only:
  - For borders: use "border-gray-200", "border-gray-300", etc. NOT "border-border"
  - For backgrounds: use "bg-white", "bg-gray-100", etc. NOT "bg-background"
  - For text: use "text-gray-900", "text-black", etc. NOT "text-foreground"
- Examples of good Tailwind usage:
  - Buttons: className="px-4 py-2 bg-blue-600 text-white rounded-lg shadow-md hover:bg-blue-700 hover:shadow-lg transform hover:scale-105 transition-all duration-200"
  - Cards: className="bg-white rounded-lg shadow-md p-6 border border-gray-200 hover:shadow-xl transition-shadow duration-300"
  - Full-width sections: className="w-full px-4 sm:px-6 lg:px-8"
  - Constrained content (only when needed): className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
  - Dark backgrounds: className="min-h-screen bg-gray-900 text-white"
  - Hero sections: className="animate-fade-in-up"
  - Feature cards: className="transform hover:scale-105 transition-transform duration-300"
  - CTAs: className="animate-pulse hover:animate-none"

CRITICAL STRING AND SYNTAX RULES:
- ALWAYS escape apostrophes in strings: use \' instead of ' or use double quotes
- ALWAYS escape quotes properly in JSX attributes
- NEVER use curly quotes or smart quotes ('' "" '' "") - only straight quotes (' ")
- ALWAYS convert smart/curly quotes to straight quotes:
  - ' and ' → '
  - " and " → "
  - Any other Unicode quotes → straight quotes
- When strings contain apostrophes, either:
  1. Use double quotes: "you're" instead of 'you're'
  2. Escape the apostrophe: 'you\'re'
- When working with scraped content, ALWAYS sanitize quotes first
- Replace all smart quotes with straight quotes before using in code
- Be extra careful with user-generated content or scraped text
- Always validate that JSX syntax is correct before generating

CRITICAL CODE SNIPPET DISPLAY RULES:
- When displaying code examples in JSX, NEVER put raw curly braces { } in text
- ALWAYS wrap code snippets in template literals with backticks
- For code examples in components, use one of these patterns:
  1. Template literals: <div>{\`const example = { key: 'value' }\`}</div>
  2. Pre/code blocks: <pre><code>{\`your code here\`}</code></pre>
  3. Escape braces: <div>{'{'}key: value{'}'}</div>
- NEVER do this: <div>const example = { key: 'value' }</div> (causes parse errors)
- For multi-line code snippets, always use:
  <pre className="bg-gray-900 text-gray-100 p-4 rounded">
    <code>{\`
      // Your code here
      const example = {
        key: 'value'
      }
    \`}</code>
  </pre>

CRITICAL: When asked to create a React app or components:
- ALWAYS CREATE ALL FILES IN FULL - never provide partial implementations
- ALWAYS CREATE EVERY COMPONENT that you import - no placeholders
- ALWAYS IMPLEMENT COMPLETE FUNCTIONALITY - don't leave TODOs unless explicitly asked
- If you're recreating a website, implement ALL sections and features completely
- NEVER create tailwind.config.js - it's already configured in the template
- ALWAYS include a Navigation/Header component (Nav.jsx or Header.jsx) - websites need navigation!

REQUIRED COMPONENTS for website clones:
1. Nav.jsx or Header.jsx - Navigation bar with links (NEVER SKIP THIS!)
2. Hero.jsx - Main landing section
3. Features/Services/Products sections - Based on the site content
4. Footer.jsx - Footer with links and info
5. App.jsx - Main component that imports and arranges all components
- NEVER create vite.config.js - it's already configured in the template
- NEVER create package.json - it's already configured in the template

WHEN WORKING WITH SCRAPED CONTENT:
- ALWAYS sanitize all text content before using in code
- Convert ALL smart quotes to straight quotes
- Example transformations:
  - "Firecrawl's API" → "Firecrawl's API" or "Firecrawl\\'s API"
  - 'It's amazing' → "It's amazing" or 'It\\'s amazing'
  - "Best tool ever" → "Best tool ever"
- When in doubt, use double quotes for strings containing apostrophes
- For testimonials or quotes from scraped content, ALWAYS clean the text:
  - Bad: content: 'Moved our internal agent's web scraping...'
  - Good: content: "Moved our internal agent's web scraping..."
  - Also good: content: 'Moved our internal agent\\'s web scraping...'

When generating code, FOLLOW THIS PROCESS:
1. ALWAYS generate src/index.css FIRST - this establishes the styling foundation
2. List ALL components you plan to import in App.jsx
3. Count them - if there are 10 imports, you MUST create 10 component files
4. Generate src/index.css first (with proper CSS reset and base styles)
5. Generate App.jsx second
6. Then generate EVERY SINGLE component file you imported
7. Do NOT stop until all imports are satisfied

Use this XML format for React components only (DO NOT create tailwind.config.js - it already exists):

<file path="src/index.css">
@tailwind base;
@tailwind components;
@tailwind utilities;
</file>

<file path="src/App.jsx">
// Main App component that imports and uses other components
// Use Tailwind classes: className="min-h-screen bg-gray-50"
</file>

<file path="src/components/Example.jsx">
// Your React component code here
// Use Tailwind classes for ALL styling
</file>

CRITICAL COMPLETION RULES:
1. NEVER say "I'll continue with the remaining components"
2. NEVER say "Would you like me to proceed?"
3. NEVER use <continue> tags
4. Generate ALL components in ONE response
5. If App.jsx imports 10 components, generate ALL 10
6. Complete EVERYTHING before ending your response

With 16,000 tokens available, you have plenty of space to generate a complete application. Use it!

UNDERSTANDING USER INTENT FOR INCREMENTAL VS FULL GENERATION:
- "add/create/make a [specific feature]" → Add ONLY that feature to existing app
- "add a videos page" → Create ONLY Videos.jsx and update routing
- "update the header" → Modify ONLY header component
- "fix the styling" → Update ONLY the affected components
- "change X to Y" → Find the file containing X and modify it
- "make the header black" → Find Header component and change its color
- "rebuild/recreate/start over" → Full regeneration
- Default to incremental updates when working on an existing app

SURGICAL EDIT RULES (CRITICAL FOR PERFORMANCE):
- **PREFER TARGETED CHANGES**: Don't regenerate entire components for small edits
- For color/style changes: Edit ONLY the specific className or style prop
- For text changes: Change ONLY the text content, keep everything else
- For adding elements: INSERT into existing JSX, don't rewrite the whole return
- **PRESERVE EXISTING CODE**: Keep all imports, functions, and unrelated code exactly as-is
- Maximum files to edit:
  - Style change = 1 file ONLY
  - Text change = 1 file ONLY
  - New feature = 2 files MAX (feature + parent)
- If you're editing >3 files for a simple request, STOP - you're doing too much

EXAMPLES OF CORRECT SURGICAL EDITS:
✅ "change header to black" → Find className="..." in Header.jsx, change ONLY color classes
✅ "update hero text" → Find the <h1> or <p> in Hero.jsx, change ONLY the text inside
✅ "add a button to hero" → Find the return statement, ADD button, keep everything else
❌ WRONG: Regenerating entire Header.jsx to change one color
❌ WRONG: Rewriting Hero.jsx to add one button

NAVIGATION/HEADER INTELLIGENCE:
- ALWAYS check App.jsx imports first
- Navigation is usually INSIDE Header.jsx, not separate
- If user says "nav", check Header.jsx FIRST
- Only create Nav.jsx if no navigation exists anywhere
- Logo, menu, hamburger = all typically in Header

CRITICAL: When files are provided in the context:
1. The user is asking you to MODIFY the existing app, not create a new one
2. Find the relevant file(s) from the provided context
3. Generate ONLY the files that need changes
4. Do NOT ask to see files - they are already provided in the context above
5. Make the requested change immediately`;

        // If Morph Fast Apply is enabled (edit mode + MORPH_API_KEY), force <edit> block output
        const morphFastApplyEnabled = Boolean(isEdit && process.env.MORPH_API_KEY);
        if (morphFastApplyEnabled) {
          systemPrompt += `

MORPH FAST APPLY MODE (EDIT-ONLY):
- Output edits as <edit> blocks, not full <file> blocks, for files that already exist.
- Format for each edit:
  <edit target_file="src/components/Header.jsx">
    <instructions>Describe the minimal change, single sentence.</instructions>
    <update>Provide the SMALLEST code snippet necessary to perform the change.</update>
  </edit>
- Only use <file> blocks when you must CREATE a brand-new file.
- Prefer ONE edit block for a simple change; multiple edits only if absolutely needed for separate files.
- Keep updates minimal and precise; do not rewrite entire files.
`;
        }

        // Build full prompt with context
        let fullPrompt = prompt;
        if (context) {
          const contextParts = [];
          
          if (context.sandboxId) {
            contextParts.push(`Current sandbox ID: ${context.sandboxId}`);
          }
          
          if (context.structure) {
            contextParts.push(`Current file structure:\n${context.structure}`);
          }
          
          // Use backend file cache instead of frontend-provided files
          let backendFiles = global.sandboxState?.fileCache?.files || {};
          let hasBackendFiles = Object.keys(backendFiles).length > 0;
          
          console.log('[generate-ai-code-stream] Backend file cache status:');
          console.log('[generate-ai-code-stream] - Has sandboxState:', !!global.sandboxState);
          console.log('[generate-ai-code-stream] - Has fileCache:', !!global.sandboxState?.fileCache);
          console.log('[generate-ai-code-stream] - File count:', Object.keys(backendFiles).length);
          console.log('[generate-ai-code-stream] - Has manifest:', !!global.sandboxState?.fileCache?.manifest);
          
          // If no backend files and we're in edit mode, try to fetch from sandbox
          if (!hasBackendFiles && isEdit && (global.activeSandbox || context?.sandboxId)) {
            console.log('[generate-ai-code-stream] No backend files, attempting to fetch from sandbox...');
            
            try {
              const filesResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/get-sandbox-files`, {
                method: 'GET',
                headers: { 'Content-Type': 'application/json' }
              });
              
              if (filesResponse.ok) {
                const filesData = await filesResponse.json();
                if (filesData.success && filesData.files) {
                  console.log('[generate-ai-code-stream] Successfully fetched', Object.keys(filesData.files).length, 'files from sandbox');
                  
                  // Initialize sandboxState if needed
                  if (!global.sandboxState) {
                    global.sandboxState = {
                      fileCache: {
                        files: {},
                        lastSync: Date.now(),
                        sandboxId: context?.sandboxId || 'unknown'
                      }
                    } as any;
                  } else if (!global.sandboxState.fileCache) {
                    global.sandboxState.fileCache = {
                      files: {},
                      lastSync: Date.now(),
                      sandboxId: context?.sandboxId || 'unknown'
                    };
                  }
                  
                  // Store files in cache
                  for (const [path, content] of Object.entries(filesData.files)) {
                    const normalizedPath = path.replace('/home/user/app/', '');
                    if (global.sandboxState.fileCache) {
                      global.sandboxState.fileCache.files[normalizedPath] = {
                        content: content as string,
                        lastModified: Date.now()
                      };
                    }
                  }
                  
                  if (filesData.manifest && global.sandboxState.fileCache) {
                    global.sandboxState.fileCache.manifest = filesData.manifest;
                    
                    // Now try to analyze edit intent with the fetched manifest
                    if (!editContext) {
                      console.log('[generate-ai-code-stream] Analyzing edit intent with fetched manifest');
                      try {
                        const intentResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/analyze-edit-intent`, {
                          method: 'POST',
                          headers: { 'Content-Type': 'application/json' },
                          body: JSON.stringify({ prompt, manifest: filesData.manifest, model })
                        });
                        
                        if (intentResponse.ok) {
                          const { searchPlan } = await intentResponse.json();
                          console.log('[generate-ai-code-stream] Search plan received:', searchPlan);
                          
                          // Create edit context from AI analysis
                          // Note: We can't execute search here without file contents, so fall back to keyword method
                          const fileContext = selectFilesForEdit(prompt, filesData.manifest);
                          editContext = fileContext;
                          enhancedSystemPrompt = fileContext.systemPrompt;
                          
                          console.log('[generate-ai-code-stream] Edit context created with', editContext.primaryFiles.length, 'primary files');
                        }
                      } catch (error) {
                        console.error('[generate-ai-code-stream] Failed to analyze edit intent:', error);
                      }
                    }
                  }
                  
                  // Update variables
                  backendFiles = global.sandboxState.fileCache?.files || {};
                  hasBackendFiles = Object.keys(backendFiles).length > 0;
                  console.log('[generate-ai-code-stream] Updated backend cache with fetched files');
                }
              }
            } catch (error) {
              console.error('[generate-ai-code-stream] Failed to fetch sandbox files:', error);
            }
          }
          
          // Include current file contents from backend cache
          if (hasBackendFiles) {
            // If we have edit context, use intelligent file selection
            if (editContext && editContext.primaryFiles.length > 0) {
              contextParts.push('\nEXISTING APPLICATION - TARGETED EDIT MODE');
              contextParts.push(`\n${editContext.systemPrompt || enhancedSystemPrompt}\n`);
              
              // Get contents of primary and context files
              const primaryFileContents = await getFileContents(editContext.primaryFiles, global.sandboxState!.fileCache!.manifest!);
              const contextFileContents = await getFileContents(editContext.contextFiles, global.sandboxState!.fileCache!.manifest!);
              
              // Format files for AI
              const formattedFiles = formatFilesForAI(primaryFileContents, contextFileContents);
              contextParts.push(formattedFiles);
              
              contextParts.push('\nIMPORTANT: Only modify the files listed under "Files to Edit". The context files are provided for reference only.');
            } else {
              // Fallback to showing all files if no edit context
              console.log('[generate-ai-code-stream] WARNING: Using fallback mode - no edit context available');
              contextParts.push('\nEXISTING APPLICATION - TARGETED EDIT REQUIRED');
              contextParts.push('\nYou MUST analyze the user request and determine which specific file(s) to edit.');
              contextParts.push('\nCurrent project files (DO NOT regenerate all of these):');
              
              const fileEntries = Object.entries(backendFiles);
              console.log(`[generate-ai-code-stream] Using backend cache: ${fileEntries.length} files`);
              
              // Show file list first for reference
              contextParts.push('\n### File List:');
              for (const [path] of fileEntries) {
                contextParts.push(`- ${path}`);
              }
              
              // Include ALL files as context in fallback mode
              contextParts.push('\n### File Contents (ALL FILES FOR CONTEXT):');
              for (const [path, fileData] of fileEntries) {
                const content = fileData.content;
                if (typeof content === 'string') {
                  contextParts.push(`\n<file path="${path}">\n${content}\n</file>`);
                }
              }
              
              contextParts.push('\n🚨 CRITICAL INSTRUCTIONS - VIOLATION = FAILURE 🚨');
              contextParts.push('1. Analyze the user request: "' + prompt + '"');
              contextParts.push('2. Identify the MINIMUM number of files that need editing (usually just ONE)');
              contextParts.push('3. PRESERVE ALL EXISTING CONTENT in those files');
              contextParts.push('4. ONLY ADD/MODIFY the specific part requested');
              contextParts.push('5. DO NOT regenerate entire components from scratch');
              contextParts.push('6. DO NOT change unrelated parts of any file');
              contextParts.push('7. Generate ONLY the files that MUST be changed - NO EXTRAS');
              contextParts.push('\n⚠️ FILE COUNT RULE:');
              contextParts.push('- Simple change (color, text, spacing) = 1 file ONLY');
              contextParts.push('- Adding new component = 2 files MAX (new component + parent that imports it)');
              contextParts.push('- DO NOT exceed these limits unless absolutely necessary');
              contextParts.push('\nEXAMPLES OF CORRECT BEHAVIOR:');
              contextParts.push('✅ "add a chart to the hero" → Edit ONLY Hero.jsx, ADD the chart, KEEP everything else');
              contextParts.push('✅ "change header to black" → Edit ONLY Header.jsx, change ONLY the color');
              contextParts.push('✅ "fix spacing in footer" → Edit ONLY Footer.jsx, adjust ONLY spacing');
              contextParts.push('\nEXAMPLES OF FAILURES:');
              contextParts.push('❌ "change header color" → You edit Header, Footer, and App "for consistency"');
              contextParts.push('❌ "add chart to hero" → You regenerate the entire Hero component');
              contextParts.push('❌ "fix button" → You update 5 different component files');
              contextParts.push('\n⚠️ FINAL WARNING:');
              contextParts.push('If you generate MORE files than necessary, you have FAILED');
              contextParts.push('If you DELETE or REWRITE existing functionality, you have FAILED');
              contextParts.push('ONLY change what was EXPLICITLY requested - NOTHING MORE');
            }
          } else if (context.currentFiles && Object.keys(context.currentFiles).length > 0) {
            // Fallback to frontend-provided files if backend cache is empty
            console.log('[generate-ai-code-stream] Warning: Backend cache empty, using frontend files');
            contextParts.push('\nEXISTING APPLICATION - DO NOT REGENERATE FROM SCRATCH');
            contextParts.push('Current project files (modify these, do not recreate):');
            
            const fileEntries = Object.entries(context.currentFiles);
            for (const [path, content] of fileEntries) {
              if (typeof content === 'string') {
                contextParts.push(`\n<file path="${path}">\n${content}\n</file>`);
              }
            }
            contextParts.push('\nThe above files already exist. When the user asks to modify something (like "change the header color to black"), find the relevant file above and generate ONLY that file with the requested changes.');
          }
          
          // Add explicit edit mode indicator
          if (isEdit) {
            contextParts.push('\nEDIT MODE ACTIVE');
            contextParts.push('This is an incremental update to an existing application.');
            contextParts.push('DO NOT regenerate App.jsx, index.css, or other core files unless explicitly requested.');
            contextParts.push('ONLY create or modify the specific files needed for the user\'s request.');
            contextParts.push('\n⚠️ CRITICAL FILE OUTPUT FORMAT - VIOLATION = FAILURE:');
            contextParts.push('YOU MUST OUTPUT EVERY FILE IN THIS EXACT XML FORMAT:');
            contextParts.push('<file path="src/components/ComponentName.jsx">');
            contextParts.push('// Complete file content here');
            contextParts.push('</file>');
            contextParts.push('<file path="src/index.css">');
            contextParts.push('/* CSS content here */');
            contextParts.push('</file>');
            contextParts.push('\n❌ NEVER OUTPUT: "Generated Files: index.css, App.jsx"');
            contextParts.push('❌ NEVER LIST FILE NAMES WITHOUT CONTENT');
            contextParts.push('✅ ALWAYS: One <file> tag per file with COMPLETE content');
            contextParts.push('✅ ALWAYS: Include EVERY file you modified');
          } else if (!hasBackendFiles) {
            // First generation mode - make it beautiful!
            contextParts.push('\n🎨 FIRST GENERATION MODE - CREATE SOMETHING BEAUTIFUL!');
            contextParts.push('\nThis is the user\'s FIRST experience. Make it impressive:');
            contextParts.push('1. **USE TAILWIND PROPERLY** - Use standard Tailwind color classes');
            contextParts.push('2. **NO PLACEHOLDERS** - Use real content, not lorem ipsum');
            contextParts.push('3. **COMPLETE COMPONENTS** - Header, Hero, Features, Footer minimum');
            contextParts.push('4. **VISUAL POLISH** - Shadows, hover states, transitions');
            contextParts.push('5. **STANDARD CLASSES** - bg-white, text-gray-900, bg-blue-500, NOT bg-background');
            contextParts.push('\nCreate a polished, professional application that works perfectly on first load.');
            contextParts.push('\n⚠️ OUTPUT FORMAT:');
            contextParts.push('Use <file path="...">content</file> tags for EVERY file');
            contextParts.push('NEVER output "Generated Files:" as plain text');
          }
          
          // Add conversation context (scraped websites, etc)
          if (context.conversationContext) {
            if (context.conversationContext.scrapedWebsites?.length > 0) {
              contextParts.push('\nScraped Websites in Context:');
              context.conversationContext.scrapedWebsites.forEach((site: any) => {
                contextParts.push(`\nURL: ${site.url}`);
                contextParts.push(`Scraped: ${new Date(site.timestamp).toLocaleString()}`);
                if (site.content) {
                  // Include a summary of the scraped content
                  const contentPreview = typeof site.content === 'string' 
                    ? site.content.substring(0, 1000) 
                    : JSON.stringify(site.content).substring(0, 1000);
                  contextParts.push(`Content Preview: ${contentPreview}...`);
                }
              });
            }
            
            if (context.conversationContext.currentProject) {
              contextParts.push(`\nCurrent Project: ${context.conversationContext.currentProject}`);
            }
          }
          
          if (contextParts.length > 0) {
            if (morphFastApplyEnabled) {
              contextParts.push('\nOUTPUT FORMAT (REQUIRED IN MORPH MODE):');
              contextParts.push('<edit target_file="src/components/Component.jsx">');
              contextParts.push('<instructions>Minimal, precise instruction.</instructions>');
              contextParts.push('<update>// Smallest necessary snippet</update>');
              contextParts.push('</edit>');
              contextParts.push('\nIf you need to create a NEW file, then and only then output a full file:');
              contextParts.push('<file path="src/components/NewComponent.jsx">');
              contextParts.push('// Full file content when creating new files');
              contextParts.push('</file>');
            }
            fullPrompt = `CONTEXT:\n${contextParts.join('\n')}\n\nUSER REQUEST:\n${prompt}`;
          }
        }
        
        await sendProgress({ type: 'status', message: 'Planning application structure...' });
        
        console.log('\n[generate-ai-code-stream] Starting streaming response...\n');
        
        // Track packages that need to be installed
        const packagesToInstall: string[] = [];
        
        // Determine which provider to use based on model
        const isAnthropic = model.startsWith('anthropic/');
        const isGoogle = model.startsWith('google/');
        const isOpenAI = model.startsWith('openai/');
        const isKimiGroq = model === 'moonshotai/kimi-k2-instruct-0905';
        const modelProvider = isAnthropic ? anthropic : 
                              (isOpenAI ? openai : 
                              (isGoogle ? googleGenerativeAI : 
                              (isKimiGroq ? groq : groq)));
        
        // Fix model name transformation for different providers
        let actualModel: string;
        if (isAnthropic) {
          actualModel = model.replace('anthropic/', '');
        } else if (isOpenAI) {
          actualModel = model.replace('openai/', '');
        } else if (isKimiGroq) {
          // Kimi on Groq - use full model string
          actualModel = 'moonshotai/kimi-k2-instruct-0905';
        } else if (isGoogle) {
          // Google uses specific model names - convert our naming to theirs  
          actualModel = model.replace('google/', '');
        } else {
          actualModel = model;
        }

        console.log(`[generate-ai-code-stream] Using provider: ${isAnthropic ? 'Anthropic' : isGoogle ? 'Google' : isOpenAI ? 'OpenAI' : 'Groq'}, model: ${actualModel}`);
        console.log(`[generate-ai-code-stream] AI Gateway enabled: ${isUsingAIGateway}`);
        console.log(`[generate-ai-code-stream] Model string: ${model}`);

        // Make streaming API call with appropriate provider
        const streamOptions: any = {
          model: modelProvider(actualModel),
          messages: [
            { 
              role: 'system', 
              content: systemPrompt + `

🚨 CRITICAL CODE GENERATION RULES - VIOLATION = FAILURE 🚨:
1. NEVER truncate ANY code - ALWAYS write COMPLETE files
2. NEVER use "..." anywhere in your code - this causes syntax errors
3. NEVER cut off strings mid-sentence - COMPLETE every string
4. NEVER leave incomplete class names or attributes
5. ALWAYS close ALL tags, quotes, brackets, and parentheses
6. If you run out of space, prioritize completing the current file

CRITICAL STRING RULES TO PREVENT SYNTAX ERRORS:
- NEVER write: className="px-8 py-4 bg-black text-white font-bold neobrut-border neobr...
- ALWAYS write: className="px-8 py-4 bg-black text-white font-bold neobrut-border neobrut-shadow"
- COMPLETE every className attribute
- COMPLETE every string literal
- NO ellipsis (...) ANYWHERE in code

PACKAGE RULES:
- For INITIAL generation: Use ONLY React, no external packages
- For EDITS: You may use packages, specify them with <package> tags
- NEVER install packages like @mendable/firecrawl-js unless explicitly requested

Examples of SYNTAX ERRORS (NEVER DO THIS):
❌ className="px-4 py-2 bg-blue-600 hover:bg-blue-7...
❌ <button className="btn btn-primary btn-...
❌ const title = "Welcome to our...
❌ import { useState, useEffect, ... } from 'react'

Examples of CORRECT CODE (ALWAYS DO THIS):
✅ className="px-4 py-2 bg-blue-600 hover:bg-blue-700"
✅ <button className="btn btn-primary btn-large">
✅ const title = "Welcome to our application"
✅ import { useState, useEffect, useCallback } from 'react'

REMEMBER: It's better to generate fewer COMPLETE files than many INCOMPLETE files.`
            },
            { 
              role: 'user', 
              content: fullPrompt + `

CRITICAL: You MUST complete EVERY file you start. If you write:
<file path="src/components/Hero.jsx">

You MUST include the closing </file> tag and ALL the code in between.

NEVER write partial code like:
<h1>Build and deploy on the AI Cloud.</h1>
<p>Some text...</p>  ❌ WRONG

ALWAYS write complete code:
<h1>Build and deploy on the AI Cloud.</h1>
<p>Some text here with full content</p>  ✅ CORRECT

If you're running out of space, generate FEWER files but make them COMPLETE.
It's better to have 3 complete files than 10 incomplete files.`
            }
          ],
          maxTokens: 8192, // Reduce to ensure completion
          stopSequences: [] // Don't stop early
          // Note: Neither Groq nor Anthropic models support tool/function calling in this context
          // We use XML tags for package detection instead
        };
        
        // Add temperature for non-reasoning models
        if (!model.startsWith('openai/gpt-5')) {
          streamOptions.temperature = 0.7;
        }
        
        // Add reasoning effort for GPT-5 models
        if (isOpenAI) {
          streamOptions.experimental_providerMetadata = {
            openai: {
              reasoningEffort: 'high'
            }
          };
        }
        
        let result;
        let retryCount = 0;
        const maxRetries = 2;
        
        while (retryCount <= maxRetries) {
          try {
            result = await streamText(streamOptions);
            break; // Success, exit retry loop
          } catch (streamError: any) {
            console.error(`[generate-ai-code-stream] Error calling streamText (attempt ${retryCount + 1}/${maxRetries + 1}):`, streamError);
            
            // Check if this is a Groq service unavailable error
            const isGroqServiceError = isKimiGroq && streamError.message?.includes('Service unavailable');
            const isRetryableError = streamError.message?.includes('Service unavailable') || 
                                    streamError.message?.includes('rate limit') ||
                                    streamError.message?.includes('timeout');
            
            if (retryCount < maxRetries && isRetryableError) {
              retryCount++;
              console.log(`[generate-ai-code-stream] Retrying in ${retryCount * 2} seconds...`);
              
              // Send progress update about retry
              await sendProgress({ 
                type: 'info', 
                message: `Service temporarily unavailable, retrying (attempt ${retryCount + 1}/${maxRetries + 1})...` 
              });
              
              // Wait before retry with exponential backoff
              await new Promise(resolve => setTimeout(resolve, retryCount * 2000));
              
              // If Groq fails, try switching to a fallback model
              if (isGroqServiceError && retryCount === maxRetries) {
                console.log('[generate-ai-code-stream] Groq service unavailable, falling back to GPT-4');
                streamOptions.model = openai('gpt-4-turbo');
                actualModel = 'gpt-4-turbo';
              }
            } else {
              // Final error, send to user
              await sendProgress({ 
                type: 'error', 
                message: `Failed to initialize ${isGoogle ? 'Gemini' : isAnthropic ? 'Claude' : isOpenAI ? 'GPT-5' : isKimiGroq ? 'Kimi (Groq)' : 'Groq'} streaming: ${streamError.message}` 
              });
              
              // If this is a Google model error, provide helpful info
              if (isGoogle) {
                await sendProgress({ 
                  type: 'info', 
                  message: 'Tip: Make sure your GEMINI_API_KEY is set correctly and has proper permissions.' 
                });
              }
              
              throw streamError;
            }
          }
        }
        
        // Stream the response and parse in real-time
        let generatedCode = '';
        let currentFile = '';
        let currentFilePath = '';
        let componentCount = 0;
        let isInFile = false;
        let isInTag = false;
        let conversationalBuffer = '';
        
        // Buffer for incomplete tags
        let tagBuffer = '';
        
        // Stream the response and parse for packages in real-time
        for await (const textPart of result?.textStream || []) {
          const text = textPart || '';
          generatedCode += text;
          currentFile += text;
          
          // Combine with buffer for tag detection
          const searchText = tagBuffer + text;
          
          // Log streaming chunks to console
          process.stdout.write(text);
          
          // Check if we're entering or leaving a tag
          const hasOpenTag = /<(file|package|packages|explanation|command|structure|template)\b/.test(text);
          const hasCloseTag = /<\/(file|package|packages|explanation|command|structure|template)>/.test(text);
          
          if (hasOpenTag) {
            // Send any buffered conversational text before the tag
            if (conversationalBuffer.trim() && !isInTag) {
              await sendProgress({ 
                type: 'conversation', 
                text: conversationalBuffer.trim()
              });
              conversationalBuffer = '';
            }
            isInTag = true;
          }
          
          if (hasCloseTag) {
            isInTag = false;
          }
          
          // If we're not in a tag, buffer as conversational text
          if (!isInTag && !hasOpenTag) {
            conversationalBuffer += text;
          }
          
          // Stream the raw text for live preview
          await sendProgress({ 
            type: 'stream', 
            text: text,
            raw: true 
          });
          
          // Debug: Log every 100 characters streamed
          if (generatedCode.length % 100 < text.length) {
            console.log(`[generate-ai-code-stream] Streamed ${generatedCode.length} chars`);
          }
          
          // Check for package tags in buffered text (ONLY for edits, not initial generation)
          let lastIndex = 0;
          if (isEdit) {
            const packageRegex = /<package>([^<]+)<\/package>/g;
            let packageMatch;
            
            while ((packageMatch = packageRegex.exec(searchText)) !== null) {
              const packageName = packageMatch[1].trim();
              if (packageName && !packagesToInstall.includes(packageName)) {
                packagesToInstall.push(packageName);
                console.log(`[generate-ai-code-stream] Package detected: ${packageName}`);
                await sendProgress({ 
                  type: 'package', 
                  name: packageName,
                  message: `Package detected: ${packageName}`
                });
              }
              lastIndex = packageMatch.index + packageMatch[0].length;
            }
          }
          
          // Keep unmatched portion in buffer for next iteration
          tagBuffer = searchText.substring(Math.max(0, lastIndex - 50)); // Keep last 50 chars
          
          // Check for file boundaries
          if (text.includes('<file path="')) {
            const pathMatch = text.match(/<file path="([^"]+)"/);
            if (pathMatch) {
              currentFilePath = pathMatch[1];
              isInFile = true;
              currentFile = text;
            }
          }
          
          // Check for file end
          if (isInFile && currentFile.includes('</file>')) {
            isInFile = false;
            
            // Send component progress update
            if (currentFilePath.includes('components/')) {
              componentCount++;
              const componentName = currentFilePath.split('/').pop()?.replace('.jsx', '') || 'Component';
              await sendProgress({ 
                type: 'component', 
                name: componentName,
                path: currentFilePath,
                index: componentCount
              });
            } else if (currentFilePath.includes('App.jsx')) {
              await sendProgress({ 
                type: 'app', 
                message: 'Generated main App.jsx',
                path: currentFilePath
              });
            }
            
            currentFile = '';
            currentFilePath = '';
          }
        }
        
        console.log('\n\n[generate-ai-code-stream] Streaming complete.');
        
        // Send any remaining conversational text
        if (conversationalBuffer.trim()) {
          await sendProgress({ 
            type: 'conversation', 
            text: conversationalBuffer.trim()
          });
        }
        
        // Also parse <packages> tag for multiple packages - ONLY for edits
        if (isEdit) {
          const packagesRegex = /<packages>([\s\S]*?)<\/packages>/g;
          let packagesMatch;
          while ((packagesMatch = packagesRegex.exec(generatedCode)) !== null) {
            const packagesContent = packagesMatch[1].trim();
            const packagesList = packagesContent.split(/[\n,]+/)
              .map(pkg => pkg.trim())
              .filter(pkg => pkg.length > 0);
            
            for (const packageName of packagesList) {
              if (!packagesToInstall.includes(packageName)) {
                packagesToInstall.push(packageName);
                console.log(`[generate-ai-code-stream] Package from <packages> tag: ${packageName}`);
                await sendProgress({ 
                  type: 'package', 
                  name: packageName,
                  message: `Package detected: ${packageName}`
                });
              }
            }
          }
        }
        
        // Function to extract packages from import statements
        function extractPackagesFromCode(content: string): string[] {
          const packages: string[] = [];
          // Match ES6 imports
          const importRegex = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)(?:\s*,\s*(?:\{[^}]*\}|\*\s+as\s+\w+|\w+))*\s+from\s+)?['"]([^'"]+)['"]/g;
          let importMatch;
          
          while ((importMatch = importRegex.exec(content)) !== null) {
            const importPath = importMatch[1];
            // Skip relative imports and built-in React
            if (!importPath.startsWith('.') && !importPath.startsWith('/') && 
                importPath !== 'react' && importPath !== 'react-dom' &&
                !importPath.startsWith('@/')) {
              // Extract package name (handle scoped packages like @heroicons/react)
              const packageName = importPath.startsWith('@') 
                ? importPath.split('/').slice(0, 2).join('/')
                : importPath.split('/')[0];
              
              if (!packages.includes(packageName)) {
                packages.push(packageName);
              }
            }
          }
          
          return packages;
        }
        
        // Parse files and send progress for each
        const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
        const files = [];
        let match;
        
        while ((match = fileRegex.exec(generatedCode)) !== null) {
          const filePath = match[1];
          const content = match[2].trim();
          files.push({ path: filePath, content });
          
          // Extract packages from file content - ONLY for edits
          if (isEdit) {
            const filePackages = extractPackagesFromCode(content);
            for (const pkg of filePackages) {
              if (!packagesToInstall.includes(pkg)) {
                packagesToInstall.push(pkg);
                console.log(`[generate-ai-code-stream] Package detected from imports: ${pkg}`);
                await sendProgress({ 
                  type: 'package', 
                  name: pkg,
                  message: `Package detected from imports: ${pkg}`
                });
              }
            }
          }
          
          // Send progress for each file (reusing componentCount from streaming)
          if (filePath.includes('components/')) {
            const componentName = filePath.split('/').pop()?.replace('.jsx', '') || 'Component';
            await sendProgress({ 
              type: 'component', 
              name: componentName,
              path: filePath,
              index: componentCount
            });
          } else if (filePath.includes('App.jsx')) {
            await sendProgress({ 
              type: 'app', 
              message: 'Generated main App.jsx',
              path: filePath
            });
          }
        }
        
        // Extract explanation
        const explanationMatch = generatedCode.match(/<explanation>([\s\S]*?)<\/explanation>/);
        const explanation = explanationMatch ? explanationMatch[1].trim() : 'Code generated successfully!';
        
        // Validate generated code for truncation issues
        const truncationWarnings: string[] = [];
        
        // Skip ellipsis checking entirely - too many false positives with spread operators, loading text, etc.
        
        // Check for unclosed file tags
        const fileOpenCount = (generatedCode.match(/<file path="/g) || []).length;
        const fileCloseCount = (generatedCode.match(/<\/file>/g) || []).length;
        if (fileOpenCount !== fileCloseCount) {
          truncationWarnings.push(`Unclosed file tags detected: ${fileOpenCount} open, ${fileCloseCount} closed`);
        }
        
        // Check for files that seem truncated (very short or ending abruptly)
        const truncationCheckRegex = /<file path="([^"]+)">([\s\S]*?)(?:<\/file>|$)/g;
        let truncationMatch;
        while ((truncationMatch = truncationCheckRegex.exec(generatedCode)) !== null) {
          const filePath = truncationMatch[1];
          const content = truncationMatch[2];
          
          // Only check for really obvious HTML truncation - file ends with opening tag
          if (content.trim().endsWith('<') || content.trim().endsWith('</')) {
            truncationWarnings.push(`File ${filePath} appears to have incomplete HTML tags`);
          }
          
          // Skip "..." check - too many false positives with loading text, etc.
          
          // Only check for SEVERE truncation issues
          if (filePath.match(/\.(jsx?|tsx?)$/)) {
            // Only check for severely unmatched brackets (more than 3 difference)
            const openBraces = (content.match(/{/g) || []).length;
            const closeBraces = (content.match(/}/g) || []).length;
            const braceDiff = Math.abs(openBraces - closeBraces);
            if (braceDiff > 3) { // Only flag severe mismatches
              truncationWarnings.push(`File ${filePath} has severely unmatched braces (${openBraces} open, ${closeBraces} closed)`);
            }
            
            // Check if file is extremely short and looks incomplete
            if (content.length < 20 && content.includes('function') && !content.includes('}')) {
              truncationWarnings.push(`File ${filePath} appears severely truncated`);
            }
          }
        }
        
        // Handle truncation with automatic retry (if enabled in config)
        if (truncationWarnings.length > 0 && appConfig.codeApplication.enableTruncationRecovery) {
          console.warn('[generate-ai-code-stream] Truncation detected, attempting to fix:', truncationWarnings);
          
          await sendProgress({
            type: 'warning',
            message: 'Detected incomplete code generation. Attempting to complete...',
            warnings: truncationWarnings
          });
          
          // Try to fix truncated files automatically
          const truncatedFiles: string[] = [];
          const fileRegex = /<file path="([^"]+)">([\s\S]*?)(?:<\/file>|$)/g;
          let match;
          
          while ((match = fileRegex.exec(generatedCode)) !== null) {
            const filePath = match[1];
            const content = match[2];
            
            // Check if this file appears truncated - be more selective
            const hasEllipsis = content.includes('...') && 
                               !content.includes('...rest') && 
                               !content.includes('...props') &&
                               !content.includes('spread');
                               
            const endsAbruptly = content.trim().endsWith('...') || 
                                 content.trim().endsWith(',') ||
                                 content.trim().endsWith('(');
                                 
            const hasUnclosedTags = content.includes('</') && 
                                    !content.match(/<\/[a-zA-Z0-9]+>/) &&
                                    content.includes('<');
                                    
            const tooShort = content.length < 50 && filePath.match(/\.(jsx?|tsx?)$/);
            
            // Check for unmatched braces specifically
            const openBraceCount = (content.match(/{/g) || []).length;
            const closeBraceCount = (content.match(/}/g) || []).length;
            const hasUnmatchedBraces = Math.abs(openBraceCount - closeBraceCount) > 1;
            
            const isTruncated = (hasEllipsis && endsAbruptly) || 
                               hasUnclosedTags || 
                               (tooShort && !content.includes('export')) ||
                               hasUnmatchedBraces;
            
            if (isTruncated) {
              truncatedFiles.push(filePath);
            }
          }
          
          // If we have truncated files, try to regenerate them
          if (truncatedFiles.length > 0) {
            console.log('[generate-ai-code-stream] Attempting to regenerate truncated files:', truncatedFiles);
            
            for (const filePath of truncatedFiles) {
              await sendProgress({
                type: 'info',
                message: `Completing ${filePath}...`
              });
              
              try {
                // Create a focused prompt to complete just this file
                const completionPrompt = `Complete the following file that was truncated. Provide the FULL file content.
                
File: ${filePath}
Original request: ${prompt}
                
Provide the complete file content without any truncation. Include all necessary imports, complete all functions, and close all tags properly.`;
                
                // Make a focused API call to complete this specific file
                // Create a new client for the completion based on the provider
                let completionClient;
                if (model.includes('gpt') || model.includes('openai')) {
                  completionClient = openai;
                } else if (model.includes('claude')) {
                  completionClient = anthropic;
                } else if (model === 'moonshotai/kimi-k2-instruct-0905') {
                  completionClient = groq;
                } else {
                  completionClient = groq;
                }
                
                // Determine the correct model name for the completion
                let completionModelName: string;
                if (model === 'moonshotai/kimi-k2-instruct-0905') {
                  completionModelName = 'moonshotai/kimi-k2-instruct-0905';
                } else if (model.includes('openai')) {
                  completionModelName = model.replace('openai/', '');
                } else if (model.includes('anthropic')) {
                  completionModelName = model.replace('anthropic/', '');
                } else if (model.includes('google')) {
                  completionModelName = model.replace('google/', '');
                } else {
                  completionModelName = model;
                }
                
                const completionResult = await streamText({
                  model: completionClient(completionModelName),
                  messages: [
                    { 
                      role: 'system', 
                      content: 'You are completing a truncated file. Provide the complete, working file content.'
                    },
                    { role: 'user', content: completionPrompt }
                  ],
                  temperature: model.startsWith('openai/gpt-5') ? undefined : appConfig.ai.defaultTemperature
                });
                
                // Get the full text from the stream
                let completedContent = '';
                for await (const chunk of completionResult.textStream) {
                  completedContent += chunk;
                }
                
                // Replace the truncated file in the generatedCode
                const filePattern = new RegExp(
                  `<file path="${filePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}">[\\s\\S]*?(?:</file>|$)`,
                  'g'
                );
                
                // Extract just the code content (remove any markdown or explanation)
                let cleanContent = completedContent;
                if (cleanContent.includes('```')) {
                  const codeMatch = cleanContent.match(/```[\w]*\n([\s\S]*?)```/);
                  if (codeMatch) {
                    cleanContent = codeMatch[1];
                  }
                }
                
                generatedCode = generatedCode.replace(
                  filePattern,
                  `<file path="${filePath}">\n${cleanContent}\n</file>`
                );
                
                console.log(`[generate-ai-code-stream] Successfully completed ${filePath}`);
                
              } catch (completionError) {
                console.error(`[generate-ai-code-stream] Failed to complete ${filePath}:`, completionError);
                await sendProgress({
                  type: 'warning',
                  message: `Could not auto-complete ${filePath}. Manual review may be needed.`
                });
              }
            }
            
            // Clear the warnings after attempting fixes
            truncationWarnings.length = 0;
            await sendProgress({
              type: 'info',
              message: 'Truncation recovery complete'
            });
          }
        }
        
        // Send completion with packages info
        await sendProgress({ 
          type: 'complete', 
          generatedCode,
          explanation,
          files: files.length,
          components: componentCount,
          model,
          packagesToInstall: packagesToInstall.length > 0 ? packagesToInstall : undefined,
          warnings: truncationWarnings.length > 0 ? truncationWarnings : undefined
        });
        
        // Track edit in conversation history
        if (isEdit && editContext && global.conversationState) {
          const editRecord: ConversationEdit = {
            timestamp: Date.now(),
            userRequest: prompt,
            editType: editContext.editIntent.type,
            targetFiles: editContext.primaryFiles,
            confidence: editContext.editIntent.confidence,
            outcome: 'success' // Assuming success if we got here
          };
          
          global.conversationState.context.edits.push(editRecord);
          
          // Track major changes
          if (editContext.editIntent.type === 'ADD_FEATURE' || files.length > 3) {
            global.conversationState.context.projectEvolution.majorChanges.push({
              timestamp: Date.now(),
              description: editContext.editIntent.description,
              filesAffected: editContext.primaryFiles
            });
          }
          
          // Update last updated timestamp
          global.conversationState.lastUpdated = Date.now();
          
          console.log('[generate-ai-code-stream] Updated conversation history with edit:', editRecord);
        }
        
      } catch (error) {
        console.error('[generate
Download .txt
gitextract__mw591t9/

├── .cursor/
│   └── mcp.json
├── .env.example
├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── api/
│   │   ├── analyze-edit-intent/
│   │   │   └── route.ts
│   │   ├── apply-ai-code/
│   │   │   └── route.ts
│   │   ├── apply-ai-code-stream/
│   │   │   └── route.ts
│   │   ├── check-vite-errors/
│   │   │   └── route.ts
│   │   ├── clear-vite-errors-cache/
│   │   │   └── route.ts
│   │   ├── conversation-state/
│   │   │   └── route.ts
│   │   ├── create-ai-sandbox/
│   │   │   └── route.ts
│   │   ├── create-ai-sandbox-v2/
│   │   │   └── route.ts
│   │   ├── create-zip/
│   │   │   └── route.ts
│   │   ├── detect-and-install-packages/
│   │   │   └── route.ts
│   │   ├── extract-brand-styles/
│   │   │   └── route.ts
│   │   ├── generate-ai-code-stream/
│   │   │   └── route.ts
│   │   ├── get-sandbox-files/
│   │   │   └── route.ts
│   │   ├── install-packages/
│   │   │   └── route.ts
│   │   ├── install-packages-v2/
│   │   │   └── route.ts
│   │   ├── kill-sandbox/
│   │   │   └── route.ts
│   │   ├── monitor-vite-logs/
│   │   │   └── route.ts
│   │   ├── report-vite-error/
│   │   │   └── route.ts
│   │   ├── restart-vite/
│   │   │   └── route.ts
│   │   ├── run-command/
│   │   │   └── route.ts
│   │   ├── run-command-v2/
│   │   │   └── route.ts
│   │   ├── sandbox-logs/
│   │   │   └── route.ts
│   │   ├── sandbox-status/
│   │   │   └── route.ts
│   │   ├── scrape-screenshot/
│   │   │   └── route.ts
│   │   ├── scrape-url-enhanced/
│   │   │   └── route.ts
│   │   ├── scrape-website/
│   │   │   └── route.ts
│   │   └── search/
│   │       └── route.ts
│   ├── builder/
│   │   └── page.tsx
│   ├── generation/
│   │   └── page.tsx
│   ├── globals.css
│   ├── landing.tsx
│   ├── layout.tsx
│   └── page.tsx
├── atoms/
│   └── sheets.ts
├── colors.json
├── components/
│   ├── CodeApplicationProgress.tsx
│   ├── FirecrawlIcon.tsx
│   ├── FirecrawlLogo.tsx
│   ├── HMRErrorDetector.tsx
│   ├── HeroInput.tsx
│   ├── SandboxPreview.tsx
│   ├── app/
│   │   ├── (home)/
│   │   │   └── sections/
│   │   │       ├── ai-readiness/
│   │   │       │   ├── ControlPanel.tsx
│   │   │       │   ├── InlineResults.tsx
│   │   │       │   ├── MetricBars.tsx
│   │   │       │   ├── RadarChart.tsx
│   │   │       │   └── ScoreChart.tsx
│   │   │       ├── endpoints/
│   │   │       │   ├── EndpointsCrawl/
│   │   │       │   │   └── EndpointsCrawl.tsx
│   │   │       │   ├── EndpointsExtract/
│   │   │       │   │   └── EndpointsExtract.tsx
│   │   │       │   ├── EndpointsMap/
│   │   │       │   │   └── EndpointsMap.tsx
│   │   │       │   ├── EndpointsScrape/
│   │   │       │   │   └── EndpointsScrape.tsx
│   │   │       │   ├── EndpointsSearch/
│   │   │       │   │   └── EndpointsSearch.tsx
│   │   │       │   ├── Extract/
│   │   │       │   │   └── Extract.tsx
│   │   │       │   └── Mcp/
│   │   │       │       └── Mcp.tsx
│   │   │       ├── hero/
│   │   │       │   ├── Background/
│   │   │       │   │   ├── Background.tsx
│   │   │       │   │   ├── BackgroundOuterPiece.tsx
│   │   │       │   │   └── _svg/
│   │   │       │   │       └── CenterStar.tsx
│   │   │       │   ├── Badge/
│   │   │       │   │   └── Badge.tsx
│   │   │       │   ├── Hero.tsx
│   │   │       │   ├── Pixi/
│   │   │       │   │   ├── Pixi.tsx
│   │   │       │   │   └── tickers/
│   │   │       │   │       ├── ascii.ts
│   │   │       │   │       └── features/
│   │   │       │   │           ├── cell.ts
│   │   │       │   │           ├── cellReveal.ts
│   │   │       │   │           ├── components/
│   │   │       │   │           │   ├── AnimatedRect.ts
│   │   │       │   │           │   ├── BlinkingContainer.ts
│   │   │       │   │           │   └── Dot.ts
│   │   │       │   │           ├── crawl.ts
│   │   │       │   │           ├── index.ts
│   │   │       │   │           ├── mapping.ts
│   │   │       │   │           ├── scrape.ts
│   │   │       │   │           └── search.ts
│   │   │       │   └── Title/
│   │   │       │       └── Title.tsx
│   │   │       ├── hero-flame/
│   │   │       │   ├── HeroFlame.tsx
│   │   │       │   └── data.json
│   │   │       ├── hero-input/
│   │   │       │   ├── Button/
│   │   │       │   │   └── Button.tsx
│   │   │       │   ├── HeroInput.tsx
│   │   │       │   ├── Tabs/
│   │   │       │   │   ├── Mobile/
│   │   │       │   │   │   └── Mobile.tsx
│   │   │       │   │   └── Tabs.tsx
│   │   │       │   └── _svg/
│   │   │       │       ├── ArrowRight.tsx
│   │   │       │       └── Globe.tsx
│   │   │       └── hero-scraping/
│   │   │           ├── Code/
│   │   │           │   ├── Code.tsx
│   │   │           │   └── Loading/
│   │   │           │       ├── Loading.tsx
│   │   │           │       └── _svg/
│   │   │           │           └── Check.tsx
│   │   │           ├── HeroScraping.css
│   │   │           ├── HeroScraping.tsx
│   │   │           ├── Tag/
│   │   │           │   └── Tag.tsx
│   │   │           └── _svg/
│   │   │               ├── BrowserMobile.tsx
│   │   │               └── BrowserTab.tsx
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── home-page-components.md
│   │   └── generation/
│   │       ├── SidebarInput.tsx
│   │       └── SidebarQuickInput.tsx
│   ├── shared/
│   │   ├── Playground/
│   │   │   └── Context/
│   │   │       └── types.ts
│   │   ├── animated-dot-icon.tsx
│   │   ├── animated-height.tsx
│   │   ├── ascii-background.tsx
│   │   ├── ascii-flame-background.tsx
│   │   ├── button/
│   │   │   ├── Button.css
│   │   │   └── Button.tsx
│   │   ├── buttons/
│   │   │   ├── capsule-button.tsx
│   │   │   ├── fire-action-link.tsx
│   │   │   ├── index.ts
│   │   │   └── slate-button.tsx
│   │   ├── color-styles/
│   │   │   └── color-styles.tsx
│   │   ├── combobox/
│   │   │   └── combobox.tsx
│   │   ├── effects/
│   │   │   ├── .cursor/
│   │   │   │   └── rules/
│   │   │   │       └── flame-effects.md
│   │   │   ├── flame/
│   │   │   │   ├── Flame.tsx
│   │   │   │   ├── ascii-explosion.tsx
│   │   │   │   ├── auth-pulse/
│   │   │   │   │   ├── auth-pulse.tsx
│   │   │   │   │   └── pulse-data.json
│   │   │   │   ├── core-flame.json
│   │   │   │   ├── core-flame.tsx
│   │   │   │   ├── explosion-data.json
│   │   │   │   ├── flame-background.tsx
│   │   │   │   ├── hero-flame-data.json
│   │   │   │   ├── hero-flame.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── slate-grid/
│   │   │   │   │   ├── grid-data.json
│   │   │   │   │   └── slate-grid.tsx
│   │   │   │   ├── subtle-explosion.tsx
│   │   │   │   └── subtle-wave/
│   │   │   │       ├── subtle-wave.tsx
│   │   │   │       └── wave-data.json
│   │   │   ├── index.ts
│   │   │   └── subtle-ascii-animation.tsx
│   │   ├── firecrawl-icon/
│   │   │   ├── firecrawl-icon-static.tsx
│   │   │   └── firecrawl-icon.tsx
│   │   ├── header/
│   │   │   ├── BrandKit/
│   │   │   │   ├── BrandKit.tsx
│   │   │   │   └── _svg/
│   │   │   │       ├── Download.tsx
│   │   │   │       ├── Guidelines.tsx
│   │   │   │       └── Icon.tsx
│   │   │   ├── Dropdown/
│   │   │   │   ├── Content/
│   │   │   │   │   ├── Content.tsx
│   │   │   │   │   └── NavItemRow.tsx
│   │   │   │   ├── Github/
│   │   │   │   │   ├── Flame/
│   │   │   │   │   │   ├── Flame.tsx
│   │   │   │   │   │   └── data.json
│   │   │   │   │   └── Github.tsx
│   │   │   │   ├── Mobile/
│   │   │   │   │   ├── Item/
│   │   │   │   │   │   └── Item.tsx
│   │   │   │   │   └── Mobile.tsx
│   │   │   │   ├── Stories/
│   │   │   │   │   ├── Flame/
│   │   │   │   │   │   └── Flame.tsx
│   │   │   │   │   ├── Stories.tsx
│   │   │   │   │   └── _svg/
│   │   │   │   │       ├── ArrowUp.tsx
│   │   │   │   │       └── Replit.tsx
│   │   │   │   └── Wrapper/
│   │   │   │       └── Wrapper.tsx
│   │   │   ├── Github/
│   │   │   │   ├── GithubClient.tsx
│   │   │   │   └── _svg/
│   │   │   │       └── GithubIcon.tsx
│   │   │   ├── HeaderContext.tsx
│   │   │   ├── Nav/
│   │   │   │   ├── Item/
│   │   │   │   │   ├── Item.tsx
│   │   │   │   │   └── _svg/
│   │   │   │   │       └── ChevronDown.tsx
│   │   │   │   ├── Nav.tsx
│   │   │   │   ├── RenderEndpointIcon.tsx
│   │   │   │   └── _svg/
│   │   │   │       ├── Affiliate.tsx
│   │   │   │       ├── Api.tsx
│   │   │   │       ├── ArrowRight.tsx
│   │   │   │       ├── Careers.tsx
│   │   │   │       ├── Changelog.tsx
│   │   │   │       ├── Chats.tsx
│   │   │   │       ├── Lead.tsx
│   │   │   │       ├── MCP.tsx
│   │   │   │       ├── Platforms.tsx
│   │   │   │       ├── Research.tsx
│   │   │   │       ├── Student.tsx
│   │   │   │       └── Templates.tsx
│   │   │   ├── Toggle/
│   │   │   │   └── Toggle.tsx
│   │   │   ├── Wrapper/
│   │   │   │   └── Wrapper.tsx
│   │   │   └── _svg/
│   │   │       └── Logo.tsx
│   │   ├── hero-flame.tsx
│   │   ├── icons/
│   │   │   ├── GitHub.tsx
│   │   │   ├── Logo.tsx
│   │   │   ├── animated-chevron.tsx
│   │   │   ├── animated-icons.tsx
│   │   │   ├── arrow-animated.tsx
│   │   │   ├── check.tsx
│   │   │   ├── chevron-slide.tsx
│   │   │   ├── copied.tsx
│   │   │   ├── copy.tsx
│   │   │   ├── curve.tsx
│   │   │   ├── fingerprint-icon.tsx
│   │   │   ├── openai.tsx
│   │   │   ├── source-icon.tsx
│   │   │   ├── symbol-colored.tsx
│   │   │   ├── symbol-white.tsx
│   │   │   ├── tremor-placeholder.tsx
│   │   │   ├── wordmark-colored.tsx
│   │   │   └── wordmark-white.tsx
│   │   ├── image/
│   │   │   ├── Image.tsx
│   │   │   └── getImageSrc.ts
│   │   ├── layout/
│   │   │   ├── animated-height.tsx
│   │   │   ├── animated-width.tsx
│   │   │   ├── curvy-rect-divider.tsx
│   │   │   └── curvy-rect.tsx
│   │   ├── loading/
│   │   │   ├── Shimmer.tsx
│   │   │   └── usage-loading.tsx
│   │   ├── lockBody.tsx
│   │   ├── logo-cloud/
│   │   │   ├── index.ts
│   │   │   ├── logo-cloud.tsx
│   │   │   └── logo-cloud2/
│   │   │       ├── Logocloud.css
│   │   │       └── Logocloud.tsx
│   │   ├── notifications/
│   │   │   └── slack-notification.tsx
│   │   ├── pixi/
│   │   │   ├── Pixi.tsx
│   │   │   ├── PixiAssetManager.ts
│   │   │   └── utils.ts
│   │   ├── portal-to-body/
│   │   │   └── PortalToBody.tsx
│   │   ├── preview/
│   │   │   ├── json-error-highlighter.tsx
│   │   │   ├── live-preview-frame.tsx
│   │   │   ├── multiple-web-browsers.tsx
│   │   │   └── web-browser.tsx
│   │   ├── pylon.tsx
│   │   ├── search-params-provider/
│   │   │   └── search-params-provider.tsx
│   │   ├── section-head/
│   │   │   ├── SectionHead.css
│   │   │   └── SectionHead.tsx
│   │   ├── section-title/
│   │   │   └── SectionTitle.tsx
│   │   ├── tabs/
│   │   │   └── Tabs.tsx
│   │   ├── ui/
│   │   │   ├── app-dialog.tsx
│   │   │   ├── ascii-dot-loader.tsx
│   │   │   ├── dot-grid-loader.tsx
│   │   │   ├── empty-state.tsx
│   │   │   ├── index.ts
│   │   │   ├── loading-state.tsx
│   │   │   ├── mobile-sheet.tsx
│   │   │   └── stat-card.tsx
│   │   └── utils/
│   │       └── portal-to-body.tsx
│   └── ui/
│       ├── button.tsx
│       ├── checkbox.tsx
│       ├── code.tsx
│       ├── input.tsx
│       ├── label.tsx
│       ├── motion/
│       │   └── scramble-text.tsx
│       ├── select.tsx
│       ├── shadcn/
│       │   ├── accordion.tsx
│       │   ├── alert-dialog.tsx
│       │   ├── badge.tsx
│       │   ├── button.css
│       │   ├── button.tsx
│       │   ├── card.tsx
│       │   ├── checkbox.tsx
│       │   ├── collapsible.tsx
│       │   ├── combobox.tsx
│       │   ├── context-menu.tsx
│       │   ├── data-table.tsx
│       │   ├── dialog.tsx
│       │   ├── dropdown-menu.tsx
│       │   ├── form.tsx
│       │   ├── input.tsx
│       │   ├── label.tsx
│       │   ├── navigation-menu.tsx
│       │   ├── popover.tsx
│       │   ├── progress.tsx
│       │   ├── scroll-area.tsx
│       │   ├── select.tsx
│       │   ├── separator.tsx
│       │   ├── sheet.tsx
│       │   ├── slider.tsx
│       │   ├── switch.tsx
│       │   ├── tabs.tsx
│       │   ├── textarea.tsx
│       │   ├── toast.tsx
│       │   ├── toggle.tsx
│       │   ├── tooltip-radix.tsx
│       │   └── tooltip.tsx
│       ├── spinner.tsx
│       └── textarea.tsx
├── config/
│   └── app.config.ts
├── eslint.config.mjs
├── hooks/
│   ├── useDebouncedCallback.ts
│   ├── useDebouncedEffect.ts
│   └── useSwitchingCode.ts
├── lib/
│   ├── ai/
│   │   └── provider-manager.ts
│   ├── build-validator.ts
│   ├── context-selector.ts
│   ├── edit-examples.ts
│   ├── edit-intent-analyzer.ts
│   ├── file-parser.ts
│   ├── file-search-executor.ts
│   ├── icons.ts
│   ├── morph-fast-apply.ts
│   ├── sandbox/
│   │   ├── factory.ts
│   │   ├── providers/
│   │   │   ├── e2b-provider.ts
│   │   │   └── vercel-provider.ts
│   │   ├── sandbox-manager.ts
│   │   └── types.ts
│   └── utils.ts
├── next.config.ts
├── package.json
├── packages/
│   └── create-open-lovable/
│       ├── index.js
│       ├── lib/
│       │   ├── installer.js
│       │   └── prompts.js
│       ├── package.json
│       └── templates/
│           ├── e2b/
│           │   ├── .env.example
│           │   └── README.md
│           └── vercel/
│               ├── .env.example
│               └── README.md
├── postcss.config.mjs
├── public/
│   ├── compressor.json
│   └── firecrawl-logo
├── styles/
│   ├── additional-styles/
│   │   ├── custom-fonts.css
│   │   ├── theme.css
│   │   └── utility-patterns.css
│   ├── chrome-bug.css
│   ├── colors.json
│   ├── components/
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── component-styles.md
│   │   ├── button.css
│   │   ├── code.css
│   │   └── index.css
│   ├── design-system/
│   │   ├── .cursor/
│   │   │   └── rules/
│   │   │       └── design-system.md
│   │   ├── animations.css
│   │   ├── base/
│   │   │   ├── body.css
│   │   │   ├── layout.css
│   │   │   └── reset.css
│   │   ├── colors.css
│   │   ├── fonts.css
│   │   ├── typography.css
│   │   └── utilities.css
│   ├── fire.css
│   ├── inside-border-fix.css
│   └── main.css
├── tailwind.config.ts
├── tsconfig.json
├── types/
│   ├── conversation.ts
│   ├── file-manifest.ts
│   └── sandbox.ts
└── utils/
    ├── cn.ts
    ├── init-canvas.ts
    ├── set-timeout-on-visible.ts
    └── sleep.ts
Download .txt
SYMBOL INDEX (453 symbols across 228 files)

FILE: app/api/analyze-edit-intent/route.ts
  function POST (line 62) | async function POST(request: NextRequest) {

FILE: app/api/apply-ai-code-stream/route.ts
  type ParsedResponse (line 15) | interface ParsedResponse {
  function parseAIResponse (line 24) | function parseAIResponse(response: string): ParsedResponse {
  function POST (line 264) | async function POST(request: NextRequest) {

FILE: app/api/apply-ai-code/route.ts
  type ParsedResponse (line 10) | interface ParsedResponse {
  function parseAIResponse (line 19) | function parseAIResponse(response: string): ParsedResponse {
  function POST (line 137) | async function POST(request: NextRequest) {

FILE: app/api/check-vite-errors/route.ts
  function GET (line 6) | async function GET() {

FILE: app/api/clear-vite-errors-cache/route.ts
  function POST (line 7) | async function POST() {

FILE: app/api/conversation-state/route.ts
  function GET (line 9) | async function GET() {
  function POST (line 33) | async function POST(request: NextRequest) {
  function DELETE (line 143) | async function DELETE() {

FILE: app/api/create-ai-sandbox-v2/route.ts
  function POST (line 15) | async function POST() {

FILE: app/api/create-ai-sandbox/route.ts
  function POST (line 16) | async function POST() {
  function createSandboxInternal (line 64) | async function createSandboxInternal() {

FILE: app/api/create-zip/route.ts
  function POST (line 7) | async function POST() {

FILE: app/api/detect-and-install-packages/route.ts
  function POST (line 7) | async function POST(request: NextRequest) {

FILE: app/api/extract-brand-styles/route.ts
  function POST (line 3) | async function POST(request: NextRequest) {

FILE: app/api/generate-ai-code-stream/route.ts
  function analyzeUserPreferences (line 48) | function analyzeUserPreferences(messages: ConversationMessage[]): {
  function POST (line 91) | async function POST(request: NextRequest) {

FILE: app/api/get-sandbox-files/route.ts
  function GET (line 10) | async function GET() {
  function extractRoutes (line 174) | function extractRoutes(files: Record<string, FileInfo>): RouteInfo[] {

FILE: app/api/install-packages-v2/route.ts
  function POST (line 9) | async function POST(request: NextRequest) {

FILE: app/api/install-packages/route.ts
  function POST (line 9) | async function POST(request: NextRequest) {

FILE: app/api/kill-sandbox/route.ts
  function POST (line 9) | async function POST() {

FILE: app/api/monitor-vite-logs/route.ts
  function GET (line 7) | async function GET() {

FILE: app/api/report-vite-error/route.ts
  function POST (line 12) | async function POST(request: NextRequest) {

FILE: app/api/restart-vite/route.ts
  constant RESTART_COOLDOWN_MS (line 10) | const RESTART_COOLDOWN_MS = 5000;
  function POST (line 12) | async function POST() {

FILE: app/api/run-command-v2/route.ts
  function POST (line 10) | async function POST(request: NextRequest) {

FILE: app/api/run-command/route.ts
  function POST (line 8) | async function POST(request: NextRequest) {

FILE: app/api/sandbox-logs/route.ts
  function GET (line 7) | async function GET() {

FILE: app/api/sandbox-status/route.ts
  function GET (line 10) | async function GET() {

FILE: app/api/scrape-screenshot/route.ts
  function POST (line 4) | async function POST(req: NextRequest) {

FILE: app/api/scrape-url-enhanced/route.ts
  function sanitizeQuotes (line 4) | function sanitizeQuotes(text: string): string {
  function POST (line 19) | async function POST(request: NextRequest) {

FILE: app/api/scrape-website/route.ts
  function POST (line 4) | async function POST(request: NextRequest) {
  function OPTIONS (line 101) | async function OPTIONS() {

FILE: app/api/search/route.ts
  function POST (line 3) | async function POST(req: NextRequest) {

FILE: app/builder/page.tsx
  function BuilderPage (line 7) | function BuilderPage() {

FILE: app/generation/page.tsx
  type SandboxData (line 29) | interface SandboxData {
  type ChatMessage (line 35) | interface ChatMessage {
  type ScrapeData (line 50) | interface ScrapeData {
  function AISandboxPage (line 63) | function AISandboxPage() {
  function Page (line 3952) | function Page() {

FILE: app/landing.tsx
  function LandingPage (line 25) | function LandingPage() {

FILE: app/layout.tsx
  function RootLayout (line 33) | function RootLayout({

FILE: app/page.tsx
  type SearchResult (line 32) | interface SearchResult {
  function HomePage (line 40) | function HomePage() {

FILE: components/CodeApplicationProgress.tsx
  type CodeApplicationState (line 4) | interface CodeApplicationState {
  type CodeApplicationProgressProps (line 12) | interface CodeApplicationProgressProps {
  function CodeApplicationProgress (line 16) | function CodeApplicationProgress({ state }: CodeApplicationProgressProps) {

FILE: components/FirecrawlIcon.tsx
  function FirecrawlIcon (line 1) | function FirecrawlIcon({ className = "w-5 h-5" }: { className?: string }) {

FILE: components/FirecrawlLogo.tsx
  function FirecrawlLogo (line 1) | function FirecrawlLogo() {

FILE: components/HMRErrorDetector.tsx
  type HMRErrorDetectorProps (line 3) | interface HMRErrorDetectorProps {
  function HMRErrorDetector (line 8) | function HMRErrorDetector({ iframeRef, onErrorDetected }: HMRErrorDetect...

FILE: components/HeroInput.tsx
  type HeroInputProps (line 5) | interface HeroInputProps {
  function isURL (line 14) | function isURL(str: string): boolean {
  function HeroInput (line 20) | function HeroInput({

FILE: components/SandboxPreview.tsx
  type SandboxPreviewProps (line 4) | interface SandboxPreviewProps {
  function SandboxPreview (line 11) | function SandboxPreview({

FILE: components/app/(home)/sections/ai-readiness/ControlPanel.tsx
  type ControlPanelProps (line 29) | interface ControlPanelProps {
  type CheckItem (line 37) | interface CheckItem {
  function ControlPanel (line 50) | function ControlPanel({

FILE: components/app/(home)/sections/ai-readiness/InlineResults.tsx
  type InlineResultsProps (line 8) | interface InlineResultsProps {
  function InlineResults (line 35) | function InlineResults({

FILE: components/app/(home)/sections/ai-readiness/MetricBars.tsx
  type MetricBarsProps (line 7) | interface MetricBarsProps {
  function MetricBars (line 19) | function MetricBars({ metrics }: MetricBarsProps) {

FILE: components/app/(home)/sections/ai-readiness/RadarChart.tsx
  type RadarChartProps (line 6) | interface RadarChartProps {
  function RadarChart (line 15) | function RadarChart({ data, size = 300 }: RadarChartProps) {

FILE: components/app/(home)/sections/ai-readiness/ScoreChart.tsx
  type ScoreChartProps (line 6) | interface ScoreChartProps {
  function ScoreChart (line 12) | function ScoreChart({ score, enhanced = false, size = 200 }: ScoreChartP...

FILE: components/app/(home)/sections/endpoints/EndpointsCrawl/EndpointsCrawl.tsx
  function EndpointsCrawl (line 9) | function EndpointsCrawl({

FILE: components/app/(home)/sections/endpoints/EndpointsExtract/EndpointsExtract.tsx
  function EndpointsExtract (line 9) | function EndpointsExtract({

FILE: components/app/(home)/sections/endpoints/EndpointsMap/EndpointsMap.tsx
  function EndpointsMap (line 7) | function EndpointsMap(

FILE: components/app/(home)/sections/endpoints/EndpointsScrape/EndpointsScrape.tsx
  function EndpointsScrape (line 9) | function EndpointsScrape({

FILE: components/app/(home)/sections/endpoints/EndpointsSearch/EndpointsSearch.tsx
  function EndpointsSearch (line 7) | function EndpointsSearch({

FILE: components/app/(home)/sections/endpoints/Extract/Extract.tsx
  function EndpointsExtract (line 8) | function EndpointsExtract({ size = 20 }: { size?: number }) {

FILE: components/app/(home)/sections/endpoints/Mcp/Mcp.tsx
  function EndpointsMcp (line 9) | function EndpointsMcp({

FILE: components/app/(home)/sections/hero-flame/HeroFlame.tsx
  function HeroFlame (line 9) | function HeroFlame() {

FILE: components/app/(home)/sections/hero-input/Button/Button.tsx
  function HeroInputSubmitButton (line 7) | function HeroInputSubmitButton({

FILE: components/app/(home)/sections/hero-input/HeroInput.tsx
  function HeroInput (line 13) | function HeroInput() {

FILE: components/app/(home)/sections/hero-input/Tabs/Mobile/Mobile.tsx
  function HeroInputTabsMobile (line 8) | function HeroInputTabsMobile(props: {
  function MenuItems (line 103) | function MenuItems(props: {

FILE: components/app/(home)/sections/hero-input/Tabs/Tabs.tsx
  function HeroInputTabs (line 57) | function HeroInputTabs(props: {

FILE: components/app/(home)/sections/hero-input/_svg/ArrowRight.tsx
  function ArrowRight (line 1) | function ArrowRight() {

FILE: components/app/(home)/sections/hero-input/_svg/Globe.tsx
  function Globe (line 1) | function Globe() {

FILE: components/app/(home)/sections/hero-scraping/Code/Code.tsx
  constant URL (line 9) | const URL = {
  constant MARKDOWN (line 13) | const MARKDOWN = {
  constant TITLE (line 17) | const TITLE = {
  constant SCREENSHOT (line 21) | const SCREENSHOT = {
  function HeroScrapingCode (line 26) | function HeroScrapingCode({ step }: { step: number }) {

FILE: components/app/(home)/sections/hero-scraping/Code/Loading/Loading.tsx
  function HeroScrapingCodeLoading (line 8) | function HeroScrapingCodeLoading({

FILE: components/app/(home)/sections/hero-scraping/Code/Loading/_svg/Check.tsx
  function Check (line 1) | function Check() {

FILE: components/app/(home)/sections/hero-scraping/HeroScraping.tsx
  function HeroScraping (line 16) | function HeroScraping() {

FILE: components/app/(home)/sections/hero-scraping/Tag/Tag.tsx
  function HeroScrapingTag (line 7) | function HeroScrapingTag({

FILE: components/app/(home)/sections/hero-scraping/_svg/BrowserMobile.tsx
  function BrowserMobile (line 1) | function BrowserMobile(props: React.SVGProps<SVGSVGElement>) {

FILE: components/app/(home)/sections/hero-scraping/_svg/BrowserTab.tsx
  function BrowserTab (line 3) | function BrowserTab(attrs: HTMLAttributes<SVGSVGElement>) {

FILE: components/app/(home)/sections/hero/Background/Background.tsx
  function HomeHeroBackground (line 9) | function HomeHeroBackground() {

FILE: components/app/(home)/sections/hero/Background/_svg/CenterStar.tsx
  function CenterStar (line 1) | function CenterStar({

FILE: components/app/(home)/sections/hero/Badge/Badge.tsx
  function HomeHeroBadge (line 3) | function HomeHeroBadge() {

FILE: components/app/(home)/sections/hero/Hero.tsx
  function HomeHero (line 14) | function HomeHero() {

FILE: components/app/(home)/sections/hero/Pixi/Pixi.tsx
  function PixiContent (line 8) | function PixiContent() {
  function HomeHeroPixi (line 22) | function HomeHeroPixi() {

FILE: components/app/(home)/sections/hero/Pixi/tickers/ascii.ts
  constant ASCII_CHARS (line 7) | const ASCII_CHARS = ' .":,-_^=+';
  function getAsciiChar (line 9) | function getAsciiChar(luminance: number) {
  function sprinkleAscii (line 24) | function sprinkleAscii(line: string) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/cell.ts
  type Props (line 10) | type Props = Parameters<Ticker>[0] & {
  constant CELL_SIZE (line 15) | const CELL_SIZE = 80;
  constant MAIN_COLOR (line 17) | const MAIN_COLOR = 0xe6e6e6;
  function cell (line 23) | function cell(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/cellReveal.ts
  type Props (line 4) | type Props = Parameters<Ticker>[0] & {
  function cellReveal (line 9) | function cellReveal(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/components/AnimatedRect.ts
  type Props (line 7) | type Props = {
  type IAnimatedRect (line 24) | type IAnimatedRect = ReturnType<typeof AnimatedRect>;
  function AnimatedRect (line 26) | function AnimatedRect(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/components/BlinkingContainer.ts
  type IBlinkingContainer (line 8) | type IBlinkingContainer = ReturnType<typeof BlinkingContainer>;
  function BlinkingContainer (line 10) | function BlinkingContainer({

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/components/Dot.ts
  function Dot (line 5) | function Dot(

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/crawl.ts
  type Props (line 11) | type Props = Parameters<Ticker>[0] & {
  function crawl (line 18) | async function crawl(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/index.ts
  constant CELL_GRID (line 7) | const CELL_GRID = [
  constant REVEAL_ANIMATION_GRID (line 15) | const REVEAL_ANIMATION_GRID = [

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/mapping.ts
  type Props (line 9) | type Props = Parameters<Ticker>[0] & {
  function mapping (line 16) | async function mapping(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/scrape.ts
  type Props (line 9) | type Props = Parameters<Ticker>[0] & {
  function scrape (line 16) | async function scrape(props: Props) {

FILE: components/app/(home)/sections/hero/Pixi/tickers/features/search.ts
  type Props (line 9) | type Props = Parameters<Ticker>[0] & {
  function search (line 16) | async function search(props: Props) {

FILE: components/app/(home)/sections/hero/Title/Title.tsx
  type Options (line 9) | type Options = {
  function HomeHeroTitle (line 257) | function HomeHeroTitle() {

FILE: components/app/generation/SidebarInput.tsx
  type SidebarInputProps (line 7) | interface SidebarInputProps {
  function SidebarInput (line 12) | function SidebarInput({ onSubmit, disabled = false }: SidebarInputProps) {

FILE: components/app/generation/SidebarQuickInput.tsx
  type SidebarQuickInputProps (line 6) | interface SidebarQuickInputProps {
  function SidebarQuickInput (line 11) | function SidebarQuickInput({ onSubmit, disabled = false }: SidebarQuickI...

FILE: components/shared/Playground/Context/types.ts
  type Endpoint (line 1) | enum Endpoint {
  type AgentModel (line 9) | enum AgentModel {
  type FormatType (line 13) | enum FormatType {
  type SearchFormatType (line 24) | enum SearchFormatType {
  type Prev (line 30) | type Prev = [never, 0, 1, 2, 3, 4, 5];
  type Join (line 32) | type Join<K, P> = K extends string | number
  type Paths (line 38) | type Paths<T, D extends number = 5> = [D] extends [never]

FILE: components/shared/animated-dot-icon.tsx
  type AnimatedDotIconProps (line 7) | interface AnimatedDotIconProps {
  function AnimatedDotIcon (line 225) | function AnimatedDotIcon({

FILE: components/shared/animated-height.tsx
  function AnimatedHeight (line 9) | function AnimatedHeight({

FILE: components/shared/ascii-background.tsx
  type AsciiBackgroundProps (line 29) | interface AsciiBackgroundProps {
  function AsciiBackground (line 34) | function AsciiBackground({

FILE: components/shared/ascii-flame-background.tsx
  type AsciiFlameBackgroundProps (line 8) | interface AsciiFlameBackgroundProps {
  function AsciiFlameBackground (line 16) | function AsciiFlameBackground({

FILE: components/shared/button/Button.tsx
  type Props (line 5) | interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
  function Button (line 11) | function Button({

FILE: components/shared/buttons/capsule-button.tsx
  type CapsuleButtonProps (line 9) | interface CapsuleButtonProps
  function CapsuleButton (line 20) | function CapsuleButton({

FILE: components/shared/buttons/fire-action-link.tsx
  type FireActionLinkProps (line 4) | interface FireActionLinkProps {
  function FireActionLink (line 12) | function FireActionLink({

FILE: components/shared/buttons/slate-button.tsx
  type SlateButtonProps (line 7) | interface SlateButtonProps

FILE: components/shared/color-styles/color-styles.tsx
  constant TYPED_COLORS (line 3) | const TYPED_COLORS = colors as unknown as Record<
  function ColorStyles (line 29) | function ColorStyles() {

FILE: components/shared/combobox/combobox.tsx
  function Combobox (line 8) | function Combobox({

FILE: components/shared/effects/flame/Flame.tsx
  function CoreFlame (line 10) | function CoreFlame(attrs: HTMLAttributes<HTMLDivElement>) {

FILE: components/shared/effects/flame/ascii-explosion.tsx
  function AsciiExplosion (line 10) | function AsciiExplosion(attrs: HTMLAttributes<HTMLDivElement>) {

FILE: components/shared/effects/flame/auth-pulse/auth-pulse.tsx
  type AuthPulseProps (line 8) | interface AuthPulseProps extends HTMLAttributes<HTMLDivElement> {
  function AuthPulse (line 13) | function AuthPulse({

FILE: components/shared/effects/flame/core-flame.tsx
  function CoreFlame (line 8) | function CoreFlame(attrs: HTMLAttributes<HTMLDivElement>) {

FILE: components/shared/effects/flame/flame-background.tsx
  type FlameBackgroundProps (line 7) | interface FlameBackgroundProps {
  function FlameBackground (line 14) | function FlameBackground({

FILE: components/shared/effects/flame/hero-flame.tsx
  function HeroFlame (line 8) | function HeroFlame() {

FILE: components/shared/effects/flame/slate-grid/slate-grid.tsx
  type SlateGridProps (line 8) | interface SlateGridProps extends HTMLAttributes<HTMLDivElement> {
  function SlateGrid (line 13) | function SlateGrid({

FILE: components/shared/effects/flame/subtle-explosion.tsx
  type SubtleExplosionProps (line 8) | interface SubtleExplosionProps extends HTMLAttributes<HTMLDivElement> {
  function SubtleExplosion (line 14) | function SubtleExplosion({

FILE: components/shared/effects/flame/subtle-wave/subtle-wave.tsx
  function SubtleWave (line 7) | function SubtleWave({ className = "" }: { className?: string }) {

FILE: components/shared/effects/subtle-ascii-animation.tsx
  function SubtleAsciiAnimation (line 6) | function SubtleAsciiAnimation({

FILE: components/shared/firecrawl-icon/firecrawl-icon-static.tsx
  function FirecrawlIconStatic (line 3) | function FirecrawlIconStatic({

FILE: components/shared/firecrawl-icon/firecrawl-icon.tsx
  function FirecrawlIcon (line 3) | function FirecrawlIcon({

FILE: components/shared/header/BrandKit/BrandKit.tsx
  function HeaderBrandKit (line 18) | function HeaderBrandKit() {

FILE: components/shared/header/BrandKit/_svg/Download.tsx
  function Download (line 1) | function Download() {

FILE: components/shared/header/BrandKit/_svg/Guidelines.tsx
  function Guidelines (line 1) | function Guidelines() {

FILE: components/shared/header/BrandKit/_svg/Icon.tsx
  function Icon (line 1) | function Icon() {

FILE: components/shared/header/Dropdown/Content/Content.tsx
  type Props (line 5) | interface Props {
  function HeaderDropdownContent (line 31) | function HeaderDropdownContent({

FILE: components/shared/header/Dropdown/Content/NavItemRow.tsx
  type NavItemRowProps (line 4) | interface NavItemRowProps {
  function NavItemRow (line 14) | function NavItemRow({
  type NavItemRowBigProps (line 45) | interface NavItemRowBigProps extends Omit<NavItemRowProps, "target"> {
  function NavItemRowBig (line 49) | function NavItemRowBig({

FILE: components/shared/header/Dropdown/Github/Flame/Flame.tsx
  function GithubFlame (line 10) | function GithubFlame(attrs: HTMLAttributes<HTMLDivElement>) {

FILE: components/shared/header/Dropdown/Github/Github.tsx
  function HeaderDropdownGithub (line 5) | function HeaderDropdownGithub() {

FILE: components/shared/header/Dropdown/Mobile/Item/Item.tsx
  function HeaderDropdownMobileItem (line 12) | function HeaderDropdownMobileItem({

FILE: components/shared/header/Dropdown/Mobile/Mobile.tsx
  function HeaderDropdownMobile (line 15) | function HeaderDropdownMobile({

FILE: components/shared/header/Dropdown/Stories/Flame/Flame.tsx
  function StoriesFlame (line 9) | function StoriesFlame(attrs: HTMLAttributes<HTMLDivElement>) {

FILE: components/shared/header/Dropdown/Stories/Stories.tsx
  function HeaderDropdownStories (line 5) | function HeaderDropdownStories() {

FILE: components/shared/header/Dropdown/Stories/_svg/ArrowUp.tsx
  function ArrowUp (line 1) | function ArrowUp() {

FILE: components/shared/header/Dropdown/Stories/_svg/Replit.tsx
  function Replit (line 1) | function Replit() {

FILE: components/shared/header/Dropdown/Wrapper/Wrapper.tsx
  function HeaderDropdownWrapper (line 10) | function HeaderDropdownWrapper() {

FILE: components/shared/header/Github/GithubClient.tsx
  function HeaderGithubClient (line 6) | function HeaderGithubClient() {

FILE: components/shared/header/Github/_svg/GithubIcon.tsx
  function GithubIcon (line 1) | function GithubIcon() {

FILE: components/shared/header/HeaderContext.tsx
  type HeaderContextType (line 12) | interface HeaderContextType {

FILE: components/shared/header/Nav/Item/Item.tsx
  function HeaderNavItem (line 10) | function HeaderNavItem({

FILE: components/shared/header/Nav/Item/_svg/ChevronDown.tsx
  function ChevronDown (line 1) | function ChevronDown() {

FILE: components/shared/header/Nav/Nav.tsx
  function HeaderNav (line 28) | function HeaderNav() {
  constant NAV_ITEMS (line 38) | const NAV_ITEMS = [

FILE: components/shared/header/Nav/_svg/Affiliate.tsx
  function Affiliate (line 1) | function Affiliate() {

FILE: components/shared/header/Nav/_svg/Api.tsx
  function Api (line 1) | function Api() {

FILE: components/shared/header/Nav/_svg/ArrowRight.tsx
  function ArrowRight (line 1) | function ArrowRight() {

FILE: components/shared/header/Nav/_svg/Careers.tsx
  function Careers (line 1) | function Careers() {

FILE: components/shared/header/Nav/_svg/Changelog.tsx
  function Changelog (line 1) | function Changelog() {

FILE: components/shared/header/Nav/_svg/Chats.tsx
  function Ai (line 1) | function Ai() {

FILE: components/shared/header/Nav/_svg/Lead.tsx
  function Lead (line 1) | function Lead() {

FILE: components/shared/header/Nav/_svg/MCP.tsx
  function MCPIcon (line 1) | function MCPIcon() {

FILE: components/shared/header/Nav/_svg/Platforms.tsx
  function Platforms (line 1) | function Platforms() {

FILE: components/shared/header/Nav/_svg/Research.tsx
  function Research (line 1) | function Research() {

FILE: components/shared/header/Nav/_svg/Student.tsx
  function Student (line 1) | function Student() {

FILE: components/shared/header/Nav/_svg/Templates.tsx
  function Templates (line 1) | function Templates() {

FILE: components/shared/header/Toggle/Toggle.tsx
  function HeaderToggle (line 7) | function HeaderToggle({

FILE: components/shared/header/Wrapper/Wrapper.tsx
  function HeaderWrapper (line 8) | function HeaderWrapper({

FILE: components/shared/header/_svg/Logo.tsx
  function Logo (line 1) | function Logo() {

FILE: components/shared/hero-flame.tsx
  type HeroFlameProps (line 66) | interface HeroFlameProps {
  function HeroFlame (line 71) | function HeroFlame({ className, size = "medium" }: HeroFlameProps) {

FILE: components/shared/icons/animated-chevron.tsx
  type AnimatedChevronProps (line 8) | interface AnimatedChevronProps {
  function AnimatedChevron (line 14) | function AnimatedChevron({

FILE: components/shared/icons/arrow-animated.tsx
  function ArrowAnimated (line 3) | function ArrowAnimated({

FILE: components/shared/icons/check.tsx
  function Check (line 1) | function Check() {

FILE: components/shared/icons/chevron-slide.tsx
  type Direction (line 6) | type Direction = "left" | "right";
  type ChevronSlideProps (line 8) | interface ChevronSlideProps extends React.SVGAttributes<SVGElement> {
  function ChevronSlide (line 13) | function ChevronSlide({

FILE: components/shared/icons/copied.tsx
  function CopiedIcon (line 1) | function CopiedIcon() {

FILE: components/shared/icons/copy.tsx
  function CopyIcon (line 1) | function CopyIcon() {

FILE: components/shared/icons/curve.tsx
  type CurveProps (line 1) | interface CurveProps extends React.SVGAttributes<SVGSVGElement> {
  function Curve (line 5) | function Curve({

FILE: components/shared/icons/fingerprint-icon.tsx
  type FingerprintIconHandle (line 15) | interface FingerprintIconHandle {
  type FingerprintIconProps (line 20) | interface FingerprintIconProps extends HTMLAttributes<HTMLDivElement> {

FILE: components/shared/icons/openai.tsx
  function IconOpenai (line 3) | function IconOpenai(props: React.SVGProps<SVGSVGElement>) {

FILE: components/shared/image/Image.tsx
  type Props (line 6) | interface Props extends ComponentProps<"img"> {
  constant BASE_SRC (line 12) | const BASE_SRC = "/assets/";
  constant RAW_SRC (line 13) | const RAW_SRC = "/assets-original/";
  function Image (line 15) | function Image({ src, raw, ...attrs }: Props) {

FILE: components/shared/image/getImageSrc.ts
  function getImageSrc (line 10) | async function getImageSrc(src: string) {
  function supportsEncode (line 22) | async function supportsEncode() {

FILE: components/shared/layout/animated-height.tsx
  type AnimatedHeight (line 8) | type AnimatedHeight = {
  function AnimatedHeight (line 17) | function AnimatedHeight({ children, ...attrs }: AnimatedHeight) {

FILE: components/shared/layout/animated-width.tsx
  type AnimatedWidthProps (line 6) | type AnimatedWidthProps = {
  function AnimatedWidth (line 13) | function AnimatedWidth({

FILE: components/shared/layout/curvy-rect-divider.tsx
  function CurvyRectDivider (line 5) | function CurvyRectDivider() {

FILE: components/shared/layout/curvy-rect.tsx
  type CurvyRectProps (line 5) | interface CurvyRectProps extends React.HTMLAttributes<HTMLDivElement> {
  function CurvyRect (line 22) | function CurvyRect({

FILE: components/shared/loading/usage-loading.tsx
  function UsageLoadingText (line 6) | function UsageLoadingText({ text = "Loading..." }: { text?: string }) {

FILE: components/shared/lockBody.tsx
  function lockBody (line 11) | function lockBody(

FILE: components/shared/logo-cloud/logo-cloud.tsx
  function LogoCloud (line 1) | function LogoCloud() {

FILE: components/shared/logo-cloud/logo-cloud2/Logocloud.tsx
  function Logocloud (line 5) | function Logocloud() {

FILE: components/shared/notifications/slack-notification.tsx
  function SlackNotification (line 6) | function SlackNotification({

FILE: components/shared/pixi/Pixi.tsx
  type TickerResult (line 12) | type TickerResult = void;
  type Ticker (line 14) | type Ticker = ({
  type PixiProps (line 22) | interface PixiProps {
  function Pixi (line 33) | function Pixi({

FILE: components/shared/pixi/PixiAssetManager.ts
  class PixiAssetManager (line 3) | class PixiAssetManager {
    method load (line 9) | public static load<T = any>(...sources: string[]): Promise<T> {

FILE: components/shared/portal-to-body/PortalToBody.tsx
  function PortalToBody (line 4) | function PortalToBody({ children }: { children: React.ReactNode }) {

FILE: components/shared/preview/json-error-highlighter.tsx
  function JsonErrorHighlighter (line 4) | function JsonErrorHighlighter({

FILE: components/shared/preview/live-preview-frame.tsx
  function LivePreviewFrame (line 5) | function LivePreviewFrame({

FILE: components/shared/preview/multiple-web-browsers.tsx
  type BrowserData (line 8) | type BrowserData = {
  function MultipleWebBrowsers (line 15) | function MultipleWebBrowsers({

FILE: components/shared/preview/web-browser.tsx
  function WebBrowser (line 4) | function WebBrowser({

FILE: components/shared/pylon.tsx
  type PylonChatProps (line 8) | interface PylonChatProps {

FILE: components/shared/search-params-provider/search-params-provider.tsx
  type SearchParamsContextType (line 5) | type SearchParamsContextType = { [key: string]: string | string[] | unde...

FILE: components/shared/section-head/SectionHead.tsx
  type SectionHeadProps (line 7) | type SectionHeadProps = {
  function SectionHead (line 21) | function SectionHead({

FILE: components/shared/section-title/SectionTitle.tsx
  function SectionTitle (line 7) | function SectionTitle({

FILE: components/shared/tabs/Tabs.tsx
  type Props (line 7) | type Props = {
  function Tabs (line 23) | function Tabs({

FILE: components/shared/ui/app-dialog.tsx
  type AppDialogContentProps (line 20) | type AppDialogContentProps = React.ComponentPropsWithoutRef<
  function AppDialogContent (line 27) | function AppDialogContent({

FILE: components/shared/ui/ascii-dot-loader.tsx
  type AsciiDotLoaderProps (line 6) | interface AsciiDotLoaderProps {
  function AsciiDotLoader (line 14) | function AsciiDotLoader({

FILE: components/shared/ui/dot-grid-loader.tsx
  type DotGridLoaderProps (line 6) | interface DotGridLoaderProps {
  function DotGridLoader (line 15) | function DotGridLoader({

FILE: components/shared/ui/empty-state.tsx
  type EmptyStateProps (line 7) | interface EmptyStateProps {
  function EmptyState (line 16) | function EmptyState({

FILE: components/shared/ui/loading-state.tsx
  type LoadingStateProps (line 7) | interface LoadingStateProps {
  function LoadingState (line 14) | function LoadingState({

FILE: components/shared/ui/mobile-sheet.tsx
  type MobileSheetProps (line 10) | interface MobileSheetProps {
  function useAutoCloseOnDesktop (line 25) | function useAutoCloseOnDesktop(isOpen: boolean, onClose: () => void) {
  function MobileSheet (line 47) | function MobileSheet({

FILE: components/shared/ui/stat-card.tsx
  type StatCardProps (line 7) | interface StatCardProps {
  function StatCard (line 18) | function StatCard({

FILE: components/shared/utils/portal-to-body.tsx
  function PortalToBody (line 4) | function PortalToBody({ children }: { children: React.ReactNode }) {

FILE: components/ui/button.tsx
  type ButtonProps (line 32) | interface ButtonProps

FILE: components/ui/checkbox.tsx
  type CheckboxProps (line 6) | interface CheckboxProps {

FILE: components/ui/code.tsx
  type CodeProps (line 3) | interface CodeProps {
  function Code (line 9) | function Code({ code, language = 'json', className = '' }: CodeProps) {

FILE: components/ui/input.tsx
  type InputProps (line 5) | type InputProps = React.InputHTMLAttributes<HTMLInputElement>

FILE: components/ui/motion/scramble-text.tsx
  type ScrambleTextProps (line 3) | interface ScrambleTextProps {
  function ScrambleText (line 11) | function ScrambleText({ text, className = '', duration = 1, delay = 0, i...

FILE: components/ui/select.tsx
  type SelectProps (line 5) | type SelectProps = React.SelectHTMLAttributes<HTMLSelectElement>

FILE: components/ui/shadcn/badge.tsx
  function Badge (line 6) | function Badge({ ...attrs }: HTMLAttributes<HTMLDivElement>) {

FILE: components/ui/shadcn/button.tsx
  type Props (line 7) | interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {

FILE: components/ui/shadcn/checkbox.tsx
  function Checkbox (line 5) | function Checkbox({

FILE: components/ui/shadcn/combobox.tsx
  function Combobox (line 8) | function Combobox({

FILE: components/ui/shadcn/form.tsx
  type FormFieldContextValue (line 20) | type FormFieldContextValue<
  type FormItemContextValue (line 67) | type FormItemContextValue = {

FILE: components/ui/shadcn/sheet.tsx
  type SheetContentProps (line 52) | interface SheetContentProps

FILE: components/ui/shadcn/switch.tsx
  type SwitchProps (line 40) | interface SwitchProps

FILE: components/ui/shadcn/textarea.tsx
  function Textarea (line 3) | function Textarea(

FILE: components/ui/shadcn/toast.tsx
  type ToasterProps (line 6) | type ToasterProps = React.ComponentProps<typeof Sonner>;

FILE: components/ui/shadcn/toggle.tsx
  function Toggle (line 6) | function Toggle({

FILE: components/ui/shadcn/tooltip.tsx
  function Tooltip (line 10) | function Tooltip({

FILE: components/ui/spinner.tsx
  type SpinnerProps (line 3) | interface SpinnerProps {
  function Spinner (line 9) | function Spinner({ className = '', size = 'md', finished = false }: Spin...

FILE: components/ui/textarea.tsx
  type TextareaProps (line 5) | type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>

FILE: config/app.config.ts
  method timeoutMs (line 11) | get timeoutMs() {
  method timeoutMs (line 37) | get timeoutMs() {
  function getConfig (line 187) | function getConfig<K extends keyof typeof appConfig>(key: K): typeof app...
  function getConfigValue (line 192) | function getConfigValue(path: string): any {

FILE: hooks/useDebouncedCallback.ts
  constant DEFAULT_CONFIG (line 5) | const DEFAULT_CONFIG = { timeout: 0 };
  function useDebouncedCallback (line 7) | function useDebouncedCallback<T extends (...args: any[]) => any>(

FILE: hooks/useDebouncedEffect.ts
  constant DEFAULT_CONFIG (line 5) | const DEFAULT_CONFIG = {
  function useDebouncedEffect (line 10) | function useDebouncedEffect(

FILE: hooks/useSwitchingCode.ts
  function useSwitchingCode (line 6) | function useSwitchingCode(code: string, ms = 20, progress = 1, fill = tr...

FILE: lib/ai/provider-manager.ts
  type ProviderName (line 7) | type ProviderName = 'openai' | 'anthropic' | 'groq' | 'google';
  type ProviderClient (line 10) | type ProviderClient =
  type ProviderResolution (line 16) | interface ProviderResolution {
  function getEnvDefaults (line 28) | function getEnvDefaults(provider: ProviderName): { apiKey?: string; base...
  function getOrCreateClient (line 48) | function getOrCreateClient(provider: ProviderName, apiKey?: string, base...
  function getProviderForModel (line 79) | function getProviderForModel(modelId: string): ProviderResolution {

FILE: lib/build-validator.ts
  type BuildValidation (line 1) | interface BuildValidation {
  function validateBuild (line 12) | async function validateBuild(sandboxUrl: string, sandboxId: string): Pro...
  function extractMissingPackages (line 86) | function extractMissingPackages(error: any): string[] {
  type ErrorType (line 114) | type ErrorType = 'missing-package' | 'syntax-error' | 'sandbox-timeout' ...
  function classifyError (line 116) | function classifyError(error: any): ErrorType {
  function calculateRetryDelay (line 154) | function calculateRetryDelay(attempt: number, errorType: ErrorType): num...

FILE: lib/context-selector.ts
  type FileContext (line 5) | interface FileContext {
  function selectFilesForEdit (line 15) | function selectFilesForEdit(
  function buildSystemPrompt (line 76) | function buildSystemPrompt(
  function buildFileStructureSection (line 138) | function buildFileStructureSection(manifest: FileManifest): string {
  function buildEditInstructions (line 188) | function buildEditInstructions(editType: EditType): string {
  function buildComponentRelationships (line 253) | function buildComponentRelationships(
  function getFileContents (line 289) | async function getFileContents(
  function formatFilesForAI (line 308) | function formatFilesForAI(
  function getFileExtension (line 352) | function getFileExtension(path: string): string {

FILE: lib/edit-examples.ts
  constant EDIT_EXAMPLES (line 5) | const EDIT_EXAMPLES = `
  function getEditExamplesPrompt (line 228) | function getEditExamplesPrompt(): string {
  function getComponentPatternPrompt (line 232) | function getComponentPatternPrompt(fileStructure: string): string {

FILE: lib/edit-intent-analyzer.ts
  function analyzeEditIntent (line 6) | function analyzeEditIntent(
  function findComponentFiles (line 124) | function findComponentFiles(prompt: string, manifest: FileManifest): str...
  function findFeatureInsertionPoints (line 188) | function findFeatureInsertionPoints(prompt: string, manifest: FileManife...
  function findProblemFiles (line 244) | function findProblemFiles(prompt: string, manifest: FileManifest): strin...
  function findStyleFiles (line 267) | function findStyleFiles(prompt: string, manifest: FileManifest): string[] {
  function findRefactorTargets (line 289) | function findRefactorTargets(prompt: string, manifest: FileManifest): st...
  function findPackageFiles (line 297) | function findPackageFiles(manifest: FileManifest): string[] {
  function findComponentByContent (line 314) | function findComponentByContent(prompt: string, manifest: FileManifest):...
  function extractComponentNames (line 363) | function extractComponentNames(prompt: string): string[] {
  function getSuggestedContext (line 386) | function getSuggestedContext(
  function resolveImportPath (line 398) | function resolveImportPath(
  function resolveRelativePath (line 436) | function resolveRelativePath(fromDir: string, relativePath: string): str...
  function calculateConfidence (line 454) | function calculateConfidence(
  function generateDescription (line 485) | function generateDescription(

FILE: lib/file-parser.ts
  function parseJavaScriptFile (line 6) | function parseJavaScriptFile(content: string, filePath: string): Partial...
  function extractImports (line 23) | function extractImports(content: string): ImportInfo[] {
  function extractExports (line 64) | function extractExports(content: string): string[] {
  function extractComponentInfo (line 104) | function extractComponentInfo(content: string, filePath: string): Compon...
  function determineFileType (line 170) | function determineFileType(
  function buildComponentTree (line 224) | function buildComponentTree(files: Record<string, FileInfo>) {

FILE: lib/file-search-executor.ts
  type SearchResult (line 6) | interface SearchResult {
  type SearchPlan (line 17) | interface SearchPlan {
  type SearchExecutionResult (line 30) | interface SearchExecutionResult {
  function executeSearchPlan (line 42) | function executeSearchPlan(
  function formatSearchResultsForAI (line 169) | function formatSearchResultsForAI(results: SearchResult[]): string {
  function selectTargetFile (line 226) | function selectTargetFile(

FILE: lib/morph-fast-apply.ts
  type MorphEditBlock (line 3) | interface MorphEditBlock {
  type MorphApplyResult (line 9) | interface MorphApplyResult {
  function normalizeProjectPath (line 17) | function normalizeProjectPath(inputPath: string): { normalizedPath: stri...
  function morphChatCompletionsCreate (line 42) | async function morphChatCompletionsCreate(payload: any) {
  function parseMorphEdits (line 60) | function parseMorphEdits(text: string): MorphEditBlock[] {
  function readFileFromSandbox (line 79) | async function readFileFromSandbox(sandbox: any, normalizedPath: string,...
  function writeFileToSandbox (line 119) | async function writeFileToSandbox(sandbox: any, normalizedPath: string, ...
  function applyMorphEditToFile (line 180) | async function applyMorphEditToFile(params: {

FILE: lib/sandbox/factory.ts
  class SandboxFactory (line 5) | class SandboxFactory {
    method create (line 6) | static create(provider?: string, config?: SandboxProviderConfig): Sand...
    method getAvailableProviders (line 23) | static getAvailableProviders(): string[] {
    method isProviderAvailable (line 27) | static isProviderAvailable(provider: string): boolean {

FILE: lib/sandbox/providers/e2b-provider.ts
  class E2BProvider (line 6) | class E2BProvider extends SandboxProvider {
    method reconnect (line 12) | async reconnect(sandboxId: string): Promise<boolean> {
    method createSandbox (line 27) | async createSandbox(): Promise<SandboxInfo> {
    method runCommand (line 73) | async runCommand(command: string): Promise<CommandResult> {
    method writeFile (line 108) | async writeFile(path: string, content: string): Promise<void> {
    method readFile (line 139) | async readFile(path: string): Promise<string> {
    method listFiles (line 155) | async listFiles(directory: string = '/home/user/app'): Promise<string[...
    method installPackages (line 185) | async installPackages(packages: string[]): Promise<CommandResult> {
    method setupViteApp (line 231) | async setupViteApp(): Promise<void> {
    method restartViteServer (line 454) | async restartViteServer(): Promise<void> {
    method getSandboxUrl (line 489) | getSandboxUrl(): string | null {
    method getSandboxInfo (line 493) | getSandboxInfo(): SandboxInfo | null {
    method terminate (line 497) | async terminate(): Promise<void> {
    method isAlive (line 509) | isAlive(): boolean {

FILE: lib/sandbox/providers/vercel-provider.ts
  class VercelProvider (line 5) | class VercelProvider extends SandboxProvider {
    method createSandbox (line 8) | async createSandbox(): Promise<SandboxInfo> {
    method runCommand (line 64) | async runCommand(command: string): Promise<CommandResult> {
    method writeFile (line 124) | async writeFile(path: string, content: string): Promise<void> {
    method readFile (line 191) | async readFile(path: string): Promise<string> {
    method listFiles (line 235) | async listFiles(directory: string = '/vercel/sandbox'): Promise<string...
    method installPackages (line 266) | async installPackages(packages: string[]): Promise<CommandResult> {
    method setupViteApp (line 325) | async setupViteApp(): Promise<void> {
    method restartViteServer (line 547) | async restartViteServer(): Promise<void> {
    method getSandboxUrl (line 577) | getSandboxUrl(): string | null {
    method getSandboxInfo (line 581) | getSandboxInfo(): SandboxInfo | null {
    method terminate (line 585) | async terminate(): Promise<void> {
    method isAlive (line 597) | isAlive(): boolean {

FILE: lib/sandbox/sandbox-manager.ts
  type SandboxInfo (line 4) | interface SandboxInfo {
  class SandboxManager (line 11) | class SandboxManager {
    method getOrCreateProvider (line 18) | async getOrCreateProvider(sandboxId: string): Promise<SandboxProvider> {
    method registerSandbox (line 59) | registerSandbox(sandboxId: string, provider: SandboxProvider): void {
    method getActiveProvider (line 72) | getActiveProvider(): SandboxProvider | null {
    method getProvider (line 89) | getProvider(sandboxId: string): SandboxProvider | null {
    method setActiveSandbox (line 101) | setActiveSandbox(sandboxId: string): boolean {
    method terminateSandbox (line 112) | async terminateSandbox(sandboxId: string): Promise<void> {
    method terminateAll (line 131) | async terminateAll(): Promise<void> {
    method cleanup (line 146) | async cleanup(maxAge: number = 3600000): Promise<void> {

FILE: lib/sandbox/types.ts
  type SandboxFile (line 1) | interface SandboxFile {
  type SandboxInfo (line 7) | interface SandboxInfo {
  type CommandResult (line 14) | interface CommandResult {
  type SandboxProviderConfig (line 21) | interface SandboxProviderConfig {
  method constructor (line 40) | constructor(config: SandboxProviderConfig) {
  method setupViteApp (line 56) | async setupViteApp(): Promise<void> {
  method restartViteServer (line 61) | async restartViteServer(): Promise<void> {

FILE: lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: packages/create-open-lovable/index.js
  function main (line 30) | async function main() {

FILE: packages/create-open-lovable/lib/installer.js
  function installer (line 8) | async function installer(config) {
  function copyTemplate (line 82) | async function copyTemplate(src, dest) {
  function copyMainProject (line 100) | async function copyMainProject(mainProjectPath, projectPath, sandbox) {
  function createEnvFile (line 138) | async function createEnvFile(projectPath, sandbox, answers) {
  function createEnvExample (line 195) | async function createEnvExample(projectPath, sandbox) {
  function updatePackageJson (line 231) | async function updatePackageJson(projectPath, name) {
  function updateAppConfig (line 241) | async function updateAppConfig(projectPath, sandbox) {

FILE: packages/create-open-lovable/lib/prompts.js
  function getPrompts (line 1) | function getPrompts(config) {
  function getEnvPrompts (line 53) | function getEnvPrompts(provider) {

FILE: types/conversation.ts
  type ConversationMessage (line 3) | interface ConversationMessage {
  type ConversationEdit (line 16) | interface ConversationEdit {
  type ConversationContext (line 26) | interface ConversationContext {
  type ConversationState (line 45) | interface ConversationState {

FILE: types/file-manifest.ts
  type FileInfo (line 3) | interface FileInfo {
  type ImportInfo (line 14) | interface ImportInfo {
  type ComponentInfo (line 21) | interface ComponentInfo {
  type RouteInfo (line 29) | interface RouteInfo {
  type ComponentTree (line 35) | interface ComponentTree {
  type FileManifest (line 44) | interface FileManifest {
  type EditType (line 54) | enum EditType {
  type EditIntent (line 64) | interface EditIntent {
  type IntentPattern (line 73) | interface IntentPattern {

FILE: types/sandbox.ts
  type SandboxFile (line 3) | interface SandboxFile {
  type SandboxFileCache (line 8) | interface SandboxFileCache {
  type SandboxState (line 15) | interface SandboxState {

FILE: utils/cn.ts
  function cn (line 3) | function cn(...classes: classNames.ArgumentArray) {

FILE: utils/set-timeout-on-visible.ts
  function setTimeoutOnVisible (line 1) | function setTimeoutOnVisible({
  function setIntervalOnVisible (line 70) | function setIntervalOnVisible({
Condensed preview — 326 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,135K chars).
[
  {
    "path": ".cursor/mcp.json",
    "chars": 112,
    "preview": "{\n  \"mcpServers\": {\n    \"dev3000\": {\n      \"type\": \"http\",\n      \"url\": \"http://localhost:3684/mcp\"\n    }\n  }\n}\n"
  },
  {
    "path": ".env.example",
    "chars": 2134,
    "preview": "# Required\nFIRECRAWL_API_KEY=your_firecrawl_api_key  # Get from https://firecrawl.dev (Web scraping)\n\n# ================"
  },
  {
    "path": ".gitignore",
    "chars": 665,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n**/n"
  },
  {
    "path": "LICENSE",
    "chars": 1055,
    "preview": "MIT License\n\nCopyright (c) 2024\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this so"
  },
  {
    "path": "README.md",
    "chars": 2578,
    "preview": "# Open Lovable\n\nChat with AI to build React apps instantly. An example app made by the [Firecrawl](https://firecrawl.dev"
  },
  {
    "path": "app/api/analyze-edit-intent/route.ts",
    "chars": 7162,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { createGroq } from '@ai-sdk/groq';\nimport { createAnthr"
  },
  {
    "path": "app/api/apply-ai-code/route.ts",
    "chars": 31387,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { parseMorphEdits, applyMorphEditToFile } from '@/lib/mo"
  },
  {
    "path": "app/api/apply-ai-code-stream/route.ts",
    "chars": 32071,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { parseMorphEdits, applyMorphEditToFile } from '@/lib/mo"
  },
  {
    "path": "app/api/check-vite-errors/route.ts",
    "chars": 341,
    "preview": "import { NextResponse } from 'next/server';\n\n// Stub endpoint to prevent 404 errors\n// This endpoint is being called but"
  },
  {
    "path": "app/api/clear-vite-errors-cache/route.ts",
    "chars": 614,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var viteErrorsCache: { errors: any[], timestamp: number "
  },
  {
    "path": "app/api/conversation-state/route.ts",
    "chars": 4876,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport type { ConversationState } from '@/types/conversation';\n"
  },
  {
    "path": "app/api/create-ai-sandbox/route.ts",
    "chars": 11394,
    "preview": "import { NextResponse } from 'next/server';\nimport { Sandbox } from '@vercel/sandbox';\nimport type { SandboxState } from"
  },
  {
    "path": "app/api/create-ai-sandbox-v2/route.ts",
    "chars": 3099,
    "preview": "import { NextResponse } from 'next/server';\nimport { SandboxFactory } from '@/lib/sandbox/factory';\n// SandboxProvider t"
  },
  {
    "path": "app/api/create-zip/route.ts",
    "chars": 1968,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n}\n\nexport async function POST() "
  },
  {
    "path": "app/api/detect-and-install-packages/route.ts",
    "chars": 5890,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n}\n\nexport async fun"
  },
  {
    "path": "app/api/extract-brand-styles/route.ts",
    "chars": 2437,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\nexport async function POST(request: NextRequest) {\n  try {\n   "
  },
  {
    "path": "app/api/generate-ai-code-stream/route.ts",
    "chars": 91576,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { createGroq } from '@ai-sdk/groq';\nimport { createAnthr"
  },
  {
    "path": "app/api/get-sandbox-files/route.ts",
    "chars": 6421,
    "preview": "import { NextResponse } from 'next/server';\nimport { parseJavaScriptFile, buildComponentTree } from '@/lib/file-parser';"
  },
  {
    "path": "app/api/install-packages/route.ts",
    "chars": 8891,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n  var activeSandbox"
  },
  {
    "path": "app/api/install-packages-v2/route.ts",
    "chars": 1443,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { SandboxProvider } from '@/lib/sandbox/types';\nimport {"
  },
  {
    "path": "app/api/kill-sandbox/route.ts",
    "chars": 1187,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandboxProvider: any;\n  var sandboxData: any;\n"
  },
  {
    "path": "app/api/monitor-vite-logs/route.ts",
    "chars": 3830,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n}\n\nexport async function GET() {"
  },
  {
    "path": "app/api/report-vite-error/route.ts",
    "chars": 1646,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\ndeclare global {\n  var viteErrors: any[];\n}\n\n// Initialize glo"
  },
  {
    "path": "app/api/restart-vite/route.ts",
    "chars": 3422,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n  var activeSandboxProvider: any"
  },
  {
    "path": "app/api/run-command/route.ts",
    "chars": 1719,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\n// Get active sandbox from global state (in production, use a "
  },
  {
    "path": "app/api/run-command-v2/route.ts",
    "chars": 1419,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport { SandboxProvider } from '@/lib/sandbox/types';\nimport {"
  },
  {
    "path": "app/api/sandbox-logs/route.ts",
    "chars": 2501,
    "preview": "import { NextResponse } from 'next/server';\n\ndeclare global {\n  var activeSandbox: any;\n}\n\nexport async function GET() {"
  },
  {
    "path": "app/api/sandbox-status/route.ts",
    "chars": 1745,
    "preview": "import { NextResponse } from 'next/server';\nimport { sandboxManager } from '@/lib/sandbox/sandbox-manager';\n\ndeclare glo"
  },
  {
    "path": "app/api/scrape-screenshot/route.ts",
    "chars": 3180,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nexport asyn"
  },
  {
    "path": "app/api/scrape-url-enhanced/route.ts",
    "chars": 3972,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\n// Function to sanitize smart quotes and other problematic cha"
  },
  {
    "path": "app/api/scrape-website/route.ts",
    "chars": 3834,
    "preview": "import { NextRequest, NextResponse } from \"next/server\";\nimport FirecrawlApp from '@mendable/firecrawl-js';\n\nexport asyn"
  },
  {
    "path": "app/api/search/route.ts",
    "chars": 1440,
    "preview": "import { NextRequest, NextResponse } from 'next/server';\n\nexport async function POST(req: NextRequest) {\n  try {\n    con"
  },
  {
    "path": "app/builder/page.tsx",
    "chars": 8517,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { toast "
  },
  {
    "path": "app/generation/page.tsx",
    "chars": 179659,
    "preview": "'use client';\n\nimport { useState, useEffect, useRef, Suspense } from 'react';\nimport { useSearchParams, useRouter } from"
  },
  {
    "path": "app/globals.css",
    "chars": 29,
    "preview": "@import \"../styles/main.css\";"
  },
  {
    "path": "app/landing.tsx",
    "chars": 4464,
    "preview": "\"use client\";\n\n// useState not currently used but kept for future interactivity\nimport Link from \"next/link\";\n\n// Import"
  },
  {
    "path": "app/layout.tsx",
    "chars": 1031,
    "preview": "import type { Metadata } from \"next\";\nimport { Inter, Roboto_Mono } from \"next/font/google\";\nimport localFont from \"next"
  },
  {
    "path": "app/page.tsx",
    "chars": 45909,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { "
  },
  {
    "path": "atoms/sheets.ts",
    "chars": 80,
    "preview": "import { atom } from 'jotai';\n\nexport const isMobileSheetOpenAtom = atom(false);"
  },
  {
    "path": "colors.json",
    "chars": 4447,
    "preview": "{\n  \"heat-4\": {\n    \"hex\": \"fa5d190a\",\n    \"p3\": \"0.980392 0.364706 0.098039 / 0.039216\"\n  },\n  \"heat-8\": {\n    \"hex\": \""
  },
  {
    "path": "components/CodeApplicationProgress.tsx",
    "chars": 1805,
    "preview": "import React from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\n\nexport interface CodeApplicationSta"
  },
  {
    "path": "components/FirecrawlIcon.tsx",
    "chars": 1663,
    "preview": "export default function FirecrawlIcon({ className = \"w-5 h-5\" }: { className?: string }) {\n  return (\n    <svg \n      cl"
  },
  {
    "path": "components/FirecrawlLogo.tsx",
    "chars": 3160,
    "preview": "export default function FirecrawlLogo() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"15\"\n      viewBox=\"0 0 79 "
  },
  {
    "path": "components/HMRErrorDetector.tsx",
    "chars": 2298,
    "preview": "import { useEffect, useRef } from 'react';\n\ninterface HMRErrorDetectorProps {\n  iframeRef: React.RefObject<HTMLIFrameEle"
  },
  {
    "path": "components/HeroInput.tsx",
    "chars": 7875,
    "preview": "\"use client\";\n\nimport { useState, KeyboardEvent, useEffect, useRef } from \"react\";\n\ninterface HeroInputProps {\n  value: "
  },
  {
    "path": "components/SandboxPreview.tsx",
    "chars": 4078,
    "preview": "import { useState } from 'react';\nimport { Loader2, ExternalLink, RefreshCw, Terminal } from 'lucide-react';\n\ninterface "
  },
  {
    "path": "components/app/(home)/sections/ai-readiness/ControlPanel.tsx",
    "chars": 32060,
    "preview": "\"use client\";\n\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { \n  Globe, \n  FileText, \n  Code, \n  Shie"
  },
  {
    "path": "components/app/(home)/sections/ai-readiness/InlineResults.tsx",
    "chars": 12004,
    "preview": "\"use client\";\n\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { Check, X, FileText, Globe, Code, Sparkl"
  },
  {
    "path": "components/app/(home)/sections/ai-readiness/MetricBars.tsx",
    "chars": 7403,
    "preview": "\"use client\";\n\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { useState } from \"react\";\nimport { Chevr"
  },
  {
    "path": "components/app/(home)/sections/ai-readiness/RadarChart.tsx",
    "chars": 6843,
    "preview": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { useEffect, useState } from \"react\";\n\ninterface RadarChar"
  },
  {
    "path": "components/app/(home)/sections/ai-readiness/ScoreChart.tsx",
    "chars": 3246,
    "preview": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { useEffect, useState } from \"react\";\n\ninterface ScoreChar"
  },
  {
    "path": "components/app/(home)/sections/endpoints/EndpointsCrawl/EndpointsCrawl.tsx",
    "chars": 4196,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils"
  },
  {
    "path": "components/app/(home)/sections/endpoints/EndpointsExtract/EndpointsExtract.tsx",
    "chars": 4339,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils"
  },
  {
    "path": "components/app/(home)/sections/endpoints/EndpointsMap/EndpointsMap.tsx",
    "chars": 337,
    "preview": "\"use client\";\n\nimport { ComponentProps } from \"react\";\n\nimport EndpointsScrape from \"@/components/app/(home)/sections/en"
  },
  {
    "path": "components/app/(home)/sections/endpoints/EndpointsScrape/EndpointsScrape.tsx",
    "chars": 4065,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils"
  },
  {
    "path": "components/app/(home)/sections/endpoints/EndpointsSearch/EndpointsSearch.tsx",
    "chars": 3015,
    "preview": "\"use client\";\n\nimport initCanvas from \"@/utils/init-canvas\";\nimport { animate } from \"motion\";\nimport { useEffect, useRe"
  },
  {
    "path": "components/app/(home)/sections/endpoints/Extract/Extract.tsx",
    "chars": 2946,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef } from \"react\";\n\nimport initCanvas from \"@/u"
  },
  {
    "path": "components/app/(home)/sections/endpoints/Mcp/Mcp.tsx",
    "chars": 4337,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils"
  },
  {
    "path": "components/app/(home)/sections/hero/Background/Background.tsx",
    "chars": 5646,
    "preview": "\"use client\";\n\nimport { Fragment } from \"react\";\n\nimport CurvyRect from \"@/components/shared/layout/curvy-rect\";\n\nimport"
  },
  {
    "path": "components/app/(home)/sections/hero/Background/BackgroundOuterPiece.tsx",
    "chars": 1615,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { Connector } from \"@/components/shared/layout/curvy"
  },
  {
    "path": "components/app/(home)/sections/hero/Background/_svg/CenterStar.tsx",
    "chars": 588,
    "preview": "export default function CenterStar({\n  ...props\n}: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg\n      fill=\"none"
  },
  {
    "path": "components/app/(home)/sections/hero/Badge/Badge.tsx",
    "chars": 1371,
    "preview": "import Link from \"next/link\";\n\nexport default function HomeHeroBadge() {\n  return (\n    <Link\n      className=\"p-4 round"
  },
  {
    "path": "components/app/(home)/sections/hero/Hero.tsx",
    "chars": 2242,
    "preview": "import Link from \"next/link\";\n\nimport { Connector } from \"@/components/shared/layout/curvy-rect\";\nimport HeroFlame from "
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/Pixi.tsx",
    "chars": 1197,
    "preview": "\"use client\";\n\nimport { Suspense, lazy, useState, useEffect } from \"react\";\n\nconst Pixi = lazy(() => import(\"@/component"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/ascii.ts",
    "chars": 3553,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport PixiAssetManager from \"@/components/shared/pixi/PixiAsset"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/cell.ts",
    "chars": 1320,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\n\nimport AnimatedRect from \"./components/AnimatedRect\";\nimport Bl"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/cellReveal.ts",
    "chars": 998,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport AnimatedRect from \"./components/AnimatedRect\";\n\ntype Prop"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/components/AnimatedRect.ts",
    "chars": 2212,
    "preview": "\nimport { AnimationOptions, cubicBezier } from \"motion\";\nimport { Application, Container, Graphics, Sprite } from \"pixi."
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/components/BlinkingContainer.ts",
    "chars": 1907,
    "preview": "\nimport { Application, Graphics } from \"pixi.js\";\n\nimport { CELL_SIZE } from \"@/components/app/(home)/sections/hero/Pixi"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/components/Dot.ts",
    "chars": 406,
    "preview": "import { MAIN_COLOR } from \"@/components/app/(home)/sections/hero/Pixi/tickers/features/cell\";\n\nimport AnimatedRect from"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/crawl.ts",
    "chars": 5488,
    "preview": "import { animate } from \"motion\";\n\nimport { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport { sleep } from \"@/util"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/index.ts",
    "chars": 2455,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport setTimeoutOnVisible from \"@/utils/set-timeout-on-visible\""
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/mapping.ts",
    "chars": 5112,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport { sleep } from \"@/utils/sleep\";\n\nimport { CELL_SIZE, MAIN"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/scrape.ts",
    "chars": 5774,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport { sleep } from \"@/utils/sleep\";\n\nimport { CELL_SIZE, MAIN"
  },
  {
    "path": "components/app/(home)/sections/hero/Pixi/tickers/features/search.ts",
    "chars": 3476,
    "preview": "import { Ticker } from \"@/components/shared/pixi/Pixi\";\nimport { sleep } from \"@/utils/sleep\";\n\nimport { CELL_SIZE, MAIN"
  },
  {
    "path": "components/app/(home)/sections/hero/Title/Title.tsx",
    "chars": 6536,
    "preview": "\"use client\";\n\n// import dynamic from \"next/dynamic\";\n// import { useRef, useEffect, forwardRef } from \"react\";\n\n// cons"
  },
  {
    "path": "components/app/(home)/sections/hero-flame/HeroFlame.tsx",
    "chars": 1669,
    "preview": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\nimport { setIntervalOnVisible } from \"@/utils/set-timeout-on-"
  },
  {
    "path": "components/app/(home)/sections/hero-flame/data.json",
    "chars": 169635,
    "preview": "[\n  \"                                                                                                                   "
  },
  {
    "path": "components/app/(home)/sections/hero-input/Button/Button.tsx",
    "chars": 1415,
    "preview": "import { AnimatePresence, motion } from \"motion/react\";\n\nimport AnimatedWidth from \"@/components/shared/layout/animated-"
  },
  {
    "path": "components/app/(home)/sections/hero-input/HeroInput.tsx",
    "chars": 2647,
    "preview": "\"use client\";\n\nimport Link from \"next/link\";\nimport { useState } from \"react\";\n\nimport Globe from \"./_svg/Globe\";\nimport"
  },
  {
    "path": "components/app/(home)/sections/hero-input/Tabs/Mobile/Mobile.tsx",
    "chars": 5873,
    "preview": "import { animate, AnimatePresence, cubicBezier, motion } from \"motion/react\";\nimport { useEffect, useRef, useState } fro"
  },
  {
    "path": "components/app/(home)/sections/hero-input/Tabs/Tabs.tsx",
    "chars": 5871,
    "preview": "import { animate } from \"motion\";\nimport { Fragment, useRef } from \"react\";\n\nimport EndpointsSearch from \"@/components/a"
  },
  {
    "path": "components/app/(home)/sections/hero-input/_svg/ArrowRight.tsx",
    "chars": 437,
    "preview": "export default function ArrowRight() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\""
  },
  {
    "path": "components/app/(home)/sections/hero-input/_svg/Globe.tsx",
    "chars": 744,
    "preview": "export default function Globe() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n    "
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/Code/Code.tsx",
    "chars": 5064,
    "preview": "import { useCallback, useEffect, useState } from \"react\";\n\nimport CurvyRect, { Connector } from \"@/components/shared/lay"
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/Code/Loading/Loading.tsx",
    "chars": 1698,
    "preview": "import { AnimatePresence, motion } from \"motion/react\";\nimport { useEffect, useState } from \"react\";\n\nimport { encryptTe"
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/Code/Loading/_svg/Check.tsx",
    "chars": 812,
    "preview": "export default function Check() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n    "
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/HeroScraping.css",
    "chars": 378,
    "preview": ".hero-scraping-highlight::before {\n  animation: hero-scraping-highlight-before 1s linear infinite;\n  transition: none !i"
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/HeroScraping.tsx",
    "chars": 11313,
    "preview": "\"use client\";\n\nimport { animate } from \"motion\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport CurvyRect "
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/Tag/Tag.tsx",
    "chars": 1582,
    "preview": "import { motion } from \"motion/react\";\nimport { ComponentProps, useEffect, useState } from \"react\";\n\nimport { encryptTex"
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/_svg/BrowserMobile.tsx",
    "chars": 4382,
    "preview": "export default function BrowserMobile(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg\n      fill=\"none\"\n    "
  },
  {
    "path": "components/app/(home)/sections/hero-scraping/_svg/BrowserTab.tsx",
    "chars": 486,
    "preview": "import { HTMLAttributes } from \"react\";\n\nexport default function BrowserTab(attrs: HTMLAttributes<SVGSVGElement>) {\n  re"
  },
  {
    "path": "components/app/.cursor/rules/home-page-components.md",
    "chars": 1595,
    "preview": "# Home Page Components Rules\n\nWhen working with home/landing page components in components-new/app/(home):\n\n## Structure"
  },
  {
    "path": "components/app/generation/SidebarInput.tsx",
    "chars": 5835,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport { appConfig } from \"@/config/app.c"
  },
  {
    "path": "components/app/generation/SidebarQuickInput.tsx",
    "chars": 1392,
    "preview": "\"use client\";\n\nimport { useState } from \"react\";\nimport HeroInputSubmitButton from \"@/components/app/(home)/sections/her"
  },
  {
    "path": "components/shared/Playground/Context/types.ts",
    "chars": 942,
    "preview": "export enum Endpoint {\n  Scrape = \"scrape\",\n  Crawl = \"crawl\",\n  Search = \"search\",\n  Map = \"map\",\n  Extract = \"extract\""
  },
  {
    "path": "components/shared/animated-dot-icon.tsx",
    "chars": 8091,
    "preview": "\"use client\";\n\nimport { animate } from \"framer-motion\";\nimport { useEffect, useRef } from \"react\";\nimport { cn } from \"@"
  },
  {
    "path": "components/shared/animated-height.tsx",
    "chars": 1826,
    "preview": "\"use client\";\n\nimport React, { useRef, useEffect, ReactNode, useState } from \"react\";\n\n// Smoothly animates its containe"
  },
  {
    "path": "components/shared/ascii-background.tsx",
    "chars": 2146,
    "preview": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\n\nconst asciiPatterns = [\n  "
  },
  {
    "path": "components/shared/ascii-flame-background.tsx",
    "chars": 1533,
    "preview": "\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { setIntervalOn"
  },
  {
    "path": "components/shared/button/Button.css",
    "chars": 2649,
    "preview": ".button {\n  transition: all 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),\n    scale 0.1s cubic-bezier(0.25, 0.1, 0.25, 1),\n    "
  },
  {
    "path": "components/shared/button/Button.tsx",
    "chars": 1973,
    "preview": "import { Children, ButtonHTMLAttributes } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\ninterface Props extends Butto"
  },
  {
    "path": "components/shared/buttons/capsule-button.tsx",
    "chars": 4439,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { LucideIcon } from \"lucide-react\";\nim"
  },
  {
    "path": "components/shared/buttons/fire-action-link.tsx",
    "chars": 1032,
    "preview": "import Link from \"next/link\";\nimport { cn } from \"@/utils/cn\";\n\ninterface FireActionLinkProps {\n  href?: string;\n  label"
  },
  {
    "path": "components/shared/buttons/index.ts",
    "chars": 167,
    "preview": "// Button Components\nexport { SlateButton } from \"./slate-button\";\n// export { HeatButton } from \"./heat-button\";\nexport"
  },
  {
    "path": "components/shared/buttons/slate-button.tsx",
    "chars": 3067,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { LucideIcon } from \"lucide-react\";\n\ni"
  },
  {
    "path": "components/shared/color-styles/color-styles.tsx",
    "chars": 787,
    "preview": "import colors from \"@/styles/colors.json\";\n\nconst TYPED_COLORS = colors as unknown as Record<\n  string,\n  Record<\"hex\" |"
  },
  {
    "path": "components/shared/combobox/combobox.tsx",
    "chars": 5125,
    "preview": "import { animate, AnimatePresence, cubicBezier, motion } from \"motion/react\";\nimport { useEffect, useMemo, useRef, useSt"
  },
  {
    "path": "components/shared/effects/.cursor/rules/flame-effects.md",
    "chars": 2323,
    "preview": "# Flame Effects Rules\n\nWhen working with visual effects components in components-new/shared/effects:\n\n## Flame ASCII Sys"
  },
  {
    "path": "components/shared/effects/flame/Flame.tsx",
    "chars": 1362,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { set"
  },
  {
    "path": "components/shared/effects/flame/ascii-explosion.tsx",
    "chars": 1535,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef, memo } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport"
  },
  {
    "path": "components/shared/effects/flame/auth-pulse/auth-pulse.tsx",
    "chars": 1491,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { setI"
  },
  {
    "path": "components/shared/effects/flame/auth-pulse/pulse-data.json",
    "chars": 25063,
    "preview": "[\n  \"                                                                                                    \\n             "
  },
  {
    "path": "components/shared/effects/flame/core-flame.json",
    "chars": 222029,
    "preview": "[\n  \"                                                                     ''                               ''           "
  },
  {
    "path": "components/shared/effects/flame/core-flame.tsx",
    "chars": 1405,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { setI"
  },
  {
    "path": "components/shared/effects/flame/explosion-data.json",
    "chars": 98179,
    "preview": "[\n  \"                                                                                                                   "
  },
  {
    "path": "components/shared/effects/flame/flame-background.tsx",
    "chars": 1017,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { CoreFlame } from \"./core-flame\";\n\nin"
  },
  {
    "path": "components/shared/effects/flame/hero-flame-data.json",
    "chars": 169635,
    "preview": "[\n  \"                                                                                                                   "
  },
  {
    "path": "components/shared/effects/flame/hero-flame.tsx",
    "chars": 1775,
    "preview": "\"use client\";\n\nimport { useEffect, useRef, memo } from \"react\";\n\nimport { setIntervalOnVisible } from \"@/utils/set-timeo"
  },
  {
    "path": "components/shared/effects/flame/index.ts",
    "chars": 299,
    "preview": "export { CoreFlame } from \"./core-flame\";\nexport { AsciiExplosion } from \"./ascii-explosion\";\nexport { default as HeroFl"
  },
  {
    "path": "components/shared/effects/flame/slate-grid/grid-data.json",
    "chars": 8568,
    "preview": "[\n  \"┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐\\n│         │         │         │         │         │ "
  },
  {
    "path": "components/shared/effects/flame/slate-grid/slate-grid.tsx",
    "chars": 1539,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { setI"
  },
  {
    "path": "components/shared/effects/flame/subtle-explosion.tsx",
    "chars": 1556,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { setI"
  },
  {
    "path": "components/shared/effects/flame/subtle-wave/subtle-wave.tsx",
    "chars": 1148,
    "preview": "\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { setIntervalOnVisible } from \"@/utils/set-timeo"
  },
  {
    "path": "components/shared/effects/flame/subtle-wave/wave-data.json",
    "chars": 2115,
    "preview": "[\n  \"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\",\n  \"▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░"
  },
  {
    "path": "components/shared/effects/index.ts",
    "chars": 127,
    "preview": "// Effect Components\nexport { CoreFlame } from \"./flame/core-flame\";\nexport { AsciiExplosion } from \"./flame/ascii-explo"
  },
  {
    "path": "components/shared/effects/subtle-ascii-animation.tsx",
    "chars": 1656,
    "preview": "\"use client\";\n\nimport React, { useEffect, useRef, useMemo } from \"react\";\nimport { setIntervalOnVisible } from \"@/utils/"
  },
  {
    "path": "components/shared/firecrawl-icon/firecrawl-icon-static.tsx",
    "chars": 1814,
    "preview": "import { HTMLAttributes } from \"react\";\n\nexport default function FirecrawlIconStatic({\n  fill = \"var(--heat-100)\",\n  cla"
  },
  {
    "path": "components/shared/firecrawl-icon/firecrawl-icon.tsx",
    "chars": 23106,
    "preview": "import { HTMLAttributes } from \"react\";\n\nexport default function FirecrawlIcon({\n  fill = \"var(--heat-100)\",\n  innerFill"
  },
  {
    "path": "components/shared/header/BrandKit/BrandKit.tsx",
    "chars": 7018,
    "preview": "\"use client\";\n\nimport copy from \"copy-to-clipboard\";\nimport { animate, cubicBezier } from \"motion\";\nimport { AnimatePres"
  },
  {
    "path": "components/shared/header/BrandKit/_svg/Download.tsx",
    "chars": 572,
    "preview": "export default function Download() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n "
  },
  {
    "path": "components/shared/header/BrandKit/_svg/Guidelines.tsx",
    "chars": 1014,
    "preview": "export default function Guidelines() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\""
  },
  {
    "path": "components/shared/header/BrandKit/_svg/Icon.tsx",
    "chars": 1683,
    "preview": "export default function Icon() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n     "
  },
  {
    "path": "components/shared/header/Dropdown/Content/Content.tsx",
    "chars": 6287,
    "preview": "import CurvyRect from \"@/components/shared/layout/curvy-rect\";\nimport { cn } from \"@/utils/cn\";\nimport { ArrowUpRight } "
  },
  {
    "path": "components/shared/header/Dropdown/Content/NavItemRow.tsx",
    "chars": 2459,
    "preview": "import { ArrowUpRight } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\n\nexport interface NavItemRowProps {\n  icon"
  },
  {
    "path": "components/shared/header/Dropdown/Github/Flame/Flame.tsx",
    "chars": 1475,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { set"
  },
  {
    "path": "components/shared/header/Dropdown/Github/Flame/data.json",
    "chars": 58335,
    "preview": "[\n  \"                                                                \\n                                                 "
  },
  {
    "path": "components/shared/header/Dropdown/Github/Github.tsx",
    "chars": 707,
    "preview": "import Image from \"@/components/shared/image/Image\";\n\nimport GithubFlame from \"./Flame/Flame\";\n\nexport default function "
  },
  {
    "path": "components/shared/header/Dropdown/Mobile/Item/Item.tsx",
    "chars": 2610,
    "preview": "\"use client\";\nimport { AnimatePresence, cubicBezier, motion } from \"motion/react\";\nimport { useState } from \"react\";\n\nim"
  },
  {
    "path": "components/shared/header/Dropdown/Mobile/Mobile.tsx",
    "chars": 1432,
    "preview": "import { Fragment } from \"react\";\n\nimport Button from \"@/components/ui/shadcn/button\";\nimport {\n  ConnectorToBottom,\n  C"
  },
  {
    "path": "components/shared/header/Dropdown/Stories/Flame/Flame.tsx",
    "chars": 1519,
    "preview": "\"use client\";\n\nimport { HTMLAttributes, useEffect, useRef } from \"react\";\n\nimport data from \"@/components/app/(home)/sec"
  },
  {
    "path": "components/shared/header/Dropdown/Stories/Stories.tsx",
    "chars": 989,
    "preview": "import ArrowUp from \"./_svg/ArrowUp\";\nimport Replit from \"./_svg/Replit\";\nimport StoriesFlame from \"./Flame/Flame\";\n\nexp"
  },
  {
    "path": "components/shared/header/Dropdown/Stories/_svg/ArrowUp.tsx",
    "chars": 423,
    "preview": "export default function ArrowUp() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n  "
  },
  {
    "path": "components/shared/header/Dropdown/Stories/_svg/Replit.tsx",
    "chars": 881,
    "preview": "export default function Replit() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"32\"\n      viewBox=\"0 0 32 32\"\n   "
  },
  {
    "path": "components/shared/header/Dropdown/Wrapper/Wrapper.tsx",
    "chars": 3448,
    "preview": "\"use client\";\n\nimport { AnimatePresence, cubicBezier, motion } from \"motion/react\";\nimport { useEffect } from \"react\";\n\n"
  },
  {
    "path": "components/shared/header/Github/GithubClient.tsx",
    "chars": 386,
    "preview": "\"use client\";\n\nimport Button from \"@/components/ui/shadcn/button\";\nimport GithubIcon from \"./_svg/GithubIcon\";\n\nexport d"
  },
  {
    "path": "components/shared/header/Github/_svg/GithubIcon.tsx",
    "chars": 1507,
    "preview": "export default function GithubIcon() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\""
  },
  {
    "path": "components/shared/header/HeaderContext.tsx",
    "chars": 3340,
    "preview": "\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport React, {\n  createContext,\n  useContext,\n  useEffect"
  },
  {
    "path": "components/shared/header/Nav/Item/Item.tsx",
    "chars": 1340,
    "preview": "\"use client\";\n\nimport { JSX } from \"react\";\n\nimport { useHeaderContext } from \"@/components/shared/header/HeaderContext\""
  },
  {
    "path": "components/shared/header/Nav/Item/_svg/ChevronDown.tsx",
    "chars": 400,
    "preview": "export default function ChevronDown() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 18 20"
  },
  {
    "path": "components/shared/header/Nav/Nav.tsx",
    "chars": 10895,
    "preview": "import EndpointsCrawl from \"@/components/app/(home)/sections/endpoints/EndpointsCrawl/EndpointsCrawl\";\nimport EndpointsS"
  },
  {
    "path": "components/shared/header/Nav/RenderEndpointIcon.tsx",
    "chars": 472,
    "preview": "\"use client\";\n\nimport EndpointsScrape from \"@/components/app/(home)/sections/endpoints/EndpointsScrape/EndpointsScrape\";"
  },
  {
    "path": "components/shared/header/Nav/_svg/Affiliate.tsx",
    "chars": 1287,
    "preview": "export default function Affiliate() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n"
  },
  {
    "path": "components/shared/header/Nav/_svg/Api.tsx",
    "chars": 799,
    "preview": "export default function Api() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n      "
  },
  {
    "path": "components/shared/header/Nav/_svg/ArrowRight.tsx",
    "chars": 516,
    "preview": "export default function ArrowRight() {\n  return (\n    <svg\n      className=\"text-black-alpha-48 group-hover:text-heat-10"
  },
  {
    "path": "components/shared/header/Nav/_svg/Careers.tsx",
    "chars": 778,
    "preview": "export default function Careers() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n  "
  },
  {
    "path": "components/shared/header/Nav/_svg/Changelog.tsx",
    "chars": 680,
    "preview": "export default function Changelog() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n"
  },
  {
    "path": "components/shared/header/Nav/_svg/Chats.tsx",
    "chars": 958,
    "preview": "export default function Ai() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n      w"
  },
  {
    "path": "components/shared/header/Nav/_svg/Lead.tsx",
    "chars": 1001,
    "preview": "export default function Lead() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n     "
  },
  {
    "path": "components/shared/header/Nav/_svg/MCP.tsx",
    "chars": 1072,
    "preview": "export default function MCPIcon() {\n  return (\n    <svg\n      fill=\"currentColor\"\n      fill-rule=\"evenodd\"\n      height"
  },
  {
    "path": "components/shared/header/Nav/_svg/Platforms.tsx",
    "chars": 1580,
    "preview": "export default function Platforms() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n"
  },
  {
    "path": "components/shared/header/Nav/_svg/Research.tsx",
    "chars": 1593,
    "preview": "export default function Research() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n "
  },
  {
    "path": "components/shared/header/Nav/_svg/Student.tsx",
    "chars": 648,
    "preview": "export default function Student() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n  "
  },
  {
    "path": "components/shared/header/Nav/_svg/Templates.tsx",
    "chars": 753,
    "preview": "export default function Templates() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n"
  },
  {
    "path": "components/shared/header/Toggle/Toggle.tsx",
    "chars": 1638,
    "preview": "\"use client\";\n\nimport Button from \"@/components/ui/shadcn/button\";\nimport { useHeaderContext } from \"@/components/shared"
  },
  {
    "path": "components/shared/header/Wrapper/Wrapper.tsx",
    "chars": 931,
    "preview": "\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport { useEffect, useState } from \"react\";\n\nimport { cn "
  },
  {
    "path": "components/shared/header/_svg/Logo.tsx",
    "chars": 3152,
    "preview": "export default function Logo() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"15\"\n      viewBox=\"0 0 79 15\"\n     "
  },
  {
    "path": "components/shared/hero-flame.tsx",
    "chars": 2498,
    "preview": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\n\n// Sample of flame"
  },
  {
    "path": "components/shared/icons/GitHub.tsx",
    "chars": 1385,
    "preview": "const GitHub = ({ ...props }) => {\n  return (\n    <svg\n      width=\"24\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n    "
  },
  {
    "path": "components/shared/icons/Logo.tsx",
    "chars": 632,
    "preview": "const Logo = ({ ...props }) => (\n  // <svg\n  //   width=\"32\"\n  //   height=\"32\"\n  //   viewBox=\"0 0 32 32\"\n  //   fill=\""
  },
  {
    "path": "components/shared/icons/animated-chevron.tsx",
    "chars": 1567,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { ChevronDown, ChevronUp } from \"lucide-react\";\nimport { motion, Animat"
  },
  {
    "path": "components/shared/icons/animated-icons.tsx",
    "chars": 24187,
    "preview": "\"use client\";\n\nimport type { Transition, Variants } from \"framer-motion\";\nimport { motion, useAnimation } from \"framer-m"
  },
  {
    "path": "components/shared/icons/arrow-animated.tsx",
    "chars": 611,
    "preview": "import { cx } from \"class-variance-authority\";\n\nexport function ArrowAnimated({\n  className,\n  ...props\n}: React.HTMLAtt"
  },
  {
    "path": "components/shared/icons/check.tsx",
    "chars": 812,
    "preview": "export default function Check() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n    "
  },
  {
    "path": "components/shared/icons/chevron-slide.tsx",
    "chars": 1107,
    "preview": "\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\n\ntype Direction = \"left\" | \"right\";\n\ninterfac"
  },
  {
    "path": "components/shared/icons/copied.tsx",
    "chars": 399,
    "preview": "export default function CopiedIcon() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\""
  },
  {
    "path": "components/shared/icons/copy.tsx",
    "chars": 786,
    "preview": "export default function CopyIcon() {\n  return (\n    <svg\n      fill=\"none\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n "
  },
  {
    "path": "components/shared/icons/curve.tsx",
    "chars": 511,
    "preview": "interface CurveProps extends React.SVGAttributes<SVGSVGElement> {\n  fill?: string;\n}\n\nexport default function Curve({\n  "
  },
  {
    "path": "components/shared/icons/fingerprint-icon.tsx",
    "chars": 5457,
    "preview": "\"use client\";\n\nimport type { Variants } from \"motion/react\";\nimport { motion, useAnimation } from \"motion/react\";\nimport"
  },
  {
    "path": "components/shared/icons/openai.tsx",
    "chars": 1522,
    "preview": "import * as React from \"react\";\n\nfunction IconOpenai(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg\n      v"
  },
  {
    "path": "components/shared/icons/source-icon.tsx",
    "chars": 446,
    "preview": "import { JSXElementConstructor } from \"react\";\nimport Image from \"next/image\";\n\nexport const SourceIcon = ({ id }: { id:"
  },
  {
    "path": "components/shared/icons/symbol-colored.tsx",
    "chars": 1715,
    "preview": "import React from \"react\";\n\nconst SymbolColored = ({ ...props }) => {\n  return (\n    <svg\n      width=\"50\"\n      height="
  },
  {
    "path": "components/shared/icons/symbol-white.tsx",
    "chars": 1657,
    "preview": "import React from \"react\";\n\nconst SymbolWhite = ({ ...props }) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2"
  },
  {
    "path": "components/shared/icons/tremor-placeholder.tsx",
    "chars": 4667,
    "preview": "import type { SVGProps } from \"react\";\n\nexport const TremorPlaceholder = (props: SVGProps<SVGSVGElement>) => (\n  <svg cl"
  },
  {
    "path": "components/shared/icons/wordmark-colored.tsx",
    "chars": 4566,
    "preview": "import React from \"react\";\n\nconst WordmarkColored = ({ ...props }) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.o"
  },
  {
    "path": "components/shared/icons/wordmark-white.tsx",
    "chars": 4520,
    "preview": "import React from \"react\";\n\nconst WordmarkWhite = ({ ...props }) => {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org"
  },
  {
    "path": "components/shared/image/Image.tsx",
    "chars": 1304,
    "preview": "/* eslint-disable @next/next/no-img-element */\nimport { ComponentProps } from \"react\";\n\nimport compressorConfig from \"@/"
  },
  {
    "path": "components/shared/image/getImageSrc.ts",
    "chars": 1247,
    "preview": "import compressorConfig from \"@/public/compressor.json\";\n\nconst avifConfig = compressorConfig.configs.find(\n  (c) => c.e"
  },
  {
    "path": "components/shared/layout/animated-height.tsx",
    "chars": 1280,
    "preview": "\"use client\";\n\nimport { motion, MotionProps, TargetAndTransition } from \"motion/react\";\nimport { useEffect, useRef, useS"
  },
  {
    "path": "components/shared/layout/animated-width.tsx",
    "chars": 1194,
    "preview": "\"use client\";\n\nimport { motion, TargetAndTransition, Transition } from \"motion/react\";\nimport { useEffect, useRef, useSt"
  },
  {
    "path": "components/shared/layout/curvy-rect-divider.tsx",
    "chars": 748,
    "preview": "import React from \"react\";\n\nimport CurvyRect from \"./curvy-rect\";\n\nexport function CurvyRectDivider() {\n  return (\n    <"
  },
  {
    "path": "components/shared/layout/curvy-rect.tsx",
    "chars": 4104,
    "preview": "import { cn } from \"@/utils/cn\";\n\nimport Curve from \"@/components/shared/icons/curve\";\n\ninterface CurvyRectProps extends"
  },
  {
    "path": "components/shared/loading/Shimmer.tsx",
    "chars": 3465,
    "preview": "\"use client\";\n\nimport { cn } from \"@/utils/cn\";\nimport { useState, useEffect } from \"react\";\nimport ScrambleText from \"@"
  },
  {
    "path": "components/shared/loading/usage-loading.tsx",
    "chars": 462,
    "preview": "\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport ScrambleText from \"@/components/ui/motion/scramble-te"
  },
  {
    "path": "components/shared/lockBody.tsx",
    "chars": 831,
    "preview": "/**\n * Utility to lock/unlock the document body based on a set of keys.\n * Each key can \"lock\" the body (e.g., prevent s"
  },
  {
    "path": "components/shared/logo-cloud/index.ts",
    "chars": 40,
    "preview": "export { default } from \"./logo-cloud\";\n"
  },
  {
    "path": "components/shared/logo-cloud/logo-cloud.tsx",
    "chars": 1911,
    "preview": "export default function LogoCloud() {\n  return (\n    <div>\n      <p className=\"mt-24 text-xs uppercase text-zinc-400 tex"
  },
  {
    "path": "components/shared/logo-cloud/logo-cloud2/Logocloud.css",
    "chars": 568,
    "preview": ".logocloud-items {\n  animation: logocloud-items 100s infinite linear;\n  will-change: transform;\n}\n\n@keyframes logocloud-"
  },
  {
    "path": "components/shared/logo-cloud/logo-cloud2/Logocloud.tsx",
    "chars": 2019,
    "preview": "import { CurvyRect } from \"@/components/shared/ui\";\nimport Image from \"@/components/shared/image/Image\";\nimport \"./Logoc"
  },
  {
    "path": "components/shared/notifications/slack-notification.tsx",
    "chars": 1492,
    "preview": "\"use client\";\n\nimport Image from \"next/image\";\nimport { useEffect, useState } from \"react\";\n\nexport default function Sla"
  }
]

// ... and 126 more files (download for full content)

About this extraction

This page contains the full source code of the firecrawl/open-lovable GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 326 files (2.0 MB), approximately 465.2k tokens, and a symbol index with 453 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!