{"id":50969701,"url":"https://github.com/mithun-ctrl/moctale-media","last_synced_at":"2026-06-19T00:34:13.510Z","repository":{"id":347756869,"uuid":"1195179844","full_name":"mithun-ctrl/moctale-media","owner":"mithun-ctrl","description":"Upload image media here","archived":false,"fork":false,"pushed_at":"2026-03-29T12:33:56.000Z","size":61,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T13:50:42.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://moctale-media.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mithun-ctrl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-29T10:38:32.000Z","updated_at":"2026-03-29T12:34:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mithun-ctrl/moctale-media","commit_stats":null,"previous_names":["mithun-ctrl/moctale-media"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mithun-ctrl/moctale-media","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mithun-ctrl%2Fmoctale-media","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mithun-ctrl%2Fmoctale-media/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mithun-ctrl%2Fmoctale-media/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mithun-ctrl%2Fmoctale-media/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mithun-ctrl","download_url":"https://codeload.github.com/mithun-ctrl/moctale-media/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mithun-ctrl%2Fmoctale-media/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34513020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-19T00:34:12.773Z","updated_at":"2026-06-19T00:34:13.483Z","avatar_url":"https://github.com/mithun-ctrl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Moctale Media\n\nA modern, frontend-only image hosting and sharing platform built with React + Vite + Vercel Blob.\n\nUpload images and get a single shareable link — no backend, no auth, no database.\n\n---\n\n## ✨ Features\n\n- **Drag \u0026 drop** or file picker upload\n- **Multi-image upload** with previews before upload\n- **Drag to reorder** images before upload\n- **5MB per file** limit with clear validation\n- **Shareable link** — one URL for all uploaded images\n- **Gallery view** at `/view?data=...` with masonry grid layout\n- **Per-image download** and link copy\n- **Dark / light mode toggle**\n- **Fully client-side** — no Express, no database, no auth\n- **Deployed-ready** for Vercel\n\n---\n\n## 🚀 Getting Started\n\n### 1. Clone \u0026 Install\n\n```bash\ngit clone https://github.com/yourusername/moctale-media.git\ncd moctale-media\nnpm install\n```\n\n### 2. Set up Vercel Blob\n\n1. Go to [vercel.com](https://vercel.com) and create a project\n2. Navigate to **Storage** → **Create Database** → **Blob**\n3. Copy the **Read/Write Token**\n4. Create a `.env.local` file:\n\n```env\nVITE_BLOB_READ_WRITE_TOKEN=vercel_blob_rw_xxxxxxxx\n```\n\n\u003e ⚠️ Never commit `.env.local` to git. It's already in `.gitignore`.\n\n### 3. Run locally\n\n```bash\nnpm run dev\n```\n\nVisit `http://localhost:5173`\n\n---\n\n## 📦 Build \u0026 Deploy\n\n### Deploy to Vercel (recommended)\n\n```bash\nnpm install -g vercel\nvercel\n```\n\nThen add your environment variable in the Vercel dashboard:\n- **Key:** `VITE_BLOB_READ_WRITE_TOKEN`\n- **Value:** your Vercel Blob token\n\n\u003e The `vercel.json` handles SPA rewrites so `/view?data=...` routes work correctly.\n\n### Build manually\n\n```bash\nnpm run build\nnpm run preview\n```\n\n---\n\n## 📁 Project Structure\n\n```\nsrc/\n├── components/\n│   ├── UploadBox.jsx      # Drag \u0026 drop zone\n│   ├── ImagePreview.jsx   # Pre-upload previews with drag reorder\n│   ├── Loader.jsx         # Spinner, progress bar, skeletons\n│   ├── Navbar.jsx         # Top navigation bar\n│   └── ShareCard.jsx      # Post-upload share link UI\n│\n├── pages/\n│   ├── Home.jsx           # Upload page\n│   └── View.jsx           # Gallery page (/view?data=...)\n│\n├── utils/\n│   ├── encodeData.js      # URL-safe Base64 encoder\n│   └── decodeData.js      # URL-safe Base64 decoder\n│\n├── App.jsx                # Router setup + dark mode state\n├── main.jsx               # React entry point\n└── index.css              # Tailwind + custom styles\n```\n\n---\n\n## 🔗 How Multi-Image Links Work\n\nSince there's no backend, multiple image URLs are encoded into the share link itself:\n\n1. After upload, all Vercel Blob URLs are collected into an array\n2. The array is JSON-serialized and URL-safe Base64-encoded\n3. This becomes the `data=` query param: `/view?data=\u003cencoded\u003e`\n4. On the `/view` page, the param is decoded back into URLs\n5. Images are displayed in a masonry grid — works reliably after refresh\n\n---\n\n## ⚙️ Environment Variables\n\n| Variable | Description |\n|---|---|\n| `VITE_BLOB_READ_WRITE_TOKEN` | Vercel Blob read/write token |\n\n---\n\n## 🛠 Tech Stack\n\n- **React 18** + **Vite**\n- **Tailwind CSS** v3\n- **@vercel/blob** client SDK\n- **react-router-dom** v6\n- **uuid** for unique filenames\n\n---\n\n## 📝 Limitations\n\n- Max **5MB per image** (Vercel Blob free tier limit)\n- Supported formats: **JPG, PNG, WEBP, GIF**\n- Very long image collections (100+) may produce a long URL — most browsers support URLs up to 64KB\n- The `VITE_` prefix exposes the token to the client bundle — use Vercel Blob's token-scoping features to restrict access\n\n---\n\n## 📄 License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmithun-ctrl%2Fmoctale-media","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmithun-ctrl%2Fmoctale-media","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmithun-ctrl%2Fmoctale-media/lists"}