# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Development Commands ```bash npm run dev # Start Next.js dev server at http://localhost:3000 npm run build # Build for production npm run start # Start production server npm run lint # Run Next.js linting npm run test # Run all tests with Vitest (watch mode) npm run test:run # Run all tests once (CI mode) ``` ## Environment Setup Create `.env.local` in the root directory: ``` GEMINI_API_KEY=your_gemini_api_key OPENAI_API_KEY=your_openai_api_key # Optional, for OpenAI LLM provider KIE_API_KEY=your_kie_api_key # Optional, for Kie.ai models (Sora, Veo, Kling, etc.) ``` ## Architecture Overview Node Banana is a node-based visual workflow editor for AI image generation. Users drag nodes onto a React Flow canvas, connect them via typed handles, and execute pipelines that call AI APIs. ### Core Stack - **Next.js 16** (App Router) with TypeScript - **@xyflow/react** (React Flow) for the node editor canvas - **Konva.js / react-konva** for canvas annotation drawing - **Zustand** for state management (single store pattern) ### Key Files | Purpose | Location | |---------|----------| | Central workflow state & execution logic | `src/store/workflowStore.ts` | | All TypeScript type definitions | `src/types/index.ts` | | Main canvas component & connection validation | `src/components/WorkflowCanvas.tsx` | | Base node component (shared by all nodes) | `src/components/nodes/BaseNode.tsx` | | Image generation API route | `src/app/api/generate/route.ts` | | LLM text generation API route | `src/app/api/llm/route.ts` | | Cost calculations | `src/utils/costCalculator.ts` | | Grid splitting utility | `src/utils/gridSplitter.ts` | ### State Management All application state lives in `workflowStore.ts` using Zustand. Key patterns: - `useWorkflowStore()` hook provides access to nodes, edges, and all actions - `executeWorkflow(startFromNodeId?)` runs the pipeline via topological sort - `getConnectedInputs(nodeId)` retrieves upstream data for a node - `updateNodeData(nodeId, partialData)` updates node state - Auto-save runs every 90 seconds when enabled ### Execution Flow 1. User clicks Run or presses `Cmd/Ctrl+Enter` 2. `executeWorkflow()` performs topological sort on node graph 3. Nodes execute in dependency order, calling APIs as needed 4. `getConnectedInputs()` provides upstream images/text to each node 5. Locked groups are skipped; pause edges halt execution ## AI Models Image generation models (these exist and are recently released): - `gemini-2.5-flash-image` → internal name: `nano-banana` - `gemini-3-pro-image-preview` → internal name: `nano-banana-pro` LLM models: - Google: `gemini-2.5-flash`, `gemini-3-flash-preview`, `gemini-3-pro-preview` - OpenAI: `gpt-4.1-mini`, `gpt-4.1-nano` ## Node Types | Type | Purpose | Inputs | Outputs | |------|---------|--------|---------| | `imageInput` | Load/upload images | reference | image | | `annotation` | Draw on images (Konva) | image | image | | `prompt` | Text prompt input | none | text | | `nanoBanana` | AI image generation | image, text | image | | `llmGenerate` | AI text generation | text, image | text | | `generateAudio` | AI audio/TTS generation | text | audio | | `audioInput` | Load/upload audio files | audio | audio | | `glbViewer` | Load/display 3D GLB models | none | image | | `output` | Display final result | image | none | ## Node Connection System ### Handle Types | Handle Type | Data Format | Description | |-------------|-------------|-------------| | `image` | Base64 data URL | Visual content | | `text` | String | Text content | | `audio` | Base64 data URL | Audio content | ### Connection Rules 1. **Type Matching**: Handles only connect to matching types (`image`→`image`, `text`→`text`) 2. **Direction**: Connections flow from source (output) to target (input) 3. **Multiplicity**: Image inputs accept multiple connections; text inputs accept one ### Data Flow in `getConnectedInputs` Returns `{ images: string[], text: string | null }`. **Image data extracted from:** - `imageInput` → `data.image` - `annotation` → `data.outputImage` - `nanoBanana` → `data.outputImage` **Text data extracted from:** - `prompt` → `data.prompt` - `llmGenerate` → `data.outputText` **Audio data extracted from:** - `audioInput` → `data.audioFile` - `generateAudio` → `data.outputAudio` ## Keyboard Shortcuts - `Cmd/Ctrl + Enter` - Run workflow - `Cmd/Ctrl + C/V` - Copy/paste nodes - `Shift + P` - Add prompt node at center - `Shift + I` - Add image input node - `Shift + G` - Add generate (nanoBanana) node - `Shift + V` - Add video (generateVideo) node - `Shift + L` - Add LLM node - `Shift + A` - Add annotation node - `Shift + T` - Add audio (generateAudio) node - `H` - Stack selected nodes horizontally - `V` - Stack selected nodes vertically - `G` - Arrange selected nodes in grid - `?` - Show keyboard shortcuts ## Adding New Node Types 1. Define the data interface in `src/types/index.ts` 2. Add to `NodeType` union in `src/types/index.ts` 3. Create default data in `createDefaultNodeData()` in `workflowStore.ts` 4. Add dimensions to `defaultDimensions` in `workflowStore.ts` 5. Create the component in `src/components/nodes/` 6. Export from `src/components/nodes/index.ts` 7. Register in `nodeTypes` in `WorkflowCanvas.tsx` 8. Add minimap color in `WorkflowCanvas.tsx` 9. Update `getConnectedInputs()` if the node produces consumable output 10. Add execution logic in `executeWorkflow()` if the node requires processing 11. Update `ConnectionDropMenu.tsx` to include the node in source/target lists ### Handle Naming Convention Use descriptive handle IDs matching the data type: - `id="image"` for image data - `id="text"` for text data ### Validation - Connection validation: `isValidConnection()` in `WorkflowCanvas.tsx` - Workflow validation: `validateWorkflow()` in `workflowStore.ts` ## Adding New Kie.ai Models (SOP) Reference docs: https://docs.kie.ai/llms.txt lists all available model API pages. ### Step 1: Gather API Details Visit the model's doc page on https://docs.kie.ai/ and collect: - Model ID(s) (the `model` param sent to the API) - Capabilities: text-to-image, image-to-image, text-to-video, image-to-video - API endpoint (standard: `/api/v1/jobs/createTask`, or model-specific like Veo's `/api/v1/veo/generate`) - All input parameters: name, type, enum values, defaults, required status - Image/video input parameter name (e.g., `image_urls`, `imageUrls`, `input_urls`) - Polling endpoint (standard: `/api/v1/jobs/recordInfo`, or model-specific) - Response format and status field names - Pricing (per-run cost if available) ### Step 2: Add Model Registry Entry **File:** `src/app/api/models/route.ts` — Add to `KIE_MODELS` array. Each model entry needs: `id`, `name`, `description`, `provider: "kie"`, `capabilities`, `pricing`, `pageUrl`. Use separate entries for each capability variant (e.g., `model/text-to-video` and `model/image-to-video`). ### Step 3: Add Parameter Schema **File:** `src/app/api/models/[modelId]/route.ts` — Add to `getKieSchema()`. Define `parameters` (user-configurable settings) and `inputs` (connectable handles like prompt, images). ### Step 4: Add Default Parameters **File:** `src/app/api/generate/route.ts` — Add case to `getKieModelDefaults()`. Provide required defaults that must be present even if the user doesn't set them. ### Step 5: Add Image Input Key Mapping **File:** `src/app/api/generate/route.ts` — Add to `getKieImageInputKey()`. Map the model to its correct image parameter name if it differs from the default `image_urls`. ### Step 6: Handle Non-Standard API (if applicable) If the model uses different endpoints than `/api/v1/jobs/createTask` and `/api/v1/jobs/recordInfo`: - Add a detection function (e.g., `isVeoModel()`) - Add a model-ID-to-API-model mapping function - Add a custom polling function for the model's status endpoint - Add a branch in `generateWithKie()` for the custom request format ## API Routes All routes in `src/app/api/`: | Route | Timeout | Purpose | |-------|---------|---------| | `/api/generate` | 5 min | Image generation via Gemini | | `/api/llm` | 1 min | Text generation (Google/OpenAI) | | `/api/workflow` | default | Save/load workflow files | | `/api/save-generation` | default | Auto-save generated images | | `/api/logs` | default | Session logging | ## localStorage Keys - `node-banana-workflow-configs` - Project metadata (paths) - `node-banana-workflow-costs` - Cost tracking per workflow - `node-banana-nanoBanana-defaults` - Sticky generation settings ## Git Workflow - The primary development branch is `develop`, NOT `main` or `master` - Always checkout `develop` before creating feature branches: `git checkout develop` - Create feature branches from `develop` using: `feature/` or `fix/` - All PRs MUST target `develop`: use `gh pr create --base develop` - Never push directly to `main`, `master`, or `develop` ## Commits - Commit after each logical task or unit of work is complete. When implementing a multi-task plan, commit after finishing each task — do NOT batch all tasks into a single commit at the end. - Each commit should be atomic and self-contained: one task = one commit. - The .planning directory is untracked, do not attempt to commit any changes to the files in this directory.