├── .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 |
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 |
38 |
39 |
40 | Would You Rather Shorts Generator
41 |
42 |
43 |
44 |
54 |
55 |
56 |
57 | Support for both{' '}
58 | RTL and{' '}
59 | LTR languages:{' '}
60 |
61 |
62 |

67 |

72 |
73 |
74 |
75 |
76 |
77 | Unlimited Access:{' '}
78 | Only $30
79 |
80 |
81 | Gain instant access to the source code repository and receive
82 | automatic updates.
83 |
84 |
85 |
187 |
188 |
189 |
190 |
191 |
192 |
201 |
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 |
--------------------------------------------------------------------------------