├── .eslintrc.cjs ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── WYR-Demo.mp4 └── WYR-Logo.svg ├── src ├── App.tsx ├── index.css ├── main.tsx ├── useForm.tsx └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ['main'] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: 'pages' 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | - name: Set up Node 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 18 37 | cache: 'npm' 38 | - name: Install dependencies 39 | run: npm install 40 | - name: Build 41 | run: npm run build 42 | env: 43 | VITE_SERVICE_ID: ${{ secrets.VITE_SERVICE_ID }} 44 | VITE_TEMPLATE_ID: ${{ secrets.VITE_TEMPLATE_ID }} 45 | VITE_PUBLIC_KEY: ${{ secrets.VITE_PUBLIC_KEY }} 46 | VITE_RECAPTCHA_SITE_KEY: ${{ secrets.VITE_RECAPTCHA_SITE_KEY }} 47 | - name: Setup Pages 48 | uses: actions/configure-pages@v3 49 | - name: Upload artifact 50 | uses: actions/upload-pages-artifact@v2 51 | with: 52 | # Upload dist repository 53 | path: './dist' 54 | - name: Deploy to GitHub Pages 55 | id: deployment 56 | uses: actions/deploy-pages@v2 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .env 12 | dist 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Would You Rather Shorts Generator 2 | The "Would You Rather" Short Videos Generator is a powerful tool that automatically generates engaging 60-second videos suitable for platforms like YouTube Shorts, TikTok, Facebook and Instagram Reels. Built with Node.js, EJS, canvas. this dynamic generator. Explore the live demo here. 3 | 4 |
5 | 6 | # Demo 7 | https://nachat-ayoub.github.io/wyr-shorts-generator 8 | 9 |
10 | 11 | # [Contact me](mailto:ayoub.nachat.27@gmail.com) 12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Would You Rather? Shorts Generator 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wyr-shorts-gen-site", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emailjs/browser": "^4.1.0", 14 | "axios": "^1.6.7", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-google-recaptcha": "^3.1.0" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.55", 21 | "@types/react-dom": "^18.2.19", 22 | "@types/react-google-recaptcha": "^2.1.9", 23 | "@typescript-eslint/eslint-plugin": "^6.21.0", 24 | "@typescript-eslint/parser": "^6.21.0", 25 | "@vitejs/plugin-react-swc": "^3.5.0", 26 | "autoprefixer": "^10.4.17", 27 | "daisyui": "^4.7.2", 28 | "eslint": "^8.56.0", 29 | "eslint-plugin-react-hooks": "^4.6.0", 30 | "eslint-plugin-react-refresh": "^0.4.5", 31 | "postcss": "^8.4.35", 32 | "tailwindcss": "^3.4.1", 33 | "typescript": "^5.2.2", 34 | "vite": "^5.1.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/WYR-Demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachat-ayoub/wyr-shorts-generator/5f364a3956461e30ac83a2d285b9b26ccd54a0a4/public/WYR-Demo.mp4 -------------------------------------------------------------------------------- /public/WYR-Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import ReCAPTCHA from 'react-google-recaptcha'; 2 | import { useState } from 'react'; 3 | import useForm from './useForm'; 4 | 5 | type TStatus = 'success' | 'error' | 'loading' | null; 6 | 7 | export default function App() { 8 | const [error, setError] = useState(null); 9 | const [sendStatus, setSendStatus] = useState(null); 10 | const [message, setMessage] = useState(''); 11 | const [email, setEmail] = useState(''); 12 | const { sendEmail } = useForm(); 13 | 14 | async function handleSubmit(e: React.FormEvent) { 15 | e.preventDefault(); 16 | 17 | setError(null); 18 | setEmail(email.trim()); 19 | if (email === '') { 20 | setError('Please enter your email address.'); 21 | return; 22 | // check if email is valid 23 | } else if (!email.match(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/)) { 24 | setError('Please enter a valid email address.'); 25 | return; 26 | } 27 | 28 | setSendStatus('loading'); 29 | 30 | sendEmail(e, (error) => { 31 | setSendStatus(error ? 'error' : 'success'); 32 | }); 33 | } 34 | 35 | return ( 36 |
37 |
202 | ); 203 | } 204 | 205 | function Nav() { 206 | const repoLink = 'https://github.com/nachat-ayoub' + window.location.pathname; 207 | 208 | return ( 209 | 273 | ); 274 | } 275 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /src/useForm.tsx: -------------------------------------------------------------------------------- 1 | import emailjs from '@emailjs/browser'; 2 | 3 | function useForm() { 4 | const SERVICE_ID = import.meta.env.VITE_SERVICE_ID; 5 | const TEMPLATE_ID = import.meta.env.VITE_TEMPLATE_ID; 6 | const PUBLIC_KEY = import.meta.env.VITE_PUBLIC_KEY; 7 | 8 | function sendEmail(e: any, cb: (error: string | null) => void) { 9 | try { 10 | emailjs.sendForm(SERVICE_ID, TEMPLATE_ID, e.target, PUBLIC_KEY).then( 11 | function () { 12 | // console.log('SUCCESS!', response.status, response.text); 13 | cb(null); 14 | }, 15 | function () { 16 | // console.log('FAILED...', err); 17 | cb('FAILED...'); 18 | } 19 | ); 20 | } catch (error) { 21 | console.log(error); 22 | cb('Failed because of ' + error); 23 | } 24 | } 25 | 26 | return { sendEmail }; 27 | } 28 | 29 | export default useForm; 30 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | daisyui: { 8 | themes: ['light', 'dark'], 9 | }, 10 | plugins: [require('daisyui')], 11 | }; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react-swc'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | base: '/wyr-shorts-generator', 8 | }); 9 | --------------------------------------------------------------------------------