# FILE: package.json { "name": "wallpaper-generator", "version": "1.0.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.462.0", "next": "14.2.5", "postcss": "^8.4.47", "react": "18.3.1", "react-dom": "18.3.1", "tailwind-merge": "^2.5.2", "tailwindcss": "^3.4.10", "typescript": "^5.5.4" } } # FILE: next.config.mjs /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, experimental: { appDir: true } }; export default nextConfig; # FILE: tsconfig.json { "compilerOptions": { "target": "ES2022", "lib": ["dom", "dom.iterable", "es2021"], "allowJs": false, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "baseUrl": ".", "paths": { "@/components/*": ["components/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"] } # FILE: postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }; # FILE: tailwind.config.ts import type { Config } from "tailwindcss"; export default { darkMode: ["class"], content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", ], theme: { extend: { colors: { background: "hsl(0 0% 100%)", foreground: "hsl(240 10% 3.9%)", muted: "hsl(240 4.8% 95.9%)", }, borderRadius: { xl: "1rem", "2xl": "1.25rem", }, }, }, plugins: [], } satisfies Config; # FILE: app/globals.css @tailwind base; @tailwind components; @tailwind utilities; :root { color-scheme: light; } html, body, #__next { height: 100%; } body { @apply bg-white text-neutral-900; } # FILE: app/layout.tsx export const metadata = { title: "Wallpaper Generator", description: "Create custom wallpapers with affirmations and effects.", }; export default function RootLayout({ children }: { children: React.ReactNode }) { return (
{children}
); } # FILE: app/page.tsx "use client"; import React, { useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import BeforeAfterPreview from "@/components/BeforeAfterPreview"; export default function Page() { const [orig, setOrig] = useState(null); const [edit, setEdit] = useState(null); const [affirm, setAffirm] = useState("I am grounded and capable."); return (

Wallpaper Generator

Live Demo
Upload
setOrig(e.target.files?.[0] || null)} />
setEdit(e.target.files?.[0] || null)} />
setAffirm(e.target.value)} />
© 2025 Alex Campbell — Companionish AI Solutions
); } # FILE: components/BeforeAfterPreview.tsx "use client"; /** * Wallpaper Generator Before/After Preview * Created by Alex Campbell, 2025 * Companionish AI Solutions */ import React, { useEffect, useMemo, useRef, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import { Input } from "@/components/ui/input"; import { RefreshCcw, Columns2, Rows } from "lucide-react"; function srcFrom(input?: string | File | Blob | null) { if (!input) return ""; if (typeof input === "string") return input; return URL.createObjectURL(input); } export default function BeforeAfterPreview({ original, edited, height = 520, checker = true, defaultLayout = "stack", enableCompareSlider = true, }: { original?: string | File | Blob | null, edited?: string | File | Blob | null, height?: number, checker?: boolean, defaultLayout?: "stack" | "side"; enableCompareSlider?: boolean; }) { const [layout, setLayout] = useState<"stack" | "side">(defaultLayout); const [fitMode, setFitMode] = useState<"contain" | "cover">("contain"); const [zoom, setZoom] = useState(1); const [compare, setCompare] = useState(50); const [showCompare, setShowCompare] = useState(enableCompareSlider); const [spacePanning, setSpacePanning] = useState(false); const [pan, setPan] = useState({ x: 0, y: 0 }); const wrapRef = useRef(null); const isStack = layout === "stack"; const originalSrc = useMemo(() => srcFrom(original), [original]); const editedSrc = useMemo(() => srcFrom(edited), [edited]); useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (e.code === "Space") { e.preventDefault(); setSpacePanning(true); } if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "0") { setZoom(1); setPan({ x: 0, y: 0 }); } if ((e.ctrlKey || e.metaKey) && e.key === "=") { setZoom((z) => Math.min(5, +(z + 0.1).toFixed(2))); } if ((e.ctrlKey || e.metaKey) && e.key === "-") { setZoom((z) => Math.max(0.2, +(z - 0.1).toFixed(2))); } if (e.key.toLowerCase() === "s") setShowCompare((v) => !v); if (e.key.toLowerCase() === "l") setLayout((p) => (p === "stack" ? "side" : "stack")); }; const onKeyUp = (e: KeyboardEvent) => { if (e.code === "Space") setSpacePanning(false); }; window.addEventListener("keydown", onKeyDown); window.addEventListener("keyup", onKeyUp); return () => { window.removeEventListener("keydown", onKeyDown); window.removeEventListener("keyup", onKeyUp); }; }, []); useEffect(() => { const el = wrapRef.current; if (!el) return; const onWheel = (e: WheelEvent) => { if (e.ctrlKey) { e.preventDefault(); const rect = el.getBoundingClientRect(); const delta = -e.deltaY * 0.0015; setZoom((z) => { const nz = Math.min(5, Math.max(0.2, +(z + delta).toFixed(3))); const cx = e.clientX - rect.left - rect.width / 2; const cy = e.clientY - rect.top - rect.height / 2; setPan((p) => ({ x: p.x - cx * (nz - z) * 0.08, y: p.y - cy * (nz - z) * 0.08 })); return nz; }); } }; el.addEventListener("wheel", onWheel, { passive: false }); return () => el.removeEventListener("wheel", onWheel as any); }, []); const onPointerDown: React.PointerEventHandler = (e) => { if (!spacePanning) return; const start = { x: e.clientX, y: e.clientY }; const startPan = { ...pan }; const onMove = (ev: PointerEvent) => setPan({ x: startPan.x + (ev.clientX - start.x), y: startPan.y + (ev.clientY - start.y) }); const onUp = () => { window.removeEventListener("pointermove", onMove); window.removeEventListener("pointerup", onUp); }; window.addEventListener("pointermove", onMove); window.addEventListener("pointerup", onUp, { once: true }); }; const frameBase = "relative overflow-hidden rounded-2xl shadow-sm border select-none"; const checkerBg = checker ? "bg-[linear-gradient(45deg,_#f0f0f0_25%,_transparent_25%),linear-gradient(-45deg,_#f0f0f0_25%,_transparent_25%),linear-gradient(45deg,_transparent_75%,_#f0f0f0_75%),linear-gradient(-45deg,_transparent_75%,_#f0f0f0_75%)] bg-[length:20px_20px] bg-[position:0_0,0_10px,10px_-10px,-10px_0px]" : "bg-muted"; const Img = ({ src, alt }: { src?: string; alt: string }) => ( {alt} ); const Frame = ({ label, src }: { label: string; src?: string }) => ( {label}
{src ?
{label}
:
No image
}
); return (
{/* Hidden proof of authorship */}
{/* Controls */}
setZoom(v[0])} />
setCompare(Math.max(0, Math.min(100, Number(e.target.value) || 0)))} /> {compare}%
Shortcuts: Ctrl/Cmd + Scroll to zoom • Space to pan • S toggle compare • L toggle layout • Ctrl/Cmd+0 reset.
{isStack ? (
{showCompare && ( )}
) : (
{showCompare && ( )}
)}
); } function CompareOverlay({ compare, height, original, edited, fitMode, pan, zoom, checkerBg, sideBySide = false, }: { compare: number; height: number; original?: string; edited?: string; fitMode: "contain" | "cover"; pan: { x: number; y: number }; zoom: number; checkerBg: string; sideBySide?: boolean; }) { const [pct, setPct] = useState(compare); useEffect(() => setPct(compare), [compare]); const overlayRef = useRef(null); const onPointerDown: React.PointerEventHandler = (e) => { const el = overlayRef.current; if (!el) return; const rect = el.getBoundingClientRect(); const start = { x: e.clientX, y: e.clientY }; const getPct = (ev: PointerEvent) => { const rel = sideBySide ? (ev.clientX - rect.left) / rect.width : (ev.clientY - rect.top) / rect.height; return Math.min(Math.max(rel, 0), 1) * 100; }; const onMove = (ev: PointerEvent) => setPct(getPct(ev)); const onUp = () => { window.removeEventListener("pointermove", onMove); window.removeEventListener("pointerup", onUp); }; window.addEventListener("pointermove", onMove); window.addEventListener("pointerup", onUp, { once: true }); }; const transform = `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`; return (
{edited && (Edited)} {original && (
Original
)}
{Math.round(pct)}%
); } // Fancy console banner + Easter egg if (typeof window !== "undefined") { const banner = `\n%c██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███████╗\n%c██╔══██╗██╔══██╗██╔═══██╗██╔═══██╗██║ ██║██╔════╝\n%c██████╔╝██████╔╝██║ ██║██║ ██║██║ ██║█████╗ \n%c██╔═══╝ ██╔══██╗██║ ██║██║ ██║╚██╗ ██╔╝██╔══╝ \n%c██║ ██║ ██║╚██████╔╝╚██████╔╝ ╚████╔╝ ███████╗\n%c╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═══╝ ╚══════╝\n`; console.log(banner, "color:#4ade80;font-weight:bold;", "color:#22d3ee;font-weight:bold;", "color:#a855f7;font-weight:bold;", "color:#facc15;font-weight:bold;", "color:#f87171;font-weight:bold;", "color:#94a3b8;font-weight:bold;", ); console.log("%cCreated by Alex Campbell — Companionish AI Solutions (2025)", "color:#10b981; font-size:14px; font-weight:bold; background:#f0fdf4; padding:4px 8px; border-radius:4px;"); ;(window as any).wallpaper = { info: () => { console.log("%c🎨 Wallpaper Generator Before/After Preview", "color:#3b82f6; font-weight:bold; font-size:13px;"); console.log("Created by Alex Campbell — Companionish AI Solutions (2025)"); console.log("Version: 1.0.0"); console.log("Tip: Use Ctrl+Scroll to zoom, Space to pan, S to toggle compare, L to toggle layout."); console.log("💡 Have ideas or suggestions? %ccompanionishaisolutions@gmail.com", "color:#2563eb; text-decoration:underline; cursor:pointer;"); console.log("🌐 Live Demo: %chttps://wallpaper-generator-drab.vercel.app/", "color:#16a34a; text-decoration:underline; cursor:pointer;"); console.log("✨ Made with ❤️ by Companionish AI Solutions"); return "✔️ Info logged above."; } }; } # FILE: components/ui/button.tsx import * as React from "react"; import { cn } from "../utils"; type Props = React.ButtonHTMLAttributes & { variant?: "default" | "outline"; size?: "sm" | "md" }; export function Button({ className, variant = "default", size = "md", ...props }: Props) { const base = "inline-flex items-center justify-center rounded-2xl border text-sm font-medium transition-all disabled:opacity-50 disabled:pointer-events-none gap-1"; const styles = variant === "outline" ? "border-gray-300 bg-white hover:bg-gray-50" : "border-transparent bg-black text-white hover:bg-gray-800"; const sizes = size === "sm" ? "h-8 px-3" : "h-10 px-4"; return