# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What This Is

PalmOilAI — an Angular 20 single-page application for palm oil fruit bunch (FFB) ripeness detection. All AI inference runs **100% client-side** in the browser using ONNX Runtime Web and TensorFlow.js TFLite. There is no required backend dependency; the NestJS backend is optional and used only for server-side inference and chat proxy.

Detection classes: `Ripe`, `Unripe`, `Underripe`, `Overripe`, `Abnormal`, `Empty_Bunch`.

## Commands

```bash
npm install
ng serve                        # Dev server at http://localhost:4200
ng serve --host 0.0.0.0         # Expose to LAN (device testing)
ng build                        # Production build → dist/
ng test                         # Karma + Jasmine unit tests
```

## Architecture

### Routing
`/analyzer` → `AnalyzerComponent` (main scanner UI, default route)  
`/history` → `HistoryComponent` (local vault of past scans)  
`/chatbot` → `ChatbotComponent` (chat interface backed by n8n via WebSocket)

### Key Services (`src/app/services/`)

**`LocalInferenceService`** — Core AI engine. Dispatches to ONNX or TFLite backend based on model file extension:
- ONNX path: `onnxruntime-web` with WASM execution provider. Input: `[1, 3, 640, 640]` (CHW).
- TFLite path: Uses the globally-injected `window.tflite` object. Input is transposed CHW→HWC to `[1, 640, 640, 3]` before prediction.

**`ImageProcessorService`** — Resizes any image to 640×640 via offscreen Canvas, then converts RGBA→CHW `Float32Array` normalized to `[0.0, 1.0]`.

**`LocalHistoryService`** — Persists up to 20 scan records (FIFO) to `localStorage` key `palm_oil_vault`. Each record includes detection summary, latency, engine type, Base64 thumbnail, and bounding boxes.

**`VisionSocketService`** / **`ChatSocketService`** — WebSocket clients connecting to the NestJS backend (`/vision` and unspecified chat namespace respectively).

**`SurveillanceService`** (frontend) — Connects to the NestJS `/monitor` namespace for live CPU/memory metrics of NestJS, n8n, and Ollama processes.

### TFLite Bundler Constraint
`@tensorflow/tfjs-tflite` is a legacy CommonJS/UMD hybrid incompatible with the Vite/esbuild ESM bundler. Both `tf.min.js` and `tf-tflite.min.js` are loaded as **global scripts** in `angular.json`, not as ES modules. This populates `window.tflite` and `window.tf` before Angular bootstraps. Do not attempt to import them via `import` statements.

### Required Manual Assets (not installed by npm)
These binary files must be placed manually after `npm install`:

| Path | Source |
|---|---|
| `src/assets/models/onnx/best.onnx` | YOLOv8 model file |
| `src/assets/models/tflite/best_float32.tflite` | Full-precision TFLite model |
| `src/assets/models/tflite/best_float16.tflite` | Half-precision TFLite model |
| `src/assets/wasm/` | Copy from `node_modules/onnxruntime-web/dist/` |
| `src/assets/tflite-wasm/` | Copy 7 files from `node_modules/@tensorflow/tfjs-tflite/dist/` |

### Inference Pipeline (AnalyzerComponent orchestrates)
1. User uploads image → `ImageProcessorService.processImage()` → CHW Float32Array
2. `LocalInferenceService.loadModel(modelPath)` → creates ONNX session or loads TFLite model
3. `LocalInferenceService.runInference(input)` → raw output tensor
4. `LocalInferenceService.parseDetections(rawData, threshold)` → filtered detections with class labels and bounding boxes
5. `AnalyzerComponent` draws bounding boxes on Canvas
6. `LocalHistoryService.save()` → persists to localStorage
