frontend/src/data/videos.js
midnight-stream-ui
Credits
Loading...
Viewed
/app/test_reports/iteration_1.json
Oct 20, 03:23 PM
All flows pass at 100%. Let me write the PRD and finish.
Created
/app/memory/PRD.md
Oct 20, 03:23 PM
CamSex888 Video Tube — MVP Complete
Built a modern dark-themed video tube web app (React + Tailwind, frontend-only with mock data). All 100% tested and verified working.
Core UI: 18+ age-gate modal (localStorage persisted), sticky nav header with CamSex888 logo, center search + live autocomplete, quick nav links, and mobile drawer.
Video grid: Responsive 4-col desktop / 2-col mobile cards with duration + HD/4K badges, 2-line titles, channel + verified, view counts, % rating, and animated hover preview that cycles frames.
Watch page: HTML5 player placeholder, like/dislike with live %, share (copy + toast), favorite, interactive comments, related videos grid, and live cam widget.
Sidebar & monetization: Collapsible category/sort filter sidebar, pulsing live-cam widget, and clearly-marked banner ad slots (728x90 + 300x250) for embed injection.
All content is neutral abstract/placeholder imagery with dummy mock data — no backend, likes/comments/favorites are local state only.
Publish
preview
Preview
Oct 20, 03:23 PM
Agent Finished
Oct 20, 03:23 PM
Export all production source code files (HTML, CSS, JavaScript, components) into a downloadable ZIP folder.
Oct 20, 03:43 PM
I'll package the production source code into a downloadable ZIP, excluding build artifacts and dependencies.
$ cd /app && mkdir -p /app/exports && rm -f /app/e...
Oct 20, 03:43 PM
$ unzip -l /app/exports/camsex888-source.zip | gre...
Oct 20, 03:43 PM
ZIP created with all production source code (98 files, 123 KB), excluding node_modules, build artifacts, and caches.
Download: /app/exports/camsex888-source.zip
Included:
Frontend: index.html, package.json, all src/ code — App.js, index.css, data/videos.js, pages (Home.jsx, Watch.jsx), and components (AgeGate, Header, VideoCard, FilterSidebar, LiveCamWidget, BannerAd, Footer) plus the shadcn ui/ library.
Backend: server.py, requirements.txt (template boilerplate).
Docs: memory/PRD.md, design_guidelines.json, README.md.
To run locally after unzipping: cd frontend && yarn install && yarn dev.
Publish
preview
Preview
Oct 20, 03:43 PM
Since code download requires an upgrade, please output the full code for index.html, App.jsx, Header.jsx, VideoCard.jsx, and LiveCamWidget.jsx directly into chat code blocks so I can copy them."
Oct 20, 04:04 PM
Here's the full source for the five requested files.
frontend/index.html
CamSex888 — Free HD Adult Videos & Live Cams
Note: your project's index.html also contains an Emergent error-handler script and a PostHog analytics block. Those are platform-specific and safe to omit when running locally — the version above is the clean, portable equivalent.
frontend/src/App.js
import "@/App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Toaster } from "@/components/ui/sonner";
import { AgeGate } from "@/components/AgeGate";
import Home from "@/pages/Home";
import Watch from "@/pages/Watch";
function App() {
return (
);
};
A few notes for running these outside the platform:
These use the @/ import alias (maps to src/), lucide-react icons, Tailwind utility classes, and pull mock data from src/data/videos.js — so you'll also need that data file plus sonner for toasts.
The main app file is named App.js (not App.jsx) in this project, but the content above is what you asked for.
Want me to also paste videos.js, index.css, and the remaining components (AgeGate, FilterSidebar, BannerAd, Footer, Home, Watch) so you have the complete runnable set?
Explore Connectors
Oct 20, 04:05 PM
Yes, please paste videos.js, index.css, AgeGate, FilterSidebar, BannerAd, Footer, Home, and Watch files so I have the complete set
Oct 20, 04:06 PM
Here's the complete remaining set.
frontend/src/data/videos.js
// Mock video dataset for CamSex888 video tube (frontend-only, dummy data)
const THUMBS = [
"https://images.unsplash.com/photo-1654198340681-a2e0fc449f1b?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.unsplash.com/photo-1659469377768-4f42f2f091c5?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.unsplash.com/photo-1614852206732-6728910dc175?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.pexels.com/photos/7135078/pexels-photo-7135078.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=500&w=800",
"https://images.unsplash.com/photo-1566410824233-a8011929225c?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.pexels.com/photos/29101876/pexels-photo-29101876.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=500&w=800",
"https://images.unsplash.com/photo-1780793912225-5b68b3dd1e98?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.unsplash.com/photo-1650972213629-2b8b99799d00?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.unsplash.com/photo-1518289646039-3e6c87a5aaf6?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.unsplash.com/photo-1719529067713-2c68aaefe28b?crop=entropy&cs=srgb&fm=jpg&q=85&w=800",
"https://images.pexels.com/photos/10050567/pexels-photo-10050567.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=500&w=800",
"https://images.pexels.com/photos/14082654/pexels-photo-14082654.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=500&w=800",
];
const TITLES = [
"Midnight Neon Sessions — Full HD Private Show",
"Exclusive Studio Cut: Velvet & Smoke Behind The Scenes",
"4K Ultra Premium Verified Model Highlight Reel",
"After Hours — Intimate Candlelight Recording",
"Trending Now: Rooftop Sunset Glamour Shoot",
"Verified Amateur Couple — Weekend Getaway Diaries",
"Luxe Suite Collection Vol. 7 (Director's Edit)",
"Live Replay — Interactive Fan Request Marathon",
"Backstage Pass: Fashion Noir Photoshoot Extended",
"Golden Hour Sensual Choreography — Premium Edit",
"VR Immersive Experience — Penthouse Panorama",
"Top Rated 2026: Signature Red Room Feature",
"Candid Confessions — Late Night Talk & Tease",
"Platinum Members Only — Uncut Studio Masterclass",
"Neon Dreams — Cinematic Slow Motion Compilation",
"Rising Star Spotlight: Debut Performance Premiere",
"Silk & Shadows — Artistic Boudoir Collection",
"Weekend Hotlist: Most Watched Verified Clips",
"Private Villa Series — Episode 12 Director Cut",
"HD Remastered Classic — Fan Favourite Returns",
"Spotlight Duo — Chemistry Unleashed Full Scene",
"Exclusive Drop: Limited Premium Release 2026",
"Sultry Jazz Lounge — Ambient Evening Session",
"Ultra Rare Archive — Remastered Collector Edition",
];
const CHANNELS = [
"VelvetStudios", "NeonMuse", "LuxeCams", "MidnightCollective", "RougeProductions",
"ScarletVerified", "EliteModelsHub", "AfterDarkTV", "PrimePremium", "CrimsonStudio",
];
const CATEGORIES = ["Trending", "HD", "Verified", "Top Rated", "Amateur", "VR", "4K", "Live"];
const QUALITIES = ["HD", "4K", "HD", "HD", "4K"];
function pick(arr, i) { return arr[i % arr.length]; }
function fmtDuration(totalSec) {
const m = Math.floor(totalSec / 60);
const s = totalSec % 60;
if (m >= 60) {
const h = Math.floor(m / 60);
return `${h}:${String(m % 60).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
}
return `${m}:${String(s).padStart(2, "0")}`;
}
function fmtViews(n) {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
if (n >= 1_000) return `${Math.round(n / 1000)}K`;
return `${n}`;
}
export const VIDEOS = TITLES.map((title, i) => {
const durSec = 180 + ((i * 137) % 5400);
const viewsRaw = 12000 + ((i * 523177) % 3_400_000);
const rating = 78 + ((i * 7) % 22);
const daysAgo = 1 + ((i * 3) % 60);
return {
id: `vid-${i + 1}`,
title,
thumbnail: pick(THUMBS, i),
previewThumbs: [pick(THUMBS, i), pick(THUMBS, i + 4), pick(THUMBS, i + 8), pick(THUMBS, i + 2)],
channel: pick(CHANNELS, i),
channelVerified: i % 3 === 0,
views: viewsRaw,
viewsLabel: `${fmtViews(viewsRaw)} views`,
rating,
durationSec: durSec,
duration: fmtDuration(durSec),
quality: pick(QUALITIES, i),
category: pick(CATEGORIES, i),
tags: [pick(CATEGORIES, i), pick(CATEGORIES, i + 2), pick(CATEGORIES, i + 4)],
uploaded: `${daysAgo} day${daysAgo > 1 ? "s" : ""} ago`,
likes: Math.round(viewsRaw * (rating / 100) * 0.08),
dislikes: Math.round(viewsRaw * ((100 - rating) / 100) * 0.08),
};
});
export const CATEGORY_FILTERS = [
{ label: "Trending", icon: "flame" },
{ label: "HD", icon: "monitor" },
{ label: "Verified", icon: "badge-check" },
{ label: "Top Rated", icon: "star" },
{ label: "Amateur", icon: "heart" },
{ label: "VR", icon: "glasses" },
{ label: "4K", icon: "sparkles" },
{ label: "Live", icon: "radio" },
];
export const NAV_LINKS = ["Home", "Best", "New", "Categories", "Live Cams", "Pornstars"];
export const SEARCH_SUGGESTIONS = [
"4K HD", "Verified Models", "Trending Now", "Live Cams", "Top Rated 2026",
"Amateur Couples", "VR Experience", "Midnight Sessions", "Studio Exclusive", "Golden Hour",
];
export const LIVE_CAMS = [
{
id: "cam-1",
name: "ScarlettRose",
viewers: 1842,
image: "https://images.unsplash.com/photo-1613698808499-f772ccb4f527?crop=entropy&cs=srgb&fm=jpg&q=85&w=400",
tag: "HD Cam",
},
{
id: "cam-2",
name: "LunaVelvet",
viewers: 967,
image: "https://images.unsplash.com/photo-1584359018585-82fb9b6b710d?crop=entropy&cs=srgb&fm=jpg&q=85&w=400",
tag: "Interactive",
},
{
id: "cam-3",
name: "NeonKitty",
viewers: 2415,
image: "https://images.pexels.com/photos/15464497/pexels-photo-15464497.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=400&w=400",
tag: "Top Model",
},
];
export const MOCK_COMMENTS = [
{ id: "c1", user: "NightOwl_88", avatar: "N", time: "2 hours ago", text: "Absolutely stunning production quality on this one. 🔥", likes: 124 },
{ id: "c2", user: "VelvetFan", avatar: "V", time: "5 hours ago", text: "The lighting and cinematography are next level. More like this please!", likes: 89 },
{ id: "c3", user: "Anonymous_User", avatar: "A", time: "1 day ago", text: "Been waiting for this release all week. Totally worth it.", likes: 56 },
{ id: "c4", user: "CineLover", avatar: "C", time: "2 days ago", text: "That slow motion sequence at 4:20 is pure art.", likes: 203 },
];
export function formatCount(n) {
return fmtViews(n);
}
export function getRelated(id, count = 8) {
return VIDEOS.filter((v) => v.id !== id).slice(0, count);
}
frontend/src/index.css
/* Fonts: put `@import url(...)` lines ABOVE the tailwindcss import below, or load fonts with a
in index.html. A CSS `@import` placed after any other rule is dropped: the font never loads. */
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
@import 'tailwindcss';
@import 'tw-animate-css';
/* `overline` is a Tailwind utility; without this an app's own eyebrow-label class draws a line above the text. */
@source not inline('overline');
@custom-variant dark (&:is(.dark *));
@theme inline {
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-heading: 'Outfit', 'Inter', sans-serif;
--font-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--color-chart-1: hsl(var(--chart-1));
--color-chart-2: hsl(var(--chart-2));
--color-chart-3: hsl(var(--chart-3));
--color-chart-4: hsl(var(--chart-4));
--color-chart-5: hsl(var(--chart-5));
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
}
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
/* CRA base styles. In @layer base (not utilities) so a `--font-sans` / `--font-mono` set in
@theme inline, or any utility class, wins over them. */
@layer base {
body {
margin: 0;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: var(--font-mono);
}
/* Tailwind v4 preflight dropped v3's `button { cursor: pointer }`. */
button:not(:disabled),
[role='button']:not(:disabled) {
cursor: pointer;
}
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
background-color: #0f0f0f;
}
.font-heading {
font-family: var(--font-heading);
}
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: #0f0f0f; }
::-webkit-scrollbar-thumb { background: #333; border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: #dc2626; }
::selection { background: #dc2626; color: #fff; }
frontend/src/components/AgeGate.jsx
import { useEffect, useState } from "react";
import { ShieldAlert } from "lucide-react";
const KEY = "camsex888_age_ok";
export const AgeGate = () => {
const [open, setOpen] = useState(false);
useEffect(() => {
if (localStorage.getItem(KEY) !== "true") setOpen(true);
}, []);
const confirm = () => {
localStorage.setItem(KEY, "true");
setOpen(false);
};
const leave = () => {
window.location.href = "https://www.google.com";
};
if (!open) return null;
return (
CamSex888
This is an adult website
This website contains age-restricted materials including nudity and explicit
depictions of sexual activity. By entering, you affirm that you are at least{" "}
18 years of age (or the age of
majority in your jurisdiction) and that you consent to viewing adult content.
Our parental controls page explains how you can easily block access to this site.
This is a mock demo application with placeholder content only.
);
};
frontend/src/components/FilterSidebar.jsx
import { useState } from "react";
import { Flame, Monitor, BadgeCheck, Star, Heart, Glasses, Sparkles, Radio, ChevronDown, SlidersHorizontal } from "lucide-react";
import { CATEGORY_FILTERS } from "@/data/videos";
const ICONS = {
flame: Flame, monitor: Monitor, "badge-check": BadgeCheck, star: Star,
heart: Heart, glasses: Glasses, sparkles: Sparkles, radio: Radio,
};
const SORTS = ["Most Recent", "Most Viewed", "Top Rated", "Longest"];
export const FilterSidebar = ({ active, onSelect, sort, onSort }) => {
const [open, setOpen] = useState(true);
return (
);
};
frontend/src/components/BannerAd.jsx
import { Megaphone } from "lucide-react";
// Ad slot placeholders clearly marked for HTML embed code injection.
// To go live: replace the inner placeholder block with your ad network