---
name: "SmartCrop26"
client: Oliver Internal
status: active
server: optical-web-1
tech: [React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Google Gemini API, Azure AD]
local_path: /Users/ai_leed/Documents/Projects/Oliver/smartcrop26
deploy: bash deploy.sh
url: https://ai-sandbox.oliver.solutions/smartcrop26
tags: [oliver, smartcrop, image, lovable, react]
created: 2026-04-14
port: 8080
---

## Overview

SmartCrop26 is a fully client-side React SPA that provides AI-powered image cropping for multiple aspect ratios and platforms. Users upload images, select or define aspect ratios, and receive crop suggestions from either Google Gemini 2.5 Flash (remote AI) or a local Sobel edge-detection algorithm running entirely in the browser. Results export as a ZIP archive of PNGs at specified dimensions. Built for Oliver Internal and deployed to `https://ai-sandbox.oliver.solutions/smartcrop26`.

## Tech Stack

- **Frontend:** React 18, TypeScript, Vite, Tailwind CSS 3.4, shadcn/ui (Radix primitives), Lucide icons
- **Backend:** None (fully client-side; API calls to Google Gemini only)
- **Database:** None
- **Infrastructure:** Apache on optical-web-1; deployed to `/var/www/html/smartcrop26`
- **AI/ML:** Google Gemini 2.5 Flash (function calling); local Sobel edge detection (Canvas API)
- **Key libraries:** JSZip (export), file-saver (download), MSAL (Azure AD auth), React Hook Form, TanStack React Query, next-themes (dark mode)

## Architecture

**Single-page orchestrator:** `src/pages/Index.tsx` holds all top-level state (`images`, `selectedRatios`, `cropsMap`, `engine` toggle) and acts as the central wiring point.

**Dual analysis engines:**
- **AI engine** (`src/lib/analyze-image.ts`): Encodes image as base64, sends to Google Gemini 2.5 Flash via function calling with a crop suggestion prompt. Requires `VITE_GOOGLE_API_KEY`.
- **Local engine** (`src/lib/analyze-local.ts`): Sobel edge detection entirely in-browser. Scales image to max 400px, samples candidate crop windows with center-weight bias, returns edge scores.

Both engines return `CropSuggestion[]` with normalized coordinates (0–1 range).

**Image handling:** Images stored as data URLs (FileReader). Canvas API used for:
- Thumbnail rendering (CropPreviewCard)
- Interactive crop editing (CropEditor)
- Export resizing and export PNG generation

**Export pipeline** (`src/lib/export-zip.ts`): Iterates all image/crop combinations, rescales via canvas at each requested pixel size, packs PNGs into ZIP (JSZip), triggers browser download (FileSaver).

**Config parsing** (`src/lib/crop-types.ts`): `parseConfig()` handles v2 JSON schema, legacy Python tool format, and simple ratio lists. `PRESET_RATIOS` defines 12 built-in aspect ratios (Instagram, TikTok, LinkedIn, YouTube, etc.).

**Auth:** Azure AD SSO via MSAL (`@azure/msal-browser`, `@azure/msal-react`). Tenant and client IDs in `.env`.

**Styling:** HSL custom properties, Tailwind CSS, dark mode via next-themes class strategy, Montserrat font.

```
┌──────────────────────────────────┐
│   Index.tsx (State Container)    │
├──────────────────────────────────┤
│  images[] | selectedRatios[]     │
│  cropsMap | engine toggle        │
└──────────────────┬───────────────┘
         ┌─────────┼──────────┬────────────┐
         │         │          │            │
    ┌────▼──┐  ┌───▼──┐  ┌────▼───┐  ┌──▼────┐
    │Upload │  │Ratio │  │Preview │  │Export │
    │Mgr    │  │Select│  │& Editor│  │ ZIP   │
    └────┬──┘  └──────┘  └───┬────┘  └──┬────┘
         │                   │          │
    ┌────▼──────────┬────────▼──┐  ┌────▼─────┐
    │ AI Engine     │Local Sobel│  │export-zip│
    │ (Gemini 2.5)  │ (Canvas)  │  │(JSZip)   │
    └───────────────┴───────────┘  └──────────┘
```

## Dev Commands

```bash
npm run dev              # Start dev server on http://localhost:8080 (Vite)
npm run build            # Production build (output to dist/)
npm run build:dev        # Dev build with source maps
npm run lint             # ESLint check
npm run test             # Vitest single run
npm run test:watch       # Vitest watch mode
```

## Deployment

- **Server:** optical-web-1 (Apache)
- **Deploy:** `bash deploy.sh` (run on server at `/opt/smartcrop26`)
- **URL:** `https://ai-sandbox.oliver.solutions/smartcrop26`
- **Port:** N/A (served via Apache; dev server on 8080)
- **Service:** N/A (static files served by Apache)
- **Local path:** `/Users/ai_leed/Documents/Projects/Oliver/smartcrop26`

**Deploy script flow:**
1. Pulls latest from git
2. Runs `npm ci && npm run build`
3. Copies `dist/` to `/var/www/html/smartcrop26/` (uses `sudo` for permissions)

## Environment Variables

- `VITE_GOOGLE_API_KEY` — API key for Google Gemini 2.5 Flash. Without this, only local Sobel engine is available.
- `VITE_AZURE_TENANT_ID` — Azure AD tenant: `e519c2e6-bc6d-4fdf-8d9c-923c2f002385`
- `VITE_AZURE_CLIENT_ID` — Azure AD app client ID: `9079054c-9620-4757-a256-23413042f1ef`
- `VITE_AZURE_REDIRECT_URI` — Redirect URI after login: `https://ai-sandbox.oliver.solutions/smartcrop26`

See `.env.example` for template.

## API / Endpoints

**No REST backend.** All image processing runs client-side. Only external API is:
- **Google Gemini 2.5 Flash** — Called directly from browser with function calling payload (image base64 + crop suggestion schema). Credentials managed by `VITE_GOOGLE_API_KEY`.

## Known Issues

- **TypeScript config is loose:** `noImplicitAny: false`, no strict null checks. Do not tighten without full codebase audit.
- **No backend:** All state and processing on client; stateless after page reload.
- **SSH access:** Do not SSH to optical-web-1 without explicit user instruction.
- **shadcn/ui editing:** Never edit components in `src/components/ui/` manually. Regenerate via `npx shadcn-ui@latest add <component>`.

## Git

- **Remote:** `git@bitbucket.org:zlalani/smartcrop26.git`
- **Latest commits:** "Add Reset button and per-image Remove button" (24883a3); "Improve UX: batch generate, ZIP fix, workflow guide, status badges" (66bafdc)

## Sessions
### 2026-04-14 – Project catalogued
**Done:** Added to Obsidian second brain.

---
## Change Log
| Date | Requested | Changed | Files |
|------|-----------|---------|-------|
| 2026-04-14 | Initial setup | Note created | — |