diff --git a/Manga-Availability-Finder/README.md b/Manga-Availability-Finder/README.md new file mode 100644 index 0000000..867b349 --- /dev/null +++ b/Manga-Availability-Finder/README.md @@ -0,0 +1,145 @@ +# πŸ” Webtoon/Manga Availability Finder + +**Live Demo:** [webtoonhunter.lovable.app](https://webtoonhunter.lovable.app) + +--- + +## What is this? + +Webtoon/Manga Availability Finder is an AI-powered manga/webtoon availability checker that searches multiple reading platforms simultaneously. It uses the TinyFish Web Agent API for real-time browser automation, deploying parallel browser agents to search and verify availability across multiple platforms. + +--- + +## Demo + + + +https://github.com/user-attachments/assets/7b3ef9be-d4ba-43be-b3b5-ed9ea246c591 + +--- + +## Code Snippet + +```typescript +// Call TinyFish Web Agent API with SSE streaming for real-time browser automation + +const response = await fetch("https://mino.ai/v1/automation/run-sse", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-Key": process.env.MINO_API_KEY, + }, + body: JSON.stringify({ + url, // e.g., "https://mangadex.org/search?q=One+Piece" + goal: `You are searching for a manga/webtoon called "${mangaTitle}"... + STEP 1: Find and use the search bar to enter the title + STEP 2: Analyze the search results for matches + STEP 3: Return JSON with { found: boolean, match_confidence: string }`, + stream: true, + }), +}); + +// Stream SSE events back to client for live preview +const reader = response.body?.getReader(); +while (true) { + const { done, value } = await reader.read(); + if (done) break; + // Forward streamingUrl + completion events to frontend +} +``` + +--- + +## How to Run + +### Prerequisites +- Node.js 18+ +- Lovable Cloud account (or Supabase project) + +### Environment Variables + +| Variable | Description | Required | +|----------|-------------|----------| +| `MINO_API_KEY` | TinyFish Web Agent [API key](https://mino.ai) | βœ… | +| `GEMINI_API_KEY` | API key from [Google AI Studio](https://makersuite.google.com) | βœ… | + +### Setup + +```bash +# 1. Clone the repository +git clone +cd webtoon-hunter + +# 2. Install dependencies +npm install + +# 3. Add secrets to your Lovable Cloud / Supabase project +# Navigate to Settings β†’ Secrets and add: +# - TinyFish Web Agent AI KEY +# - GEMINI_API_KEY + +# 4. Start development server +npm run dev +``` + +--- + +## Architecture Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ User Interface β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ SearchHero │───▢│ useMangaSearch │───▢│ AgentCard (x6 parallel) β”‚ β”‚ +β”‚ β”‚ Component β”‚ β”‚ Hook β”‚ β”‚ with Live Stream Preview β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Edge Functions (Supabase) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ discover-manga-sites β”‚ β”‚ search-manga (x6) β”‚ β”‚ +β”‚ β”‚ (1x per search) β”‚ β”‚ (parallel browser agents) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Gemini β†’ Get site URLsβ”‚ β”‚ TinyFish API β†’ Browser Automation | | +β”‚ β”‚ (+ fallback sites) β”‚ β”‚ (SSE real-time streaming) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ External APIs β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Gemini API β”‚ β”‚ TinyFish Web Agent API β”‚ β”‚ +β”‚ β”‚ (Site Discovery) β”‚ β”‚ (Browser Automation + SSE) β”‚ β”‚ +β”‚ β”‚ Called: 1x β”‚ β”‚ Called: 5-6x parallel β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Flow Summary + +1. **User enters manga title** β†’ Client triggers search +2. **Gemini API** discovers 5-6 relevant manga site URLs (with fallback if rate-limited) +3. **TinyFish Web Agent API** deploys parallel browser agents to each site +4. **SSE Streaming** provides live browser preview URLs + real-time status updates +5. **Results** display which sites have the manga available + +--- + +## Tech Stack + +- **Frontend:** React, TypeScript, Tailwind CSS, shadcn/ui +- **Backend:** Supabase Edge Functions (Deno) +- **APIs:** TinyFish Web Agent (browser automation), Gemini (site discovery) +- **Streaming:** Server-Sent Events (SSE) + +--- + +## License + +MIT diff --git a/Manga-Availability-Finder/docs/MINO_API_INTEGRATION.md b/Manga-Availability-Finder/docs/MINO_API_INTEGRATION.md new file mode 100644 index 0000000..9ce2b3c --- /dev/null +++ b/Manga-Availability-Finder/docs/MINO_API_INTEGRATION.md @@ -0,0 +1,380 @@ +# Mino API Integration Documentation + +## Product Architecture Overview + +This application is a **Manga/Webtoon Finder** that uses AI-powered browser automation to search for manga availability across multiple websites simultaneously. + +### System Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Client (React) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ SearchHero │───▢│ useMangaSearch │───▢│ AgentCard (x6 parallel) β”‚ β”‚ +β”‚ β”‚ Component β”‚ β”‚ Hook β”‚ β”‚ with Live Stream Preview β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Edge Functions (Supabase) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ discover-manga-sites β”‚ β”‚ search-manga (x6) β”‚ β”‚ +β”‚ β”‚ (Called: 1x) β”‚ β”‚ (Called: 6x parallel) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Gemini API β†’ Get URLs β”‚ β”‚ Mino API β†’ Browser Automation β”‚ β”‚ +β”‚ β”‚ (with fallback sites) β”‚ β”‚ (SSE Streaming) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ External APIs β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Gemini API β”‚ β”‚ Mino API β”‚ β”‚ +β”‚ β”‚ (Site Discovery) β”‚ β”‚ (Browser Automation + SSE) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### API Call Summary + +| API | Purpose | Calls per Search | Response Type | +|-----|---------|------------------|---------------| +| Gemini API | Discover manga reading sites | 1x | JSON | +| Mino API | Automate browser search on each site | 5-6x (parallel) | SSE Stream | + +### Orchestration Flow + +1. **User enters manga title** β†’ Client triggers `useMangaSearch.search(title)` +2. **Site Discovery** β†’ `discover-manga-sites` edge function calls Gemini API (or uses fallback) +3. **Agent Initialization** β†’ Client creates 5-6 agent cards in "idle" state +4. **Parallel Browser Automation** β†’ `search-manga` edge function called for each site simultaneously +5. **Real-time Updates** β†’ Mino SSE stream provides live browser preview URL + final result +6. **Results Display** β†’ Each agent card updates independently as results arrive + +--- + +## Code Snippets + +### 1. Calling the Mino API (Edge Function) + +```typescript +// supabase/functions/search-manga/index.ts + +import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; + +const corsHeaders = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", +}; + +serve(async (req) => { + if (req.method === "OPTIONS") { + return new Response(null, { headers: corsHeaders }); + } + + const { url, mangaTitle } = await req.json(); + const MINO_API_KEY = Deno.env.get("MINO_API_KEY"); + + // Define the automation goal (see Goal section below) + const goal = `You are searching for a manga/webtoon called "${mangaTitle}"...`; + + // Call Mino API with SSE streaming + const response = await fetch("https://mino.ai/v1/automation/run-sse", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-Key": MINO_API_KEY, + }, + body: JSON.stringify({ + url, // Starting URL (e.g., mangadex.org/search?q=One+Piece) + goal, // Natural language instruction for the browser agent + timeout: 60000 // Maximum execution time in milliseconds + }), + }); + + // Stream SSE events back to client + // ... (see full implementation below) +}); +``` + +### 2. Client-Side SSE Consumption + +```typescript +// src/hooks/useMangaSearch.ts + +const searchSite = async (agent: SiteAgent, title: string) => { + const response = await fetch(`${supabaseUrl}/functions/v1/search-manga`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${supabaseKey}`, + "apikey": supabaseKey, + }, + body: JSON.stringify({ url: agent.siteUrl, mangaTitle: title }), + }); + + // Handle SSE stream + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value); + const lines = chunk.split("\n"); + + for (const line of lines) { + if (line.startsWith("data: ")) { + const data = JSON.parse(line.slice(6)); + + // Handle streaming URL for live preview + if (data.type === "stream" && data.streamingUrl) { + updateAgent(agent.id, { + streamingUrl: data.streamingUrl, + statusMessage: "Agent browsing..." + }); + } + + // Handle completion + if (data.type === "complete") { + updateAgent(agent.id, { + status: data.found ? "found" : "not_found", + statusMessage: data.found + ? "Manga found on this site!" + : "Not available on this site", + }); + } + } + } + } +}; +``` + +### 3. cURL Example + +```bash +curl -X POST "https://mino.ai/v1/automation/run-sse" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: YOUR_MINO_API_KEY" \ + -d '{ + "url": "https://mangadex.org/search?q=One%20Piece", + "goal": "You are searching for a manga/webtoon called \"One Piece\" on this website...", + "timeout": 60000 + }' +``` + +--- + +## Goal (Prompt) + +The following natural language prompt is sent to the Mino API to instruct the browser automation agent: + +``` +You are searching for a manga/webtoon called "${mangaTitle}" on this website. + +STEP 1 - NAVIGATION: +If there's a search bar or search input, enter "${mangaTitle}" and submit the search. +If there's no search bar visible, look for a search icon or link to a search page. + +STEP 2 - ANALYZE RESULTS: +Look at the search results or page content carefully. +Check if "${mangaTitle}" appears in the results (exact match or very close match). + +STEP 3 - RETURN RESULT: +Return a JSON object: +{ + "found": true or false, + "manga_title": "${mangaTitle}", + "site_url": "current page URL", + "match_confidence": "high" or "medium" or "low", + "notes": "brief explanation of what you found or didn't find" +} + +IMPORTANT: Only return "found": true if you see a clear match for "${mangaTitle}" in the results. +``` + +### Prompt Design Principles + +| Principle | Application | +|-----------|-------------| +| **Structured Steps** | Breaks down the task into clear navigation β†’ analysis β†’ output phases | +| **Fallback Handling** | Accounts for sites without visible search bars | +| **Strict Matching** | Prevents false positives by requiring exact/close matches | +| **JSON Output** | Ensures machine-parseable response for automation | + +--- + +## Sample Output + +### SSE Stream Events + +The Mino API returns Server-Sent Events (SSE) during execution. Here's the sequence: + +#### Event 1: Streaming URL (Immediate) + +```json +data: { + "type": "STREAM_URL", + "streamingUrl": "https://stream.mino.ai/session/abc123xyz" +} +``` + +This URL can be embedded in an iframe to show **live browser automation** in real-time. + +#### Event 2: Progress Updates (During Execution) + +```json +data: { + "type": "PROGRESS", + "step": "Entering search query...", + "screenshot": "base64_encoded_screenshot_data" +} +``` + +```json +data: { + "type": "PROGRESS", + "step": "Analyzing search results...", + "screenshot": "base64_encoded_screenshot_data" +} +``` + +#### Event 3: Completion (Final Result) + +```json +data: { + "type": "COMPLETE", + "resultJson": { + "found": true, + "manga_title": "One Piece", + "site_url": "https://mangadex.org/title/a1c7c817-4e59-43b7-9365-09675a149a6f/one-piece", + "match_confidence": "high", + "notes": "Found 'One Piece' by Eiichiro Oda with 1100+ chapters available" + }, + "duration": 12453 +} +``` + +### Processed Client Events + +The edge function transforms Mino events into simplified client events: + +```json +// Live preview available +data: {"type": "stream", "streamingUrl": "https://stream.mino.ai/session/abc123xyz"} + +// Search complete - manga found +data: {"type": "complete", "found": true} + +// Search complete - manga not found +data: {"type": "complete", "found": false} + +// Error occurred +data: {"type": "error", "error": "Search failed"} +``` + +--- + +## Error Handling + +### Rate Limiting (Gemini API) + +When Gemini API returns `429 Too Many Requests`, the system falls back to predefined sites: + +```typescript +const defaultSites = [ + { name: "MangaDex", url: `https://mangadex.org/search?q=${encodedTitle}` }, + { name: "MangaKakalot", url: `https://mangakakalot.com/search/story/${encodedTitle}` }, + { name: "MangaReader", url: `https://mangareader.to/search?keyword=${encodedTitle}` }, + { name: "Webtoon", url: `https://www.webtoons.com/en/search?keyword=${encodedTitle}` }, + { name: "Manganato", url: `https://manganato.com/search/story/${encodedTitle}` }, + { name: "Tapas", url: `https://tapas.io/search?q=${encodedTitle}` }, +]; +``` + +### Mino API Errors + +```typescript +if (data.type === "ERROR") { + const event = `data: ${JSON.stringify({ + type: "error", + error: data.message || "Search failed" + })}\n\n`; + controller.enqueue(encoder.encode(event)); +} +``` + +--- + +## Environment Variables + +| Variable | Purpose | Where Used | +|----------|---------|------------| +| `MINO_API_KEY` | Authenticate with Mino API | `search-manga` edge function | +| `GEMINI_API_KEY` | Authenticate with Gemini API | `discover-manga-sites` edge function | + +--- + +## Quick Start + +1. **Set up secrets** in your Supabase/Lovable Cloud project: + - `MINO_API_KEY` - Get from [mino.ai](https://mino.ai) + - `GEMINI_API_KEY` - Get from [Google AI Studio](https://makersuite.google.com) + +2. **Deploy edge functions** (automatic in Lovable) + +3. **Test the flow**: + ```typescript + import { useMangaSearch } from "@/hooks/useMangaSearch"; + + const { search, agents, isSearching } = useMangaSearch(); + + // Trigger search + search("One Piece"); + + // agents array updates in real-time with status and streamingUrl + ``` + +--- + +## Architecture Diagram (Mermaid) + +```mermaid +sequenceDiagram + participant User + participant Client as React Client + participant Discover as discover-manga-sites + participant Search as search-manga (x6) + participant Gemini as Gemini API + participant Mino as Mino API + + User->>Client: Enter "One Piece" + Client->>Discover: POST /discover-manga-sites + Discover->>Gemini: Generate site URLs + Gemini-->>Discover: [MangaDex, MangaKakalot, ...] + Discover-->>Client: { sites: [...] } + + par Parallel searches + Client->>Search: POST /search-manga (MangaDex) + Search->>Mino: run-sse (MangaDex URL) + Mino-->>Search: SSE: streamingUrl + Search-->>Client: SSE: {type: "stream", streamingUrl} + Mino-->>Search: SSE: COMPLETE + Search-->>Client: SSE: {type: "complete", found: true} + and + Client->>Search: POST /search-manga (MangaKakalot) + Search->>Mino: run-sse (MangaKakalot URL) + Mino-->>Search: SSE: streamingUrl + Search-->>Client: SSE: {type: "stream", streamingUrl} + Mino-->>Search: SSE: COMPLETE + Search-->>Client: SSE: {type: "complete", found: false} + end + + Client->>User: Display results with live previews +``` diff --git a/Manga-Availability-Finder/package.json b/Manga-Availability-Finder/package.json new file mode 100644 index 0000000..b062cac --- /dev/null +++ b/Manga-Availability-Finder/package.json @@ -0,0 +1,90 @@ +{ + "name": "vite_react_shadcn_ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "build:dev": "vite build --mode development", + "lint": "eslint .", + "preview": "vite preview", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-aspect-ratio": "^1.1.7", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-context-menu": "^2.2.15", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-hover-card": "^1.1.14", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-menubar": "^1.1.15", + "@radix-ui/react-navigation-menu": "^1.2.13", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.9", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-toast": "^1.2.14", + "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.7", + "@supabase/supabase-js": "^2.91.0", + "@tanstack/react-query": "^5.83.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^3.6.0", + "embla-carousel-react": "^8.6.0", + "input-otp": "^1.4.2", + "lucide-react": "^0.462.0", + "next-themes": "^0.3.0", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.61.1", + "react-resizable-panels": "^2.1.9", + "react-router-dom": "^6.30.1", + "recharts": "^2.15.4", + "sonner": "^1.7.4", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.9", + "zod": "^3.25.76" + }, + "devDependencies": { + "@eslint/js": "^9.32.0", + "@tailwindcss/typography": "^0.5.16", + "@testing-library/jest-dom": "^6.6.0", + "@testing-library/react": "^16.0.0", + "@types/node": "^22.16.5", + "@types/react": "^18.3.23", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react-swc": "^3.11.0", + "autoprefixer": "^10.4.21", + "eslint": "^9.32.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^15.15.0", + "jsdom": "^20.0.3", + "lovable-tagger": "^1.1.13", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vite": "^5.4.19", + "vitest": "^3.2.4" + } +} diff --git a/Manga-Availability-Finder/public/favicon.ico b/Manga-Availability-Finder/public/favicon.ico new file mode 100644 index 0000000..3c01d69 Binary files /dev/null and b/Manga-Availability-Finder/public/favicon.ico differ diff --git a/Manga-Availability-Finder/public/placeholder.svg b/Manga-Availability-Finder/public/placeholder.svg new file mode 100644 index 0000000..e763910 --- /dev/null +++ b/Manga-Availability-Finder/public/placeholder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Manga-Availability-Finder/public/robots.txt b/Manga-Availability-Finder/public/robots.txt new file mode 100644 index 0000000..6018e70 --- /dev/null +++ b/Manga-Availability-Finder/public/robots.txt @@ -0,0 +1,14 @@ +User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: / + +User-agent: Twitterbot +Allow: / + +User-agent: facebookexternalhit +Allow: / + +User-agent: * +Allow: / diff --git a/Manga-Availability-Finder/src/App.tsx b/Manga-Availability-Finder/src/App.tsx new file mode 100644 index 0000000..18daf2e --- /dev/null +++ b/Manga-Availability-Finder/src/App.tsx @@ -0,0 +1,27 @@ +import { Toaster } from "@/components/ui/toaster"; +import { Toaster as Sonner } from "@/components/ui/sonner"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import Index from "./pages/Index"; +import NotFound from "./pages/NotFound"; + +const queryClient = new QueryClient(); + +const App = () => ( + + + + + + + } /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + + + + +); + +export default App; diff --git a/Manga-Availability-Finder/src/components/AgentCard.tsx b/Manga-Availability-Finder/src/components/AgentCard.tsx new file mode 100644 index 0000000..995611e --- /dev/null +++ b/Manga-Availability-Finder/src/components/AgentCard.tsx @@ -0,0 +1,160 @@ +import { ExternalLink, CheckCircle2, XCircle, Loader2, Globe, Eye } from "lucide-react"; +import { cn } from "@/lib/utils"; + +export type AgentStatus = "idle" | "searching" | "found" | "not_found" | "error"; + +interface AgentCardProps { + siteName: string; + siteUrl: string; + status: AgentStatus; + statusMessage?: string; + streamingUrl?: string; + mangaTitle: string; +} + +const statusConfig: Record = { + idle: { + icon: , + label: "Ready", + className: "text-muted-foreground bg-muted/50", + }, + searching: { + icon: , + label: "Searching...", + className: "status-searching", + }, + found: { + icon: , + label: "Found", + className: "status-found", + }, + not_found: { + icon: , + label: "Not Found", + className: "status-not-found", + }, + error: { + icon: , + label: "Error", + className: "status-not-found", + }, +}; + +export function AgentCard({ + siteName, + siteUrl, + status, + statusMessage, + streamingUrl, + mangaTitle, +}: AgentCardProps) { + const config = statusConfig[status]; + + return ( +
+ {/* Scanning effect for searching state */} + {status === "searching" && ( +
+
+
+ )} + + {/* Header */} +
+
+
+
+ +
+
+

+ {siteName} +

+

{new URL(siteUrl).hostname}

+
+
+ +
+ {config.icon} + {config.label} +
+
+
+ + {/* Browser preview area */} +
+ {streamingUrl && status === "searching" ? ( +