A modern, privacy-first digital recipe book with AI-powered PDF extraction
Designed for home cooks who want to digitize their recipe collections without compromising on simplicity and speed.
- About the Project
- Features
- Quick Start
- Installation
- Tech Stack
- Architecture & Design Decisions
- Development
- Deployment
- Database Schema
- API Reference
- Contributing
- Troubleshooting
- License
- Support
Il Mio Ricettario (My Recipe Book) is a full-stack web application that helps home cooks digitize and organize their recipe collections. Built with Next.js and powered by Claude AI, it offers intelligent PDF recipe extraction, making it effortless to convert physical cookbooks or PDF recipe collections into a searchable, organized digital format.
- Text-Focused Design: No image uploads required. Focus on what matters while cooking: ingredients, steps, and techniques.
- Privacy-First Architecture: Your recipes, your data. All data is stored in your own Firebase account with owner-based security rules.
- AI-Powered: Upload PDFs, paste free text, or chat with an AI chef to get new recipe suggestions — all with intelligent categorization and seasonal classification.
- Cooking-Optimized: Screen wake lock, progress tracking, and mobile-first design make it perfect for actual kitchen use.
- Open Source: AGPL-3.0 licensed to ensure the project and its improvements remain open and available to everyone.
Clean, text-based interface optimized for actual cooking use. Focus on content (ingredients, steps, techniques) over visual aesthetics. Your recipes, your data, your device.
This project deliberately avoids image-heavy interfaces common in recipe apps. Instead, it prioritizes speed, clarity, and functionality—exactly what you need when you're cooking.
- Complete CRUD Operations: Create, read, update, and delete recipes with full metadata support
- Sectioned Organization: Organize ingredients and steps into sections (e.g., "For the dough", "For the filling")
- Manual Step Reordering: Move preparation steps up or down while editing a recipe
- Rich Metadata: Track servings, prep time, cook time, difficulty level, and seasonal availability
- Smart Categorization: Organize recipes with customizable categories and subcategories, each with emoji and curated preset colors
- Recipe Search: Fast, real-time search by recipe name with full Italian character support (à, è, ì, ò, ù)
- Multiple Seasons: Assign multiple seasons to recipes (e.g., Pasta e Fagioli for both autumn and winter)
- Advanced Filtering: Filter recipes by category, subcategory, and season simultaneously with live count updates
- Email/Password Authentication: Standard email-based registration and login
- Google OAuth Integration: Quick sign-in with Google accounts
- Firebase Auth: Industry-standard authentication with automatic session management
- User Data Isolation: Every user's recipes are completely isolated with owner-based security rules
- Screen Wake Lock: Uses nosleep.js to prevent your device from going to sleep while cooking
- Interactive Checkboxes: Check off ingredients and steps as you complete them
- Progress Tracking: Visual progress bar shows completion percentage
- Persistent Sessions: Close the app and come back later—your progress is automatically saved
- Serving Size Scaling: Select different serving sizes and ingredient quantities adjust automatically
- Dynamic Step Quantities: When a recipe uses dynamic step references, preparation text scales together with ingredient quantities
- Italian Decimal Format: Properly formatted quantities (e.g., "1,5 kg" instead of "1.5 kg")
- Manual Finish Flow: When all ingredients and steps are completed, the app invites you to finish the cooking session explicitly
- Centralized View: See all your in-progress cooking sessions in one place
- Quick Resume: Jump back into any active cooking session with one click
- Progress At-A-Glance: See how far you've progressed in each recipe
- Real-Time Updates: Session progress updates instantly across all devices
- Most Cooked Recipes: See which dishes you prepare most often
- Completion History: Review your latest completed cooking sessions
- Totals At-A-Glance: Track how many cooking sessions you have finished over time
Three ways to get recipes in — all powered by Claude AI:
PDF Extraction:
- Automatic Recipe Extraction: Upload PDF cookbooks and Claude AI extracts all recipes automatically
- Multi-Page Support: Processes entire PDF documents, extracting every recipe found
- Maximum file size: 4.4MB (Vercel request body limit)
Free-Text Formatting:
- Type or paste any recipe: Write rough notes, copy from a website, or dictate informally
- Claude reformats it: Structures ingredients, steps, sections, and metadata automatically
- No file required — just text
AI Chat Recipe Generation (new):
- Ask the AI chef: Describe ingredients you have, request a cuisine style, or ask for something new
- Cookbook-aware: The AI knows your existing recipes and avoids suggesting duplicates
- Multi-turn: Refine suggestions with follow-up messages ("make it lighter", "add more vegetables")
- Generated recipes appear as preview cards, ready to save with one click
All modes share:
- Structure Preservation: Maintains the original organization of ingredients and steps
- Dynamic Quantity References for AI Recipes: Newly AI-generated recipes can link step quantities to ingredient scaling automatically
- Intelligent Categorization: AI suggests appropriate categories (using existing ones or proposing new ones)
- Seasonal Classification: Analyzes ingredients against an Italian seasonal ingredient database
- Smart Normalization: Converts times to minutes, capitalizes section headers, standardizes formatting
- Editable Preview: Review and modify all recipes before saving
- Transparency: Recipes and categories suggested by AI are clearly marked with badges
Technical Details:
- Powered by Claude Sonnet 4.6 (200K token context window)
- Native PDF support with base64 encoding
- Endpoints:
/api/extract-recipes(PDF),/api/format-recipe(text),/api/chat-recipe(chat)
Italian Seasonal Ingredient Database:
- Primavera (Spring): Asparagus, artichokes, fava beans, peas, strawberries
- Estate (Summer): Tomatoes, eggplant, zucchini, basil, peaches
- Autunno (Autumn): Pumpkin, mushrooms, chestnuts, radicchio
- Inverno (Winter): Black cabbage, citrus, turnip greens, fennel
- Tutte le stagioni (All seasons): For recipes without strong seasonal ingredients
Plan your meals for the week — AI-assisted or fully manual.
- AI-generated plans: The AI selects recipes from your cookbook and optionally creates new ones tailored to the season
- Manual mode: Start with an empty grid and fill each slot by picking from your cookbook
- Per-meal control: Set how many new AI-generated recipes to include for each meal type (breakfast, lunch, dinner)
- Category hints: Tell the AI which category to prefer for each meal type
- Edit after generation: Click any slot to swap the recipe at any time
- Save AI recipes: Newly generated recipes (shown in purple) can be saved to your cookbook in one click — with AI-suggested category and seasons pre-filled
- Quick navigation: Green cells link directly to the full recipe page
- Weekly history: Keep multiple saved weeks and move between past, current, and future plans
- Recoverable setup: If a week has no plan yet, the planner opens setup for that week without losing access to already saved weeks
- Persistent: Plans are saved to Firebase and the current week is restored automatically on your next visit
- Orientation-Aware Navigation:
- Desktop (≥ 1440px): Persistent sidebar navigation always visible
- Mobile Portrait: Bottom navigation bar with 4 main tabs + "More" sheet
- Mobile Landscape: Hamburger menu with slide-out sidebar
- Custom 1440px Breakpoint: Optimized for tablets to use mobile UI (better for cooking)
- Touch-Friendly: Large tap targets, swipe gestures, and mobile-optimized interactions
- Responsive Tables: Ingredient and step lists adapt gracefully to all screen sizes
- Apple Home Screen Icon: Custom icon when adding the app to iOS/iPad home screen
Get up and running in less than 5 minutes.
- Node.js 18+ and npm
- Firebase account (free tier works perfectly)
- Anthropic API key (for AI features—free trial available)
# 1. Clone the repository
git clone https://github.com/GiuseppeDM98/il-mio-ricettario.git
cd il-mio-ricettario
# 2. Install dependencies
npm install
# 3. Configure environment variables
cp .env.example .env.local
# Edit .env.local with your Firebase config, Firebase Admin credentials, and Anthropic API key
# 4. Run the development server
npm run devOpen http://localhost:3000 in your browser. You should see the login page.
Note: For full functionality, you'll need to complete the Firebase setup (see Installation below).
Complete setup guide for local development.
- Node.js: 18.0.0 or higher
- npm: 9.0.0 or higher (or yarn 1.22.0+)
- Modern Browser: Chrome 90+, Firefox 88+, Safari 14+, or Edge 90+
- Git: For cloning the repository
- Go to Firebase Console
- Click "Add project" or "Create a project"
- Enter project name (e.g., "my-recipe-book")
- (Optional) Enable Google Analytics
- Click "Create project" and wait for initialization
Verification: You should see the Firebase project dashboard
- In the Firebase Console sidebar, click Authentication
- Click "Get started"
- Go to the "Sign-in method" tab
Enable Email/Password:
- Click "Email/Password"
- Toggle the first switch to "Enabled"
- Click "Save"
Enable Google Sign-In:
- Click "Google"
- Toggle to "Enabled"
- Select a project support email
- Click "Save"
Verification: Both "Email/Password" and "Google" should show "Enabled" status
- In the sidebar, click Firestore Database
- Click "Create database"
- Security rules: Select "Start in production mode" (we'll deploy custom rules next)
- Location: Choose the closest region to your users
- Europe:
eur3(Belgium) - US:
us-central(Iowa) - Asia:
asia-southeast1(Singapore) - Important: This cannot be changed later!
- Europe:
- Click "Enable"
Verification: Empty Firestore console should appear
The project includes owner-based security rules that ensure users can only access their own data.
# Install Firebase CLI globally (if not already installed)
npm install -g firebase-tools
# Login to Firebase
firebase login
# Link your local project to your Firebase project
firebase use --add
# Select your project from the list
# Use "default" as the alias
# Deploy the security rules
firebase deploy --only firestore:rulesExpected Output:
=== Deploying to 'your-project-name'...
i deploying firestore
✔ firestore: released rules firestore.rules to cloud.firestore
✔ Deploy complete!
Verification:
- In Firebase Console, go to Firestore Database → Rules tab
- You should see the deployed rules with
isAuthenticated()andisOwner()functions
- In Firebase Console, click the gear icon ⚙️ → Project settings
- Scroll down to "Your apps" section
- If you don't have a web app yet:
- Click the web icon
</> - App nickname: "Il Mio Ricettario Web"
- Don't check "Firebase Hosting"
- Click "Register app"
- Click the web icon
- Copy the
firebaseConfigobject:
const firebaseConfig = {
apiKey: "AIzaSyC...",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789012",
appId: "1:123456789012:web:abcdef123456"
};Save these values—you'll need them for the next step.
- Go to Anthropic Console
- Sign up or log in
- Navigate to API Keys section
- Click "Create Key"
- Name: "Il Mio Ricettario"
- Copy the API key (format:
sk-ant-api03-...)
Important:
- The key is only shown once—save it securely
- New accounts get $5 in free credits
- Approximate cost: $0.05-$0.15 per PDF (10-20 recipes)
Create .env.local file in the project root:
cp .env.example .env.localEdit .env.local with your credentials:
# Firebase Configuration (all 6 values from Step 5)
NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key_here
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
# Anthropic AI Configuration (from Step 6)
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# Firebase Admin for server-side API auth verification
# Recommended: base64-encoded service account JSON in one variable
FIREBASE_ADMIN_CREDENTIALS_BASE64=your_base64_service_account_json
# Fallback: or provide the same credentials split across these variables
FIREBASE_ADMIN_PROJECT_ID=your_project_id
FIREBASE_ADMIN_CLIENT_EMAIL=firebase-adminsdk-xxx@your_project.iam.gserviceaccount.com
FIREBASE_ADMIN_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# Optional: Toggle user registrations
NEXT_PUBLIC_REGISTRATIONS_ENABLED=trueEnvironment Variable Reference:
| Variable | Required | Scope | Description |
|---|---|---|---|
NEXT_PUBLIC_FIREBASE_API_KEY |
Yes | Client+Server | Firebase Web API key |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN |
Yes | Client+Server | Firebase authentication domain |
NEXT_PUBLIC_FIREBASE_PROJECT_ID |
Yes | Client+Server | Firebase project identifier |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET |
Yes | Client+Server | Cloud Storage bucket URL |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID |
Yes | Client+Server | Firebase Cloud Messaging sender ID |
NEXT_PUBLIC_FIREBASE_APP_ID |
Yes | Client+Server | Firebase app identifier |
ANTHROPIC_API_KEY |
Yes* | Server Only | Claude API key for PDF extraction |
FIREBASE_ADMIN_CREDENTIALS_BASE64 |
Yes** | Server Only | Base64-encoded Firebase service account JSON for API token verification |
FIREBASE_ADMIN_PROJECT_ID |
Fallback | Server Only | Firebase Admin project ID when not using base64 credentials |
FIREBASE_ADMIN_CLIENT_EMAIL |
Fallback | Server Only | Firebase Admin client email when not using base64 credentials |
FIREBASE_ADMIN_PRIVATE_KEY |
Fallback | Server Only | Firebase Admin private key when not using base64 credentials |
NEXT_PUBLIC_REGISTRATIONS_ENABLED |
No | Client+Server | Enable/disable new user registrations |
* Required for AI features. The app works without it for manual recipe entry. ** Required for AI features because all AI routes now verify the caller's Firebase ID token server-side.
Security Note:
- Variables with
NEXT_PUBLIC_prefix are exposed to the browser ANTHROPIC_API_KEYdoes NOT have this prefix—it's server-only for security- Firebase Admin credentials are server-only and must never use the
NEXT_PUBLIC_prefix - Protected AI routes verify Firebase ID tokens server-side, so deploys need Firebase Admin credentials in addition to the public Firebase web config
# Start the development server
npm run devThe application will start at http://localhost:3000
Test the Installation:
- Open http://localhost:3000
- Register a new account
- Create a test recipe manually
- (Optional) Test PDF extraction with a small PDF file
-
Next.js 16.1.6 - React framework with App Router
- Server and client components
- File-based routing
- API routes for backend functionality
- Standalone output mode for optimized deployment
-
React 18.2 - UI library
- Concurrent features for better performance
- Hooks for state management
- Server components support
-
TypeScript 5.3 - Static type checking
- Strict mode enabled
- Full type safety across the codebase
- Enhanced developer experience with IntelliSense
-
Tailwind CSS 3.4 - Utility-first CSS framework
- Custom HSL design system
- Responsive breakpoints (custom 1440px
lgbreakpoint) - Mobile-first approach
-
Radix UI - Accessible component primitives
- Dialog, Toast, Sheet components
- Unstyled and accessible by default
- WAI-ARIA compliant
-
Lucide React - Icon library
- 500+ consistent icons
- Tree-shakeable for optimal bundle size
- Customizable size and color
-
class-variance-authority - Type-safe component variants
- Consistent component API
- TypeScript autocompletion
- Better DX for component styling
-
Firebase Auth 10.7 - Authentication
- Email/password authentication
- Google OAuth provider
- Session management
- Secure token refresh
-
Cloud Firestore 10.7 - NoSQL database
- Real-time synchronization
- Offline support
- Powerful querying
- Automatic scaling
-
Anthropic Claude API - AI-powered features
- Claude Sonnet 4.5 model
- 200K token context window
- Native PDF support
- Vision capabilities for document analysis
-
nosleep.js 0.12 - Screen wake lock
- Prevents device sleep during cooking mode
- Cross-browser compatible
- No permissions required
-
uuid 9.0 - Unique ID generation
- v4 (random) UUIDs for ingredients and steps
- RFC4122 compliant
-
react-hot-toast 2.6 - Toast notifications
- Beautiful, customizable toasts
- Promise-based API
- Keyboard accessible
-
zod 4.1 - Runtime type validation
- TypeScript-first schema validation
- Type inference
- Error messages
-
Jest 30.2 - Testing framework
- Unit and integration tests
- Snapshot testing
- Coverage reporting
-
@testing-library/react 16.3 - Testing utilities
- User-centric testing approach
- Best practices encouraged
- Accessible queries
-
ESLint 8.x - Code linting
- Next.js recommended rules
- Catches common errors
- Enforces code style
- Package Overrides (via package.json):
glob >= 10.4.6- Fixes security vulnerabilitiesundici >= 6.21.2- Patches HTTP client vulnerabilities
Understanding the "why" behind key technical choices.
src/
├── app/
│ ├── (auth)/ # Public authentication routes
│ │ ├── login/ # Login page
│ │ └── register/ # Registration page
│ ├── (dashboard)/ # Protected application routes
│ │ ├── ricette/ # Recipe list, detail, edit, cooking mode
│ │ │ ├── [id]/ # Dynamic recipe routes
│ │ │ │ ├── edit/ # Edit recipe page
│ │ │ │ └── cooking/ # Cooking mode
│ │ │ └── new/ # Create recipe page
│ │ ├── categorie/ # Category management
│ │ ├── cotture-in-corso/ # Active cooking sessions dashboard
│ │ └── assistente-ai/ # AI Assistant (PDF, free-text, chat)
│ └── api/ # Server-side API routes
│ ├── extract-recipes/ # PDF extraction endpoint
│ └── suggest-category/ # AI categorization endpoint
├── components/
│ ├── ui/ # Base UI components (Button, Card, Input, etc.)
│ ├── recipe/ # Recipe-specific components
│ │ ├── recipe-form.tsx
│ │ ├── recipe-card.tsx
│ │ ├── ingredient-list.tsx
│ │ └── steps-list.tsx
│ ├── auth/ # Authentication components
│ │ ├── auth-form.tsx
│ │ └── protected-route.tsx
│ └── layout/ # Layout components
│ ├── header.tsx
│ ├── sidebar.tsx
│ ├── bottom-navigation.tsx
│ └── more-sheet.tsx
├── lib/
│ ├── firebase/ # Firebase service layer
│ │ ├── config.ts # Firebase initialization (singleton)
│ │ ├── auth.ts # Auth helpers
│ │ ├── firestore.ts # Firestore CRUD operations
│ │ ├── categories.ts # Category management
│ │ └── cooking-sessions.ts # Session management
│ ├── hooks/ # Custom React hooks
│ │ ├── useAuth.ts # Authentication hook
│ │ └── useRecipes.ts # Recipe data fetching
│ ├── context/ # React context providers
│ │ └── AuthContext.tsx # Global auth state
│ └── utils/ # Utility functions
│ ├── recipe-parser.ts # Markdown → Recipe parser
│ └── ingredient-scaler.ts # Quantity scaling logic
└── types/
└── index.ts # TypeScript type definitions
Decision: Use output: 'standalone' in Next.js configuration
Why:
- Creates a self-contained build with all dependencies bundled
- Optimized for deployment to Vercel, Docker, or any Node.js environment
- Significantly smaller bundle size compared to default output
- Faster cold starts in serverless environments
Trade-off:
- Cannot use static export features (but we prioritize dynamic API routes)
Benefit:
- 40-50% reduction in deployment size
- Better performance in production
Decision: Every Firestore document has a userId field, enforced by security rules
Why:
- Multi-tenant architecture: One Firestore database serves all users
- Strong data isolation: Users can only access their own data
- GDPR-friendly: Clear data ownership and easy user deletion
- Cost-effective: No need for separate database instances
Implementation:
// Firestore security rules
match /recipes/{recipeId} {
allow read, update, delete: if isOwner(resource.data.userId);
allow create: if isAuthenticated() && request.resource.data.userId == request.auth.uid;
}Alternative Considered:
- Separate Firestore projects per user (rejected: cost prohibitive, complex management)
Benefit:
- Bulletproof privacy
- Scales to millions of users on free tier
- Easy to audit and verify security
Decision: Use flat arrays with section field instead of nested objects
Why:
- Firestore has limitations with deeply nested queries
- Easier to filter and sort (e.g., "show only ingredients in section X")
- Simpler to scale quantities across all ingredients
- Better performance for large recipes
Data Structure:
ingredients: [
{ id: "1", name: "Flour", quantity: "500g", section: "For the dough" },
{ id: "2", name: "Eggs", quantity: "2", section: "For the dough" },
{ id: "3", name: "Tomato sauce", quantity: "200ml", section: "For the filling" }
]Alternative Considered:
// Rejected: Nested structure
sections: {
"For the dough": {
ingredients: [...]
}
}Benefit:
- Firestore queries work smoothly
- Easy to reorder sections
- Compatible with array operators
Decision: Keep AI functionality in Next.js API routes, not client-side
Why:
- Security: Anthropic API key never exposed to browser
- Cost Control: All API calls logged and rate-limited server-side
- Consistency: Same AI model and prompt for all users
- Error Handling: Better error messages and retry logic
Implementation:
// src/app/api/extract-recipes/route.ts
// ANTHROPIC_API_KEY is server-only (no NEXT_PUBLIC_ prefix)
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
});Alternative Considered:
- Client-side AI calls (rejected: security risk, no cost control)
Benefit:
- Zero risk of API key leakage
- Can implement sophisticated rate limiting
- Centralized logging and monitoring
Decision: Use a setup screen to select servings before entering cooking mode
Why:
- Prevents duplicate session creation (common bug with useEffect-based approaches)
- Explicit user intent: User consciously starts cooking
- Opportunity to configure (serving size selection)
- Cleaner state management
Flow:
Recipe Detail → Click "Start Cooking" → Setup Screen (select servings)
→ Create Session → Cooking Mode
Alternative Rejected:
// Anti-pattern: useEffect automatically creates session
useEffect(() => {
if (!session) {
createCookingSession(recipeId);
}
}, [recipeId]);Problem: Creates multiple sessions on re-renders, race conditions
Benefit:
- Reliable session creation (one session per cooking attempt)
- Better UX: user has control
- Easier to test
Decision: Use 1440px as the lg breakpoint instead of standard 1024px
Why:
- Standard breakpoints: 1024px typically separates tablet from desktop
- Our use case: Tablets (like iPad at 1024px) are better served by mobile UI while cooking
- Rationale:
- Mobile UI has bottom navigation (easier to reach with messy hands)
- Sidebar navigation requires cleaner hands and more precision
- 1440px better separates "actual desktop" from "tablet/laptop"
Implementation:
// tailwind.config.ts
module.exports = {
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1440px', // Custom: Higher than standard 1024px
xl: '1536px',
}
}
}Mobile-Specific Styles:
// Use max-lg:portrait: not just portrait:
className="max-lg:portrait:block hidden"Benefit:
- Better experience on tablets
- More users get the optimized cooking UI
- Desktop users get full sidebar experience
Decision: Use AGPL-3.0 instead of MIT or Apache 2.0
Why:
- Strong Copyleft: Ensures modifications remain open source
- Network Use = Distribution: If you host this as a SaaS, you must provide source to users
- Community Protection: Prevents proprietary forks that don't give back
- Philosophical Alignment: Recipe knowledge should be shared freely
Implications:
- Commercial use allowed
- Modifications allowed
- But: Must publish modifications if you host publicly
- SaaS deployments must provide source code access
Alternative Considered:
- MIT License (rejected: too permissive, allows proprietary forks)
Benefit:
- Guarantees project stays open forever
- Encourages community contributions
- Aligns with values of food and recipe sharing
Recipe CRUD Flow:
User Action (Create/Edit)
↓
Component (RecipeForm)
↓
Firebase Service Layer (firestore.ts)
↓
Cloud Firestore (Firebase)
↓
Security Rules Enforcement
↓
Database Persistence
↓
Real-time Update to UI
AI Extraction Flow:
User Uploads PDF
↓
Client Validation (size, type)
↓
POST /api/extract-recipes
↓
Server reads file, converts to base64
↓
Claude API call (Sonnet 4.6)
↓
Markdown response
↓
Parser (recipe-parser.ts)
↓
Structured recipe objects
↓
Preview component (editable)
↓
User saves → Firestore
Cooking Mode Flow:
Recipe Detail Page
↓
User clicks "Start Cooking"
↓
Setup Screen (select servings)
↓
Create Cooking Session (Firestore)
↓
Cooking UI (checkboxes, wake lock)
↓
Checkbox changes → Auto-save to Firestore
↓
100% complete → Show "Finish cooking" CTA
↓
User confirms → Write cooking history + close session
Guide for developers working on the codebase.
| Script | Command | Description |
|---|---|---|
| Development | npm run dev |
Start Next.js development server on port 3000 with hot reload |
| Build | npm run build |
Create production build (standalone mode) |
| Start | npm run start |
Start production server (requires build first) |
| Export | npm run export |
Build and export static site for Firebase Hosting |
| Test | npm run test |
Run Jest test suite |
| Lint | npm run lint |
Run ESLint checks |
- Type Definitions: All types are centralized in
src/types/index.ts - Interfaces vs Types:
- Use
interfacefor domain models (e.g.,Recipe,Category) - Use
typefor utility types (e.g., type aliases, unions)
- Use
- Strict Mode: Always enabled—no
anytypes allowed- Use
unknownif type is truly unknown, then narrow with type guards
- Use
- Export Pattern: Named exports preferred over default exports
Example:
// src/types/index.ts
export interface Recipe {
id: string;
userId: string;
title: string;
ingredients: Ingredient[];
steps: Step[];
// ...
}
// Good
import { Recipe } from '@/types';
// Avoid
import Recipe from '@/types';- Client Components: All page components use
'use client'directive - Server Components: Only API routes are server-side
- Hooks: Custom hooks in
src/lib/hooks/, prefixed withuse - Context: Global state in
src/lib/context/, minimal usage
Example:
// Page component
'use client';
import { useAuth } from '@/lib/hooks/useAuth';
export default function RecipesPage() {
const { user } = useAuth();
// ...
}Critical Rule: Use null for optional fields, never undefined
Why: Firestore doesn't store undefined values, leading to inconsistencies
// ✅ Good
const recipe = {
categoryId: selectedCategory?.id || null,
prepTime: prepTimeValue || null
};
// ❌ Bad - undefined fields won't be saved
const recipe = {
categoryId: selectedCategory?.id,
prepTime: prepTimeValue
};Query Pattern: Always filter by userId
// ✅ Good
const recipesRef = collection(db, 'recipes');
const q = query(recipesRef, where('userId', '==', userId));
// ❌ Bad - returns all users' recipes
const q = query(recipesRef);Timestamps: Use serverTimestamp() for consistency
import { serverTimestamp } from 'firebase/firestore';
const recipe = {
// ...
createdAt: serverTimestamp(),
updatedAt: serverTimestamp()
};- Tailwind Only: No custom CSS files (except global.css for base styles)
- Semantic Tokens: Use design system variables
bg-primary,text-primary,border-primarybg-secondary,text-secondarybg-muted,text-muted-foregroundbg-destructive,text-destructive-foreground
- Mobile-First: Add breakpoints progressively
- Base styles = mobile
sm:= 640px+md:= 768px+lg:= 1440px+
- Responsive Navigation: Use
max-lg:portrait:for mobile-specific styles
// ✅ Good - Mobile-first, semantic tokens
<button className="bg-primary text-primary-foreground px-4 py-2 rounded-md hover:bg-primary/90 sm:px-6 sm:py-3">
Save Recipe
</button>
// ✅ Good - Orientation-specific
<nav className="max-lg:portrait:block max-lg:landscape:hidden lg:block">
{/* Navigation content */}
</nav>
// ❌ Bad - Custom colors, desktop-first
<button className="bg-blue-600 text-white lg:hidden">
Save
</button>Utility Function: Use cn() for conditional classes
import { cn } from '@/lib/utils';
<div className={cn(
"base-class",
isActive && "active-class",
isDisabled && "disabled-class"
)}>
Content
</div># Run all tests
npm test
# Watch mode (re-runs on file changes)
npm test -- --watch
# Coverage report
npm test -- --coverage
# Run specific test file
npm test -- Header.test.tsxSetup: Tests use Jest + React Testing Library
Example Test:
// src/components/layout/__tests__/Header.test.tsx
import { render, screen } from '@testing-library/react';
import Header from '../Header';
// Mock Firebase auth
jest.mock('@/lib/firebase/config', () => ({
auth: {}
}));
// Mock auth hook
jest.mock('@/lib/hooks/useAuth', () => ({
useAuth: () => ({
user: { uid: '123', email: 'test@example.com' },
signOut: jest.fn()
})
}));
describe('Header', () => {
it('renders user email when authenticated', () => {
render(<Header />);
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});
});Best Practices:
- Mock external dependencies (Firebase, hooks)
- Test user interactions, not implementation details
- Use accessible queries (
getByRole,getByLabelText) - Follow Testing Library principles
// Enable detailed Firestore logs (development only)
import { setLogLevel } from 'firebase/firestore';
if (process.env.NODE_ENV === 'development') {
setLogLevel('debug');
}Issue: "Firebase Auth not initialized"
- Cause: Missing environment variables
- Solution: Check
.env.local, restart dev server
Issue: "Permission denied" in Firestore
- Cause: Security rules not deployed or query doesn't filter by
userId - Solution:
Check query includes
firebase deploy --only firestore:rules
where('userId', '==', userId)
Issue: Module not found errors
- Cause: TypeScript path aliases not resolving
- Solution: Check
tsconfig.jsonhas correct paths configuration
Issue: Hydration mismatch warnings
- Cause: Browser-only code running on server
- Solution: Use
'use client'directive or check fortypeof window !== 'undefined'
Deploy Il Mio Ricettario to production. For detailed deployment instructions, see SETUP.md.
Best for: Most users, especially those new to deployment
Advantages:
- Zero-configuration deployment
- Automatic HTTPS and global CDN
- Serverless functions for API routes (no server management)
- Environment variable management in dashboard
- Git integration (auto-deploy on push)
- Free tier: 100GB bandwidth/month, unlimited projects
Limitations:
- 4.4MB request body limit (affects maximum PDF size)
- 10-second serverless function timeout on free tier
- Vendor lock-in to Vercel platform
Quick Deploy Steps:
- Push code to GitHub
- Import project in Vercel Dashboard
- Add environment variables (
NEXT_PUBLIC_FIREBASE_*,ANTHROPIC_API_KEY, and Firebase Admin credentials) - Deploy
- Add Vercel domain to Firebase authorized domains
Production auth note:
/api/extract-recipes,/api/format-recipe,/api/suggest-category,/api/chat-recipe, and/api/plan-mealsall verify Firebase ID tokens server-sideNEXT_PUBLIC_FIREBASE_*alone are not enough for those endpoints- On Vercel, prefer
FIREBASE_ADMIN_CREDENTIALS_BASE64to avoid multiline private key formatting issues
Full guide: SETUP.md
Best for: Users who want everything on Firebase, or need static hosting
Advantages:
- Same provider as Firestore (single dashboard)
- Free tier: 10GB storage, 360MB/day transfer
- Custom domains with auto-SSL
- SPA-friendly hosting
Limitations:
- Static export only (no server-side rendering)
- API routes require Cloud Functions setup (not covered in basic setup)
- AI extraction features need extra configuration
- Build-time environment variables only
Quick Deploy Steps:
- Change
next.config.jstooutput: 'export' - Build:
npm run build - Deploy:
firebase deploy --only hosting
Full guide: SETUP.md - Firebase Hosting
Best for: Users who want full control or want to run the app on their own machine/VPS
Advantages:
- Complete control over infrastructure
- No vendor lock-in
- Can deploy anywhere (AWS, GCP, Azure, DigitalOcean, etc.)
- Customizable resource allocation
- Same Next.js application codebase as Vercel
Limitations:
- You manage updates, logs, TLS, backups, and uptime
- Google sign-in in production requires a public hostname and Firebase authorized-domain setup
- If you deploy behind a reverse proxy, you must keep the external app URL stable
Recommended path: Docker Compose on a single machine
Quick Start:
# 1. Copy the example env file
cp .env.example .env.local
# 2. Fill in Firebase + Anthropic values
# 3. Build and run
docker compose --env-file .env.local up --buildOpen http://localhost:3000.
Environment model:
NEXT_PUBLIC_FIREBASE_*andNEXT_PUBLIC_REGISTRATIONS_ENABLEDare build-time sensitive in Next.js because they are embedded in the client bundle duringnext buildANTHROPIC_API_KEYis runtime-only and stays server-sideFIREBASE_ADMIN_CREDENTIALS_BASE64(or the splitFIREBASE_ADMIN_*fallback variables) is runtime-only and is required so the server can verify Firebase ID tokens on protected AI routescompose.yamlpasses the public Firebase values as Docker build args and keeps the same values at runtime so the deployment contract stays explicit- Use
--env-file .env.localbecause Docker Compose reads.envautomatically, but not.env.local
Google sign-in note:
- Local Docker on
localhostworks iflocalhostis already authorized in Firebase Auth - Self-hosted production works only when your public hostname is added to Firebase Authentication → Authorized domains
- Docker is not the blocker; the deployed origin is
- If you do not want to configure Google OAuth for self-hosted installs, set
NEXT_PUBLIC_REGISTRATIONS_ENABLED=false
Direct Docker run:
docker build \
--build-arg NEXT_PUBLIC_FIREBASE_API_KEY=your_key \
--build-arg NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com \
--build-arg NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id \
--build-arg NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com \
--build-arg NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id \
--build-arg NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id \
--build-arg NEXT_PUBLIC_REGISTRATIONS_ENABLED=true \
-t il-mio-ricettario .
docker run --rm -p 3000:3000 \
-e ANTHROPIC_API_KEY=your_anthropic_api_key_here \
-e FIREBASE_ADMIN_CREDENTIALS_BASE64=your_base64_service_account_json \
il-mio-ricettarioCommon Docker Compose commands:
# Build the image only
docker compose --env-file .env.local build
# Build and run in the foreground
docker compose --env-file .env.local up --build
# Build and run in the background
docker compose --env-file .env.local up --build -d
# Start without rebuilding
docker compose --env-file .env.local up -d
# Stop and remove the container
docker compose --env-file .env.local down
# Follow logs
docker compose --env-file .env.local logs -f appUse --env-file .env.local on Compose commands for consistency, since the project keeps Docker variables in .env.local instead of .env.
Compose service:
services:
app:
build:
context: .
ports:
- "3000:3000"
restart: unless-stoppedFull guide: SETUP.md - Docker Compose
Firestore collections and document structures.
users/{uid}- User profilesrecipes/{id}- Recipe documentscategories/{id}- User-created categoriessubcategories/{id}- Subcategories nested under categoriescooking_sessions/{id}- Active cooking progress tracking
User profile information.
interface User {
uid: string;
email: string;
displayName: string | null;
photoURL: string | null;
createdAt: Timestamp;
}Security: Users can only read/update their own profile.
Complete recipe documents with all data.
interface Recipe {
id: string;
userId: string; // Owner (required for security)
title: string;
ingredients: Ingredient[]; // Array of ingredient objects
steps: Step[]; // Array of step objects
servings: number;
prepTime: number | null; // Minutes
cookTime: number | null; // Minutes
difficulty: string | null; // "Facile", "Media", "Difficile"
categoryId: string | null;
subcategoryId: string | null;
season: Season | null; // Seasonal classification
aiSuggested: boolean; // True if AI-suggested during extraction
createdAt: Timestamp;
updatedAt: Timestamp;
}
interface Ingredient {
id: string; // UUID v4
name: string;
quantity: string;
section: string | null; // e.g., "Per la pasta", "Per il condimento"
}
interface Step {
id: string; // UUID v4
description: string;
section: string | null; // e.g., "Preparazione", "Cottura"
sectionOrder: number; // Preserves order from PDF extraction
}
type Season = 'primavera' | 'estate' | 'autunno' | 'inverno' | 'tutte_stagioni';Key Features:
userIdfield enforced by security rulesnullfor optional fields (notundefined)- Flat arrays with
sectionfield (not nested objects) sectionOrderpreserves original structure from PDFaiSuggestedflag for transparency
Security:
- Read/Write only by owner (
userId == request.auth.uid) - Create requires authenticated user and setting own
userId
User-created recipe categories.
interface Category {
id: string;
userId: string;
name: string;
emoji: string; // e.g., "🍝", "🍰"
color: string; // Hex color, e.g., "#FF6B6B"
createdAt: Timestamp;
}Default Categories: New users get 10 default Italian categories:
- Antipasti (🥗), Primi Piatti (🍝), Secondi (🍖), Contorni (🥕)
- Dolci (🍰), Pane e Pizza (🍞), Salse e Condimenti (🧈)
- Conserve (🫙), Bevande (🍹), Altro (📋)
Security: Owner-only access
Optional subcategories nested under categories.
interface Subcategory {
id: string;
userId: string;
categoryId: string; // Parent category
name: string;
createdAt: Timestamp;
}Example:
- Category: "Primi Piatti"
- Subcategories: "Pasta", "Risotti", "Zuppe"
Security: Owner-only access
Active cooking session with progress tracking.
interface CookingSession {
id: string;
userId: string;
recipeId: string;
recipeTitle: string;
servings: number; // Selected serving size (may differ from recipe default)
checkedIngredients: string[]; // Array of ingredient IDs
checkedSteps: string[]; // Array of step IDs
createdAt: Timestamp;
updatedAt: Timestamp;
}Lifecycle:
- Created when user starts cooking mode
- Updated when checkboxes are toggled
- Auto-deleted when 100% complete (all ingredients + steps checked)
Security: Owner-only access
All collections use owner-based access control:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Recipe collection
match /recipes/{recipeId} {
allow read, update, delete: if isOwner(resource.data.userId);
allow create: if isAuthenticated()
&& request.resource.data.userId == request.auth.uid;
}
// Categories, Subcategories, Cooking Sessions: same pattern
// ...
}
}Deployment: Rules are deployed from firebase/firestore.rules via Firebase CLI
Server-side API endpoints for AI functionality.
Extract recipes from PDF using Claude AI.
Endpoint: POST /api/extract-recipes
Content-Type: multipart/form-data
Request:
POST /api/extract-recipes
Content-Type: multipart/form-data
Body:
file: File (PDF, max 4.4MB)Response (Success):
{
"success": true,
"extractedRecipes": "# Pasta al Pomodoro\n\n## Ingredienti\n\n### Per la pasta\n- Pasta: 320g\n...",
"metadata": {
"pageCount": 5,
"fileSize": 2048576
}
}Response (Error):
{
"success": false,
"error": "File too large. Maximum size is 4.4MB."
}Error Codes:
400: No file provided400: Invalid file type (must be PDF)413: File too large (> 4.4MB)500: Claude API error500: Internal server error
Implementation Details:
- Model: Claude Sonnet 4.5 (claude-sonnet-4-5-20241022)
- Context Window: 200,000 tokens
- Max Output: 16,000 tokens
- Vision: Native PDF support (base64 encoding)
- Prompt: Italian-language extraction with detailed instructions
- Rate Limiting: Server-side (via Anthropic API limits)
Performance:
- Small PDF (1-5 recipes, < 1MB): 15-30 seconds
- Medium PDF (10-20 recipes, 2-3MB): 45-90 seconds
- Large PDF (20+ recipes, 3-4MB): 90-180 seconds
AI suggests category and season for a recipe.
Endpoint: POST /api/suggest-category
Content-Type: application/json
Request:
{
"recipeTitle": "Risotto ai Funghi Porcini",
"ingredients": [
{ "name": "funghi porcini", "quantity": "300g" },
{ "name": "riso carnaroli", "quantity": "320g" },
{ "name": "brodo vegetale", "quantity": "1l" }
],
"userCategories": [
{ "id": "cat1", "name": "Primi Piatti" },
{ "id": "cat2", "name": "Secondi" }
]
}Response:
{
"success": true,
"suggestion": {
"categoryName": "Primi Piatti",
"season": "autunno",
"isNewCategory": false
}
}Season Values:
primavera(Spring)estate(Summer)autunno(Autumn)inverno(Winter)tutte_stagioni(All seasons)
Logic:
-
Category Suggestion:
- Analyzes recipe title and ingredients
- Matches against user's existing categories
- If no match, proposes new category name
- Sets
isNewCategory: trueif proposing new category
-
Season Detection:
- Analyzes ingredients against Italian seasonal database
- Returns most common season among recognized ingredients
- Defaults to
tutte_stagioniif ambiguous or no seasonal ingredients found
Example Seasonal Ingredients:
- Primavera: asparagi, carciofi, fave, piselli, fragole
- Estate: pomodori, melanzane, zucchine, basilico, pesche
- Autunno: zucca, funghi, castagne, radicchio, uva
- Inverno: cavolo nero, agrumi, cime di rapa, finocchi
We welcome contributions from the community! Here's how to get started.
-
Fork the Repository
- Click "Fork" on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/il-mio-ricettario.git
-
Create a Feature Branch
git checkout -b feature/amazing-feature
-
Read the Documentation
- CLAUDE.md - Project overview and AI guidelines
- AGENTS.md - Common gotchas and patterns (30min+ debug issues)
- COMMENTS.md - Code commenting guidelines
-
Make Your Changes
- Write clean, well-documented code
- Follow the coding conventions (see Development)
- Add tests for new features
-
Commit Your Changes
git commit -m 'Add amazing feature'- Use clear, descriptive commit messages
- Explain what and why, not how
-
Push to Your Fork
git push origin feature/amazing-feature
-
Open a Pull Request
- Go to the original repository
- Click "New Pull Request"
- Describe your changes clearly
- Reference any related issues
Before submitting, ensure:
- TypeScript: Strict mode passes without errors (
npx tsc --noEmit) - Linting: ESLint passes (
npm run lint) - Tests: All tests pass (
npm test) - Build: Production build succeeds (
npm run build) - Firestore Rules: Updated if new collections added
- Documentation: Updated if new features added
- Clean Code: No
console.log, commented-out code, or debug statements
Naming Conventions:
- Components: PascalCase (e.g.,
RecipeCard.tsx) - Functions: camelCase (e.g.,
getRecipes) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_FILE_SIZE) - Routes: kebab-case (e.g.,
/estrattore-ricette)
File Organization:
- Components in
src/components/ - Hooks in
src/lib/hooks/ - Utils in
src/lib/utils/ - Types in
src/types/
TypeScript:
- Prefer
interfacefor domain models - Prefer
typefor utility types - No
anytypes—useunknownand type guards - Export types for public APIs
Styling:
- Tailwind utilities only
- Use semantic color tokens (
bg-primary, notbg-blue-600) - Mobile-first responsive design
- Use
cn()utility for conditional classes
Firebase:
- Use
nullfor optional fields, neverundefined - Always filter queries by
userId - Use
serverTimestamp()for timestamps
Looking for where to start? Try these:
- Add Unit Tests: Cover utility functions in
src/lib/utils/ - Improve Error Messages: Make error toasts more user-friendly
- Add i18n Support: Implement English localization
- Mobile UI Enhancements: Improve animations and transitions
- Documentation: Fix typos, improve examples, add missing sections
- Accessibility: Add ARIA labels, improve keyboard navigation
- Performance: Optimize re-renders, add loading states
- Test Coverage: Currently limited—need more unit and integration tests
- E2E Tests: Implement Playwright or Cypress tests
- Accessibility: WCAG 2.1 Level AA compliance
- Performance: Bundle size optimization, code splitting
- Features: See project issues for feature requests
- Documentation: Check CLAUDE.md, AGENTS.md, COMMENTS.md
- Bug Reports: Open an issue on GitHub Issues
- Discussions: Use GitHub Discussions for questions
Common issues and solutions.
Symptom:
Error: Firebase Auth not initialized
Cause: Missing or incorrect Firebase environment variables
Solution:
- Check
.env.localfile exists in project root - Verify all 6
NEXT_PUBLIC_FIREBASE_*variables are set - Ensure no typos in variable names
- Restart development server:
npm run dev
Symptom:
FirebaseError: Missing or insufficient permissions
Cause: Security rules not deployed or queries don't filter by userId
Solution:
- Deploy security rules:
firebase deploy --only firestore:rules
- Verify rules in Firebase Console → Firestore → Rules tab
- Check that queries filter by user:
query(recipesRef, where('userId', '==', userId))
Symptom:
- "API key not configured"
- "File too large"
- Extraction times out
Solutions:
For API key errors:
- Verify
ANTHROPIC_API_KEYin.env.local - Ensure it does NOT have
NEXT_PUBLIC_prefix - Check API key is active in Anthropic Console
- Verify account has available credits
For file size errors:
- Check PDF size: must be < 4.4MB (Vercel limit)
- Compress PDF using:
- Try splitting multi-page PDFs
For timeouts:
- Try smaller PDF first (1-2 pages)
- Check Vercel function logs for errors
- Increase function timeout (Vercel Pro only)
Symptom:
POST /api/chat-recipeor another AI route returns401- Server logs mention missing Firebase Admin credentials or token verification failure
Cause:
- Protected AI routes verify Firebase ID tokens server-side
NEXT_PUBLIC_FIREBASE_*alone are not enough
Solution:
- Configure Firebase Admin credentials in your runtime environment
- Use either:
FIREBASE_ADMIN_CREDENTIALS_BASE64- or
FIREBASE_ADMIN_PROJECT_ID+FIREBASE_ADMIN_CLIENT_EMAIL+FIREBASE_ADMIN_PRIVATE_KEY
- Restart the development server or redeploy after changing environment variables
- On Vercel, prefer
FIREBASE_ADMIN_CREDENTIALS_BASE64
Symptom:
COPY --from=builder /app/public ./public
... "/app/public": not found
Cause: The production image expects a public/ directory in the final runtime copy, but some installations may not have any public assets yet.
Solutions:
- Pull the latest version of the repository, which includes the Docker fix for projects without a
public/folder - Always run Docker Compose with:
docker compose --env-file .env.local up --build
- If you changed any
NEXT_PUBLIC_*value, rebuild the image instead of only restarting the container - Check the container state with:
docker compose --env-file .env.local ps docker compose --env-file .env.local logs -f app
Symptom:
- Bottom nav shows on desktop
- Sidebar visible on mobile
- Navigation breaks on rotation
Solutions:
- Check Tailwind config has custom 1440px breakpoint:
lg: '1440px' - Use
max-lg:portrait:not justportrait::className="max-lg:portrait:block lg:hidden"
- Clear browser cache and hard reload
- Test in incognito mode
- Check browser console for CSS errors
Symptom:
Type error: Property 'X' does not exist on type 'Y'
Solutions:
- Run TypeScript compiler:
npx tsc --noEmit
- Check type definitions in
src/types/index.ts - Update
@typespackages:npm install --save-dev @types/node@latest @types/react@latest
- Restart VS Code/IDE for IntelliSense refresh
Symptom:
Module not found: Can't resolve '@/lib/...'
Solutions:
- Verify
tsconfig.jsonhas correct paths:{ "compilerOptions": { "paths": { "@/*": ["./src/*"] } } } - Restart development server
- Delete
.nextfolder and rebuild:rm -rf .next npm run dev
Enable Firebase Logging (development only):
import { setLogLevel } from 'firebase/firestore';
if (process.env.NODE_ENV === 'development') {
setLogLevel('debug');
}Next.js Verbose Logging:
DEBUG=* npm run devChrome DevTools:
- Network tab → Filter for
firebaseio.comoranthropic.com - React DevTools → Inspect component state
- Performance tab → Check for unnecessary re-renders
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
You Are Free To:
- ✅ Use the software for any purpose (personal, commercial, educational)
- ✅ Study and modify the source code
- ✅ Distribute copies of the software
- ✅ Distribute modified versions
Under These Conditions:
⚠️ Disclose Source: If you distribute the software, you must make the source code available⚠️ Same License: Modifications must be licensed under AGPL-3.0⚠️ State Changes: Document significant modifications⚠️ Network Use = Distribution: If you host this app publicly, you must provide source access to users
Important: The AGPL-3.0 includes a "network use" clause. If you modify this software and run it as a web service (SaaS), you must make your modified source code available to users of that service. This ensures that improvements to the software remain open and accessible to the community.
See LICENSE.md for the full license text.
We chose AGPL-3.0 to ensure that this project and any improvements made to it remain open source forever, even when deployed as a web service. This aligns with the values of sharing and community that are central to cooking and recipe exchange.
This project uses open-source libraries with the following licenses:
- Next.js, React: MIT License
- Firebase SDK: Apache License 2.0
- Anthropic SDK: MIT License
- Tailwind CSS: MIT License
- Radix UI: MIT License
- Lucide React: ISC License
See package.json for the complete list of dependencies and their licenses.
- README.md - This file (comprehensive project overview)
- SETUP.md - Production deployment guide
- CLAUDE.md - Developer reference for AI agents
- AGENTS.md - Common gotchas and patterns
- COMMENTS.md - Code commenting guidelines
Bug Reports:
Found a bug? Open an issue on GitHub Issues with:
- Clear description of the problem
- Steps to reproduce
- Expected behavior vs actual behavior
- Screenshots (if applicable)
- Environment details (browser, OS, app version)
Feature Requests:
Have an idea? Open a feature request on GitHub Issues with the "enhancement" label:
- Describe the use case
- Explain the problem it solves
- Propose a solution (optional)
- Indicate willingness to contribute
Discussions:
For questions, ideas, or general discussion, use GitHub Discussions.
Giuseppe Di Maio @GiuseppeDM98
- Firebase Team - For backend-as-a-service platform
- Next.js Team - For the incredible React framework
- Anthropic - For Claude AI and excellent developer experience
- Open Source Community - For countless libraries that make this possible
- You - For using, contributing to, and improving this project
Made with TypeScript and passion for good food.