Nano Banana Pro
Agent skill for nano-banana-pro
This is **WebTools Pro**, a comprehensive file processing platform built as a Next.js monolithic application. The platform provides 51+ professional tools for PDF, image, video, and AI-powered file processing, all accessible through a modern web interface.
Sign in to like and favorite skills
This is WebTools Pro, a comprehensive file processing platform built as a Next.js monolithic application. The platform provides 51+ professional tools for PDF, image, video, and AI-powered file processing, all accessible through a modern web interface.
web-tools/ ├── src/ │ ├── app/ # Next.js App Router │ │ ├── api/ # API routes │ │ │ ├── admin/ # Admin endpoints │ │ │ ├── files/ # File operations │ │ │ ├── tools/ # Processing endpoints │ │ │ │ ├── pdf/ # PDF processing │ │ │ │ ├── image/ # Image processing │ │ │ │ ├── video/ # Video processing │ │ │ │ └── ai/ # AI processing │ │ │ └── health/ # Health checks │ │ ├── tools/ # Tool UI pages │ │ ├── layout.tsx # Root layout │ │ ├── page.tsx # Home page │ │ └── globals.css # Global styles │ ├── components/ # React components │ │ ├── ui/ # shadcn/ui components │ │ ├── tools/ # Tool-specific components │ │ └── layout/ # Layout components │ ├── lib/ # Utilities │ │ ├── cache*.ts # Caching system │ │ ├── ai/ # AI utilities │ │ └── utils.ts # General utilities │ └── hooks/ # Custom React hooks ├── uploads/ # User uploads ├── outputs/ # Processed files ├── docs/ # Documentation └── bmad-agent/ # BMad agent system
// ✅ Good: Use explicit types interface FileMetadata { fileId: string; originalName: string; mimeType: string; size: number; uploadedAt: string; } // ❌ Avoid: any type const processFile = (file: any) => { ... } // ✅ Good: Type-safe with Zod import { z } from 'zod'; const uploadSchema = z.object({ file: z.instanceof(File), operation: z.enum(['compress', 'convert', 'extract']), options: z.object({ quality: z.number().min(1).max(100).optional(), }), });
// Use Server Components by default // src/app/tools/pdf/page.tsx export default function PDFToolsPage() { return ( <div className="container mx-auto py-8"> <h1 className="text-4xl font-bold mb-8">PDF Tools</h1> {/* Tool grid */} </div> ); } // Client Components only when needed // src/components/tools/FileUpload.tsx 'use client'; import { useState } from 'react'; import { useDropzone } from 'react-dropzone'; export function FileUpload({ onUpload }: { onUpload: (file: File) => void }) { // Interactive component logic }
// src/app/api/tools/pdf/compress/route.ts import { NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; const requestSchema = z.object({ fileId: z.string().uuid(), quality: z.number().min(1).max(100).default(85), }); export async function POST(request: NextRequest) { try { // 1. Validate input const body = await request.json(); const { fileId, quality } = requestSchema.parse(body); // 2. Check cache const cached = await checkCache(`compress:${fileId}:${quality}`); if (cached) return NextResponse.json(cached); // 3. Process file const result = await compressPDF(fileId, quality); // 4. Cache result await cacheResult(`compress:${fileId}:${quality}`, result); // 5. Return response return NextResponse.json({ success: true, ...result, }); } catch (error) { return NextResponse.json( { success: false, error: error.message }, { status: 400 } ); } }
// Always use shadcn/ui components when available import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Progress } from '@/components/ui/progress'; // Component file naming // - PascalCase.tsx for components // - kebab-case.ts for utilities // - route.ts for API routes // Props interface naming interface FileUploaderProps { accept?: string[]; maxSize?: number; onUpload: (file: File) => Promise<void>; }
Create API Route:
# Create new tool API src/app/api/tools/pdf/new-tool/route.ts
Create UI Page:
# Create tool page src/app/tools/pdf/new-tool/page.tsx
Add to Navigation:
Implement Processing:
// Use appropriate engine import { processPDF } from '@/lib/pdf-engine';
// 1. Upload file const formData = new FormData(); formData.append('file', file); const uploadRes = await fetch('/api/files/upload', { method: 'POST', body: formData, }); const { fileId } = await uploadRes.json(); // 2. Process file const processRes = await fetch('/api/tools/pdf/compress', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileId, quality: 85 }), }); const { outputFileId } = await processRes.json(); // 3. Download result window.location.href = `/api/files/download?fileId=${outputFileId}`;
# Development npm run dev # Start dev server on port 8000 # Linting npm run lint # Run ESLint # Building npm run build # Build for production npm run start # Start production server # Type checking npx tsc --noEmit # Check TypeScript types
// Centralized error handler export class AppError extends Error { constructor( message: string, public statusCode: number = 500, public code?: string ) { super(message); } } // In API routes try { // ... processing } catch (error) { if (error instanceof AppError) { return NextResponse.json( { error: error.message, code: error.code }, { status: error.statusCode } ); } // Log unexpected errors console.error('Unexpected error:', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); }
// Multi-tier caching import { cacheManager } from '@/lib/cache'; // Check cache layers const result = await cacheManager.get(key, { memory: true, // Check memory first redis: true, // Then Redis ttl: 3600, // 1 hour TTL }); // Cache with tags for invalidation await cacheManager.set(key, data, { tags: ['pdf', `user:${userId}`], ttl: 3600, });
// Always validate file types const ALLOWED_TYPES = { pdf: ['application/pdf'], image: ['image/jpeg', 'image/png', 'image/webp'], video: ['video/mp4', 'video/webm'], }; // Sanitize filenames const sanitizeFilename = (filename: string): string => { return filename .replace(/[^a-zA-Z0-9.-]/g, '_') .replace(/\.{2,}/g, '.') .toLowerCase(); }; // Size limits const MAX_FILE_SIZE = { pdf: 50 * 1024 * 1024, // 50MB image: 25 * 1024 * 1024, // 25MB video: 500 * 1024 * 1024, // 500MB };
// Always use Next.js Image component import Image from 'next/image'; <Image src="/tool-icon.png" alt="Tool icon" width={48} height={48} loading="lazy" />
// Dynamic imports for heavy components const PDFViewer = dynamic(() => import('@/components/PDFViewer'), { loading: () => <Skeleton className="w-full h-96" />, ssr: false, });
// Use proper cache headers export async function GET() { const data = await fetchData(); return NextResponse.json(data, { headers: { 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate', }, }); }
The project includes a sophisticated agent-based development system at
/bmad-agent/. When working on architectural changes or major features:
Consult Agent Personas:
Use Agent Tasks:
/bmad-agent/tasks/Reference Templates:
/bmad-agent/templates/npm audit)npm install npm run dev # Open http://localhost:8000
# Build Docker image docker build -t web-tools . # Deploy to Google Cloud Run ./deploy-gcp.sh your-project-id us-central1
/docs/architecture.md/docs/testing-strategy-implementation.md/docs/DEPLOYMENT_GUIDE.md/bmad-agent/ide-bmad-orchestrator.cfg.mdany type - Always define proper TypeScript typesThis is a production-ready file processing platform with a focus on performance, security, and user experience. Follow the established patterns, use the provided components, and leverage the caching system for optimal results. When in doubt, refer to existing implementations in the codebase or consult the comprehensive documentation in the
/docs directory.