Initial commit: Text Corruptor PWA
Features: - Zalgo text generator with adjustable intensity (1-10) - Real-time text corruption as you type - Click-to-copy functionality with visual feedback - Progressive Web App with offline support - Responsive design for mobile and desktop - Dark theme with glitch-inspired aesthetics Technical implementation: - Pure JavaScript implementation (no frameworks) - Service Worker for offline functionality - PWA manifest for installability - Python development server - Comprehensive linting setup (ESLint, Prettier, Black, Pylint) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
20
.eslintrc.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"serviceworker": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"indent": ["error", 4],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"no-unused-vars": ["warn"],
|
||||
"no-console": ["warn", { "allow": ["warn", "error", "log"] }]
|
||||
}
|
||||
}
|
||||
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
node_modules/
|
||||
*.pyc
|
||||
__pycache__/
|
||||
.DS_Store
|
||||
*.log
|
||||
.env
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
dist/
|
||||
build/
|
||||
10
.prettierrc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"printWidth": 100,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
114
CLAUDE.md
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Text Corruptor is a Progressive Web App (PWA) that generates zalgo text (corrupted/glitched text using Unicode combining characters). The app features real-time text corruption with adjustable intensity and click-to-copy functionality.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
zalgo/
|
||||
├── app/ # Main application directory (served as document root)
|
||||
│ ├── index.html # Main HTML page
|
||||
│ ├── zalgo.js # Zalgo text generation logic
|
||||
│ ├── app.js # Main application logic and PWA registration
|
||||
│ ├── styles.css # Application styles
|
||||
│ ├── sw.js # Service worker for offline functionality
|
||||
│ ├── manifest.json # PWA manifest
|
||||
│ ├── icons/ # PWA icons (SVG placeholders)
|
||||
│ ├── create-icons.js # Node.js script to generate PNG icons
|
||||
│ └── generate-icons.html # Browser-based icon generator
|
||||
├── server.py # Development server (serves app/ as root)
|
||||
├── package.json # Node dependencies and scripts
|
||||
├── bun.lockb # Bun lockfile
|
||||
├── .eslintrc.json # ESLint configuration
|
||||
└── .prettierrc # Prettier configuration
|
||||
```
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
# Start development server (serves on http://localhost:8000/)
|
||||
python3 server.py
|
||||
|
||||
# Or using the npm script
|
||||
bun run serve
|
||||
```
|
||||
|
||||
### Code Quality and Linting
|
||||
```bash
|
||||
# Lint JavaScript files
|
||||
bun run lint:js
|
||||
|
||||
# Lint and auto-fix JavaScript
|
||||
bun run lint:js:fix
|
||||
|
||||
# Format all code with Prettier
|
||||
bun run format
|
||||
|
||||
# Check formatting without changes
|
||||
bun run check
|
||||
|
||||
# Lint Python code
|
||||
black server.py
|
||||
pylint server.py
|
||||
```
|
||||
|
||||
### Generating Icons
|
||||
```bash
|
||||
# Install canvas package first (optional, for PNG generation)
|
||||
bun add canvas
|
||||
|
||||
# Generate PNG icons programmatically
|
||||
node app/create-icons.js
|
||||
|
||||
# Or open app/generate-icons.html in browser and manually save icons
|
||||
```
|
||||
|
||||
## Architecture and Key Components
|
||||
|
||||
### Zalgo Text Generation (zalgo.js)
|
||||
- **ZalgoGenerator class**: Core text corruption engine
|
||||
- Uses Unicode combining diacritical marks (above, middle, below)
|
||||
- Intensity scale 1-10 controls corruption amount
|
||||
- `generate()`: Creates zalgo text from input
|
||||
- `clean()`: Removes corruption from text
|
||||
|
||||
### Application Logic (app.js)
|
||||
- Real-time text corruption as user types
|
||||
- Click-to-copy functionality with visual feedback
|
||||
- Keyboard shortcuts (Ctrl+K to clear)
|
||||
- PWA installation handling
|
||||
- Service worker registration
|
||||
|
||||
### PWA Implementation
|
||||
- **Service Worker (sw.js)**: Enables offline functionality with cache-first strategy
|
||||
- **Manifest (manifest.json)**: PWA configuration for installability
|
||||
- Caches all essential files for offline use
|
||||
- Automatic cache cleanup on update
|
||||
|
||||
### Styling (styles.css)
|
||||
- Dark theme with glitch-inspired aesthetics
|
||||
- Responsive design for mobile and desktop
|
||||
- CSS animations for glitch effects
|
||||
- Print-friendly styles
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
- The `app/` directory should be served as the document root
|
||||
- All paths in the app are relative to assume `app/` is the root
|
||||
- Service worker requires HTTPS in production (except localhost)
|
||||
- Icons are currently SVG placeholders; generate PNGs for production
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
When making changes, ensure:
|
||||
1. Text corruption works with various Unicode characters
|
||||
2. Copy functionality works on both desktop and mobile
|
||||
3. PWA installs correctly
|
||||
4. Offline mode functions properly
|
||||
5. Responsive design works on all screen sizes
|
||||
6. All linting passes (JavaScript, Python, HTML validation)
|
||||
122
app/app.js
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* Main application logic for Text Corruptor
|
||||
* Handles UI interactions and PWA registration
|
||||
*/
|
||||
|
||||
/* global ZalgoGenerator */
|
||||
|
||||
// Initialize zalgo generator
|
||||
const zalgo = new ZalgoGenerator();
|
||||
|
||||
// DOM elements
|
||||
const inputText = document.getElementById('inputText');
|
||||
const outputText = document.getElementById('outputText');
|
||||
const intensitySlider = document.getElementById('intensity');
|
||||
const intensityValue = document.getElementById('intensityValue');
|
||||
const clearBtn = document.getElementById('clearBtn');
|
||||
const copyNotification = document.getElementById('copyNotification');
|
||||
|
||||
/**
|
||||
* Update the corrupted text output
|
||||
*/
|
||||
function updateOutput() {
|
||||
const text = inputText.value;
|
||||
const intensity = parseInt(intensitySlider.value);
|
||||
|
||||
if (text) {
|
||||
outputText.value = zalgo.generate(text, intensity);
|
||||
} else {
|
||||
outputText.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy text to clipboard and show notification
|
||||
*/
|
||||
async function copyToClipboard() {
|
||||
if (!outputText.value) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(outputText.value);
|
||||
|
||||
// Show copy notification
|
||||
copyNotification.classList.add('show');
|
||||
setTimeout(() => {
|
||||
copyNotification.classList.remove('show');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
// Fallback for older browsers
|
||||
outputText.select();
|
||||
document.execCommand('copy');
|
||||
|
||||
// Show copy notification
|
||||
copyNotification.classList.add('show');
|
||||
setTimeout(() => {
|
||||
copyNotification.classList.remove('show');
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all text fields
|
||||
*/
|
||||
function clearAll() {
|
||||
inputText.value = '';
|
||||
outputText.value = '';
|
||||
inputText.focus();
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
inputText.addEventListener('input', updateOutput);
|
||||
|
||||
intensitySlider.addEventListener('input', () => {
|
||||
intensityValue.textContent = intensitySlider.value;
|
||||
updateOutput();
|
||||
});
|
||||
|
||||
outputText.addEventListener('click', copyToClipboard);
|
||||
|
||||
clearBtn.addEventListener('click', clearAll);
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
document.addEventListener('keydown', e => {
|
||||
// Ctrl/Cmd + K to clear
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
clearAll();
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + C when output is focused to copy
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'c' && document.activeElement === outputText) {
|
||||
copyToClipboard();
|
||||
}
|
||||
});
|
||||
|
||||
// Register service worker for PWA functionality
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker
|
||||
.register('sw.js')
|
||||
.then(registration => {
|
||||
console.log('Service Worker registered successfully:', registration.scope);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Service Worker registration failed:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle install prompt for PWA
|
||||
window.addEventListener('beforeinstallprompt', e => {
|
||||
// Prevent the mini-infobar from appearing on mobile
|
||||
e.preventDefault();
|
||||
// Store the event so it can be triggered later if needed
|
||||
// Currently not used but kept for potential future install button
|
||||
window.deferredPrompt = e;
|
||||
console.log('Install prompt ready');
|
||||
});
|
||||
|
||||
// Focus input on load
|
||||
window.addEventListener('load', () => {
|
||||
inputText.focus();
|
||||
});
|
||||
96
app/create-icons.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Node.js script to generate PWA icons programmatically
|
||||
* Run with: node create-icons.js
|
||||
* Requires: npm install canvas
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Check if we're in a browser or Node.js environment
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
if (!isBrowser) {
|
||||
// Node.js environment - use canvas package
|
||||
try {
|
||||
const { createCanvas } = require('canvas');
|
||||
|
||||
// Icon sizes needed for PWA
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
|
||||
// Create icons directory if it doesn't exist
|
||||
const iconsDir = path.join(__dirname, 'icons');
|
||||
if (!fs.existsSync(iconsDir)) {
|
||||
fs.mkdirSync(iconsDir, { recursive: true });
|
||||
}
|
||||
|
||||
const createIcon = function (size) {
|
||||
const canvas = createCanvas(size, size);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Background gradient (solid color fallback for canvas)
|
||||
ctx.fillStyle = '#1a1a2e';
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// Center circle
|
||||
const centerX = size / 2;
|
||||
const centerY = size / 2;
|
||||
const radius = size * 0.35;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = '#00ff88';
|
||||
ctx.fill();
|
||||
|
||||
// Letter "T"
|
||||
ctx.font = `bold ${size * 0.4}px Arial`;
|
||||
ctx.fillStyle = '#0f0f14';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('T', centerX, centerY);
|
||||
|
||||
return canvas;
|
||||
};
|
||||
|
||||
// Generate all icons
|
||||
sizes.forEach(size => {
|
||||
const canvas = createIcon(size);
|
||||
const buffer = canvas.toBuffer('image/png');
|
||||
const filename = path.join(iconsDir, `icon-${size}.png`);
|
||||
fs.writeFileSync(filename, buffer);
|
||||
console.log(`Created ${filename}`);
|
||||
});
|
||||
|
||||
console.log('All icons created successfully!');
|
||||
} catch (error) {
|
||||
console.log('Canvas package not installed. Creating placeholder icons instead...');
|
||||
|
||||
// Fallback: Create placeholder SVG icons
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
const iconsDir = path.join(__dirname, 'icons');
|
||||
|
||||
if (!fs.existsSync(iconsDir)) {
|
||||
fs.mkdirSync(iconsDir, { recursive: true });
|
||||
}
|
||||
|
||||
sizes.forEach(size => {
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
|
||||
<rect width="${size}" height="${size}" fill="#1a1a2e"/>
|
||||
<circle cx="${size / 2}" cy="${size / 2}" r="${size * 0.35}" fill="#00ff88"/>
|
||||
<text x="${size / 2}" y="${size / 2}" font-family="Arial" font-size="${size * 0.4}" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>`;
|
||||
|
||||
const filename = path.join(iconsDir, `icon-${size}.svg`);
|
||||
fs.writeFileSync(filename, svg);
|
||||
console.log(`Created ${filename} (SVG placeholder)`);
|
||||
});
|
||||
|
||||
console.log('\nNote: SVG placeholders created. For PNG icons, install canvas package:');
|
||||
console.log('npm install canvas');
|
||||
}
|
||||
} else {
|
||||
console.log('This script should be run in Node.js, not in a browser.');
|
||||
}
|
||||
156
app/generate-icons.html
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Generate PWA Icons</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Icon Generator for Text Corruptor</h1>
|
||||
<p>Right-click each canvas and save as PNG to the icons folder</p>
|
||||
|
||||
<div id="canvases"></div>
|
||||
|
||||
<script>
|
||||
// Icon sizes needed for PWA
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
|
||||
function createIcon(size) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Background gradient
|
||||
const gradient = ctx.createLinearGradient(0, 0, size, size);
|
||||
gradient.addColorStop(0, '#1a1a2e');
|
||||
gradient.addColorStop(1, '#0f0f14');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// Add glitch lines effect
|
||||
ctx.strokeStyle = 'rgba(0, 255, 136, 0.1)';
|
||||
ctx.lineWidth = 1;
|
||||
for (let i = 0; i < size; i += 4) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, i);
|
||||
ctx.lineTo(size, i);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Center circle background
|
||||
const centerX = size / 2;
|
||||
const centerY = size / 2;
|
||||
const radius = size * 0.35;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
||||
const circleGradient = ctx.createRadialGradient(
|
||||
centerX,
|
||||
centerY,
|
||||
0,
|
||||
centerX,
|
||||
centerY,
|
||||
radius
|
||||
);
|
||||
circleGradient.addColorStop(0, '#00ff88');
|
||||
circleGradient.addColorStop(1, '#00cc6a');
|
||||
ctx.fillStyle = circleGradient;
|
||||
ctx.fill();
|
||||
|
||||
// Add corrupted "T" letter with zalgo effect simulation
|
||||
ctx.font = `bold ${size * 0.4}px Arial`;
|
||||
ctx.fillStyle = '#0f0f14';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
// Main letter
|
||||
ctx.fillText('T', centerX, centerY);
|
||||
|
||||
// Simulated zalgo corruption marks
|
||||
ctx.fillStyle = 'rgba(15, 15, 20, 0.5)';
|
||||
const corruption = ['̴', '̸', '̶', '̷', '̵'];
|
||||
const offsetX = size * 0.02;
|
||||
const offsetY = size * 0.02;
|
||||
|
||||
// Add small corruption marks around the letter
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const x = centerX + (Math.random() - 0.5) * offsetX * 2;
|
||||
const y = centerY + (Math.random() - 0.5) * offsetY * 2;
|
||||
ctx.font = `${size * 0.1}px Arial`;
|
||||
ctx.fillText('░', x, y - size * 0.15);
|
||||
ctx.fillText('▒', x + offsetX, y + size * 0.15);
|
||||
}
|
||||
|
||||
// Add glitch effect
|
||||
ctx.strokeStyle = '#ff3366';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.globalAlpha = 0.3;
|
||||
ctx.strokeText('T', centerX - 2, centerY);
|
||||
ctx.strokeStyle = '#00ffff';
|
||||
ctx.strokeText('T', centerX + 2, centerY);
|
||||
ctx.globalAlpha = 1;
|
||||
|
||||
// Add label
|
||||
const label = document.createElement('div');
|
||||
label.textContent = `icon-${size}.png`;
|
||||
label.style.marginTop = '10px';
|
||||
label.style.marginBottom = '20px';
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.appendChild(canvas);
|
||||
container.appendChild(label);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
// Generate all icons
|
||||
const container = document.getElementById('canvases');
|
||||
sizes.forEach(size => {
|
||||
container.appendChild(createIcon(size));
|
||||
});
|
||||
|
||||
// Auto-download function (optional - uncomment to use)
|
||||
/*
|
||||
function downloadCanvas(canvas, filename) {
|
||||
canvas.toBlob(blob => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-download all icons
|
||||
setTimeout(() => {
|
||||
const canvases = document.querySelectorAll('canvas');
|
||||
canvases.forEach((canvas, index) => {
|
||||
setTimeout(() => {
|
||||
downloadCanvas(canvas, `icon-${sizes[index]}.png`);
|
||||
}, index * 500);
|
||||
});
|
||||
}, 1000);
|
||||
*/
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
canvas {
|
||||
border: 1px solid #ccc;
|
||||
display: block;
|
||||
background: white;
|
||||
}
|
||||
#canvases > div {
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
5
app/icons/icon-128.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
|
||||
<rect width="128" height="128" fill="#1a1a2e"/>
|
||||
<circle cx="64" cy="64" r="44.8" fill="#00ff88"/>
|
||||
<text x="64" y="64" font-family="Arial" font-size="51.2" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
5
app/icons/icon-144.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="144" viewBox="0 0 144 144">
|
||||
<rect width="144" height="144" fill="#1a1a2e"/>
|
||||
<circle cx="72" cy="72" r="50.4" fill="#00ff88"/>
|
||||
<text x="72" y="72" font-family="Arial" font-size="57.6" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 400 B |
5
app/icons/icon-152.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="152" height="152" viewBox="0 0 152 152">
|
||||
<rect width="152" height="152" fill="#1a1a2e"/>
|
||||
<circle cx="76" cy="76" r="53.199999999999996" fill="#00ff88"/>
|
||||
<text x="76" y="76" font-family="Arial" font-size="60.800000000000004" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 428 B |
5
app/icons/icon-192.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
|
||||
<rect width="192" height="192" fill="#1a1a2e"/>
|
||||
<circle cx="96" cy="96" r="67.19999999999999" fill="#00ff88"/>
|
||||
<text x="96" y="96" font-family="Arial" font-size="76.80000000000001" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 426 B |
5
app/icons/icon-384.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="384" height="384" viewBox="0 0 384 384">
|
||||
<rect width="384" height="384" fill="#1a1a2e"/>
|
||||
<circle cx="192" cy="192" r="134.39999999999998" fill="#00ff88"/>
|
||||
<text x="192" y="192" font-family="Arial" font-size="153.60000000000002" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 432 B |
5
app/icons/icon-512.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" fill="#1a1a2e"/>
|
||||
<circle cx="256" cy="256" r="179.2" fill="#00ff88"/>
|
||||
<text x="256" y="256" font-family="Arial" font-size="204.8" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
5
app/icons/icon-72.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72">
|
||||
<rect width="72" height="72" fill="#1a1a2e"/>
|
||||
<circle cx="36" cy="36" r="25.2" fill="#00ff88"/>
|
||||
<text x="36" y="36" font-family="Arial" font-size="28.8" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 394 B |
5
app/icons/icon-96.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
||||
<rect width="96" height="96" fill="#1a1a2e"/>
|
||||
<circle cx="48" cy="48" r="33.599999999999994" fill="#00ff88"/>
|
||||
<text x="48" y="48" font-family="Arial" font-size="38.400000000000006" font-weight="bold" text-anchor="middle" dominant-baseline="middle" fill="#0f0f14">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 422 B |
66
app/index.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Transform your text into corrupted zalgo text with one click. Perfect for creating glitchy, distorted text effects."
|
||||
/>
|
||||
<meta name="theme-color" content="#1a1a2e" />
|
||||
<title>Text Corruptor - Zalgo Text Generator</title>
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="icons/icon-192.png" />
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="icons/icon-512.png" />
|
||||
<link rel="apple-touch-icon" href="icons/icon-192.png" />
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Text Corruptor</h1>
|
||||
<p class="subtitle">Transform your text into corrupted chaos</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="controls">
|
||||
<label for="intensity">Corruption Intensity:</label>
|
||||
<input type="range" id="intensity" min="1" max="10" value="5" />
|
||||
<span id="intensityValue">5</span>
|
||||
</div>
|
||||
|
||||
<div class="text-area-container">
|
||||
<label for="inputText">Original Text</label>
|
||||
<textarea
|
||||
id="inputText"
|
||||
placeholder="Enter your text here..."
|
||||
rows="6"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="text-area-container">
|
||||
<label for="outputText">Corrupted Text</label>
|
||||
<textarea
|
||||
id="outputText"
|
||||
placeholder="Your corrupted text will appear here..."
|
||||
rows="6"
|
||||
readonly
|
||||
title="Click to copy"
|
||||
></textarea>
|
||||
<div id="copyNotification" class="copy-notification">Copied!</div>
|
||||
</div>
|
||||
|
||||
<button id="clearBtn" class="clear-btn">Clear All</button>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>Click the corrupted text to copy it to your clipboard</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="zalgo.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
62
app/manifest.json
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"name": "Text Corruptor - Zalgo Text Generator",
|
||||
"short_name": "Text Corruptor",
|
||||
"description": "Transform your text into corrupted zalgo text with adjustable intensity",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0f0f14",
|
||||
"theme_color": "#1a1a2e",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/icon-72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"categories": ["utilities", "productivity"],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "screenshots/screenshot1.png",
|
||||
"type": "image/png",
|
||||
"sizes": "1280x720"
|
||||
}
|
||||
]
|
||||
}
|
||||
344
app/styles.css
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
/**
|
||||
* Styles for Text Corruptor
|
||||
* Dark theme with glitch-inspired aesthetics
|
||||
*/
|
||||
|
||||
:root {
|
||||
--bg-primary: #0f0f14;
|
||||
--bg-secondary: #1a1a2e;
|
||||
--bg-tertiary: #16213e;
|
||||
--text-primary: #eaeaea;
|
||||
--text-secondary: #a8a8b3;
|
||||
--accent: #00ff88;
|
||||
--accent-hover: #00cc6a;
|
||||
--error: #ff3366;
|
||||
--border: #2a2a3e;
|
||||
--shadow: rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
background-image: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(0, 255, 136, 0.03) 2px,
|
||||
rgba(0, 255, 136, 0.03) 4px
|
||||
);
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
box-shadow:
|
||||
0 10px 40px rgba(0, 0, 0, 0.5),
|
||||
0 0 100px var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
background: linear-gradient(135deg, var(--accent), #00ffff);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
text-shadow: 0 0 40px var(--shadow);
|
||||
animation: glitch 3s infinite;
|
||||
}
|
||||
|
||||
@keyframes glitch {
|
||||
0%,
|
||||
100% {
|
||||
text-shadow: 0 0 40px var(--shadow);
|
||||
}
|
||||
25% {
|
||||
text-shadow:
|
||||
-2px 0 var(--error),
|
||||
2px 0 var(--accent);
|
||||
}
|
||||
50% {
|
||||
text-shadow:
|
||||
2px 0 var(--error),
|
||||
-2px 0 var(--accent);
|
||||
}
|
||||
75% {
|
||||
text-shadow: 0 0 40px var(--shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.controls label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
#intensity {
|
||||
flex: 1;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 6px;
|
||||
background: var(--border);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#intensity::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--accent);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 0 10px var(--shadow);
|
||||
}
|
||||
|
||||
#intensity::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--accent);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 0 10px var(--shadow);
|
||||
}
|
||||
|
||||
#intensity:hover::-webkit-slider-thumb {
|
||||
transform: scale(1.2);
|
||||
box-shadow: 0 0 20px var(--shadow);
|
||||
}
|
||||
|
||||
#intensity:hover::-moz-range-thumb {
|
||||
transform: scale(1.2);
|
||||
box-shadow: 0 0 20px var(--shadow);
|
||||
}
|
||||
|
||||
#intensityValue {
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.text-area-container {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-area-container label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.95rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
padding: 15px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 20px var(--shadow);
|
||||
}
|
||||
|
||||
#outputText {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#outputText:hover {
|
||||
background: rgba(0, 255, 136, 0.05);
|
||||
}
|
||||
|
||||
.copy-notification {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: var(--accent);
|
||||
color: var(--bg-primary);
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.copy-notification.show {
|
||||
opacity: 1;
|
||||
animation: pulse 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background: transparent;
|
||||
border: 2px solid var(--accent);
|
||||
border-radius: 12px;
|
||||
color: var(--accent);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.clear-btn:hover {
|
||||
background: var(--accent);
|
||||
color: var(--bg-primary);
|
||||
box-shadow: 0 0 30px var(--shadow);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.clear-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
footer p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Mobile responsiveness */
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 120px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Glitch effect for corrupted text */
|
||||
#outputText {
|
||||
animation: subtle-glitch 5s infinite;
|
||||
}
|
||||
|
||||
@keyframes subtle-glitch {
|
||||
0%,
|
||||
100% {
|
||||
filter: none;
|
||||
}
|
||||
92% {
|
||||
filter: none;
|
||||
}
|
||||
93% {
|
||||
filter: drop-shadow(-2px 0 var(--error)) drop-shadow(2px 0 var(--accent));
|
||||
}
|
||||
94% {
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-shadow: none;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
90
app/sw.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Service Worker for Text Corruptor PWA
|
||||
* Enables offline functionality and caching
|
||||
*/
|
||||
|
||||
const CACHE_NAME = 'text-corruptor-v1';
|
||||
const urlsToCache = ['/', '/index.html', '/styles.css', '/zalgo.js', '/app.js', '/manifest.json'];
|
||||
|
||||
// Install event - cache essential files
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Opened cache');
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
.then(() => {
|
||||
// Force the service worker to become active immediately
|
||||
return self.skipWaiting();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - serve from cache when offline
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(response => {
|
||||
// Cache hit - return response
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Clone the request because it's a stream
|
||||
const fetchRequest = event.request.clone();
|
||||
|
||||
return fetch(fetchRequest)
|
||||
.then(response => {
|
||||
// Check if we received a valid response
|
||||
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Clone the response because it's a stream
|
||||
const responseToCache = response.clone();
|
||||
|
||||
caches.open(CACHE_NAME).then(cache => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(() => {
|
||||
// Network request failed, serve offline fallback if available
|
||||
return caches.match('/index.html');
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Activate event - clean up old caches
|
||||
self.addEventListener('activate', event => {
|
||||
const cacheWhitelist = [CACHE_NAME];
|
||||
|
||||
event.waitUntil(
|
||||
caches
|
||||
.keys()
|
||||
.then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheWhitelist.indexOf(cacheName) === -1) {
|
||||
console.log('Deleting old cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
// Ensure the service worker takes control immediately
|
||||
return self.clients.claim();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Handle messages from the client
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
267
app/zalgo.js
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/**
|
||||
* Zalgo Text Generator
|
||||
* Generates corrupted/glitched text using Unicode combining characters
|
||||
*/
|
||||
|
||||
/* exported ZalgoGenerator */
|
||||
|
||||
class ZalgoGenerator {
|
||||
constructor() {
|
||||
// Combining diacritical marks that appear above characters
|
||||
this.above = [
|
||||
'\u0300',
|
||||
'\u0301',
|
||||
'\u0302',
|
||||
'\u0303',
|
||||
'\u0304',
|
||||
'\u0305',
|
||||
'\u0306',
|
||||
'\u0307',
|
||||
'\u0308',
|
||||
'\u0309',
|
||||
'\u030A',
|
||||
'\u030B',
|
||||
'\u030C',
|
||||
'\u030D',
|
||||
'\u030E',
|
||||
'\u030F',
|
||||
'\u0310',
|
||||
'\u0311',
|
||||
'\u0312',
|
||||
'\u0313',
|
||||
'\u0314',
|
||||
'\u0315',
|
||||
'\u0316',
|
||||
'\u0317',
|
||||
'\u0318',
|
||||
'\u0319',
|
||||
'\u031A',
|
||||
'\u031B',
|
||||
'\u031C',
|
||||
'\u031D',
|
||||
'\u031E',
|
||||
'\u031F',
|
||||
'\u0320',
|
||||
'\u0321',
|
||||
'\u0322',
|
||||
'\u0323',
|
||||
'\u0324',
|
||||
'\u0325',
|
||||
'\u0326',
|
||||
'\u0327',
|
||||
'\u0328',
|
||||
'\u0329',
|
||||
'\u032A',
|
||||
'\u032B',
|
||||
'\u032C',
|
||||
'\u032D',
|
||||
'\u032E',
|
||||
'\u032F',
|
||||
'\u0330',
|
||||
'\u0331',
|
||||
'\u0332',
|
||||
'\u0333',
|
||||
'\u0334',
|
||||
'\u0335',
|
||||
'\u0336',
|
||||
'\u0337',
|
||||
'\u0338',
|
||||
'\u0339',
|
||||
'\u033A',
|
||||
'\u033B',
|
||||
'\u033C',
|
||||
'\u033D',
|
||||
'\u033E',
|
||||
'\u033F',
|
||||
'\u0340',
|
||||
'\u0341',
|
||||
'\u0342',
|
||||
'\u0343',
|
||||
'\u0344',
|
||||
'\u0345',
|
||||
'\u0346',
|
||||
'\u0347',
|
||||
'\u0348',
|
||||
'\u0349',
|
||||
'\u034A',
|
||||
'\u034B',
|
||||
'\u034C',
|
||||
'\u034D',
|
||||
'\u034E',
|
||||
'\u034F',
|
||||
'\u0350',
|
||||
'\u0351',
|
||||
'\u0352',
|
||||
'\u0353',
|
||||
'\u0354',
|
||||
'\u0355',
|
||||
'\u0356',
|
||||
'\u0357',
|
||||
'\u0358',
|
||||
'\u0359',
|
||||
'\u035A',
|
||||
'\u035B',
|
||||
'\u035C',
|
||||
'\u035D',
|
||||
'\u035E',
|
||||
'\u035F',
|
||||
'\u0360',
|
||||
'\u0361',
|
||||
'\u0362',
|
||||
'\u0363',
|
||||
'\u0364',
|
||||
'\u0365',
|
||||
'\u0366',
|
||||
'\u0367',
|
||||
'\u0368',
|
||||
'\u0369',
|
||||
'\u036A',
|
||||
'\u036B',
|
||||
'\u036C',
|
||||
'\u036D',
|
||||
'\u036E',
|
||||
'\u036F',
|
||||
];
|
||||
|
||||
// Combining diacritical marks that appear in the middle
|
||||
this.middle = [
|
||||
'\u0315',
|
||||
'\u031B',
|
||||
'\u0340',
|
||||
'\u0341',
|
||||
'\u0358',
|
||||
'\u0321',
|
||||
'\u0322',
|
||||
'\u0327',
|
||||
'\u0328',
|
||||
'\u0334',
|
||||
'\u0335',
|
||||
'\u0336',
|
||||
'\u034F',
|
||||
'\u035C',
|
||||
'\u035D',
|
||||
'\u035E',
|
||||
'\u035F',
|
||||
'\u0360',
|
||||
'\u0362',
|
||||
'\u0338',
|
||||
'\u0337',
|
||||
'\u0361',
|
||||
'\u0489',
|
||||
];
|
||||
|
||||
// Combining diacritical marks that appear below characters
|
||||
this.below = [
|
||||
'\u0316',
|
||||
'\u0317',
|
||||
'\u0318',
|
||||
'\u0319',
|
||||
'\u031C',
|
||||
'\u031D',
|
||||
'\u031E',
|
||||
'\u031F',
|
||||
'\u0320',
|
||||
'\u0324',
|
||||
'\u0325',
|
||||
'\u0326',
|
||||
'\u0329',
|
||||
'\u032A',
|
||||
'\u032B',
|
||||
'\u032C',
|
||||
'\u032D',
|
||||
'\u032E',
|
||||
'\u032F',
|
||||
'\u0330',
|
||||
'\u0331',
|
||||
'\u0332',
|
||||
'\u0333',
|
||||
'\u0339',
|
||||
'\u033A',
|
||||
'\u033B',
|
||||
'\u033C',
|
||||
'\u0345',
|
||||
'\u0347',
|
||||
'\u0348',
|
||||
'\u0349',
|
||||
'\u034D',
|
||||
'\u034E',
|
||||
'\u0353',
|
||||
'\u0354',
|
||||
'\u0355',
|
||||
'\u0356',
|
||||
'\u0359',
|
||||
'\u035A',
|
||||
'\u0323',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random character from an array
|
||||
*/
|
||||
randomChar(array) {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a specified number of random characters from an array
|
||||
*/
|
||||
randomChars(array, count) {
|
||||
let result = '';
|
||||
for (let i = 0; i < count; i++) {
|
||||
result += this.randomChar(array);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate zalgo text with specified intensity
|
||||
* @param {string} text - The input text to corrupt
|
||||
* @param {number} intensity - Corruption intensity (1-10)
|
||||
* @returns {string} - The corrupted zalgo text
|
||||
*/
|
||||
generate(text, intensity = 5) {
|
||||
if (!text) return '';
|
||||
|
||||
// Normalize intensity to 1-10 range
|
||||
intensity = Math.max(1, Math.min(10, intensity));
|
||||
|
||||
let result = '';
|
||||
|
||||
for (let char of text) {
|
||||
result += char;
|
||||
|
||||
// Skip whitespace and newlines
|
||||
if (/\s/.test(char)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate how many combining characters to add based on intensity
|
||||
// Higher intensity = more corruption
|
||||
const factor = intensity / 10;
|
||||
|
||||
// Add characters above (0-3 based on intensity)
|
||||
const aboveCount = Math.floor(Math.random() * (4 * factor));
|
||||
result += this.randomChars(this.above, aboveCount);
|
||||
|
||||
// Add characters in middle (0-2 based on intensity)
|
||||
const middleCount = Math.floor(Math.random() * (3 * factor));
|
||||
result += this.randomChars(this.middle, middleCount);
|
||||
|
||||
// Add characters below (0-3 based on intensity)
|
||||
const belowCount = Math.floor(Math.random() * (4 * factor));
|
||||
result += this.randomChars(this.below, belowCount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove zalgo corruption from text
|
||||
* @param {string} text - The corrupted text
|
||||
* @returns {string} - Clean text without combining characters
|
||||
*/
|
||||
clean(text) {
|
||||
// Remove all combining characters (Unicode category Mn)
|
||||
return text.replace(/[\u0300-\u036f\u0489]/g, '');
|
||||
}
|
||||
}
|
||||
216
bun.lock
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "text-corruptor",
|
||||
"devDependencies": {
|
||||
"eslint": "^8.57.0",
|
||||
"prettier": "^3.2.5",
|
||||
"vnu-jar": "^24.10.17",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
|
||||
|
||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="],
|
||||
|
||||
"@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="],
|
||||
|
||||
"@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="],
|
||||
|
||||
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||
|
||||
"@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="],
|
||||
|
||||
"eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="],
|
||||
|
||||
"eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
|
||||
|
||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
||||
|
||||
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||
|
||||
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||
|
||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="],
|
||||
|
||||
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||
|
||||
"flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="],
|
||||
|
||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
||||
|
||||
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||
|
||||
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="],
|
||||
|
||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||
|
||||
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"vnu-jar": ["vnu-jar@24.10.17", "", {}, "sha512-YT7gNrRY5PiJrI1GavlWRHWIwqq2o52COc6J9QeXPfoldKRiZ9BeGP4shNLLaVfi0naA+/LMksdYWkKCr4pnVg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
}
|
||||
}
|
||||
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "text-corruptor",
|
||||
"version": "1.0.0",
|
||||
"description": "Zalgo text generator PWA",
|
||||
"scripts": {
|
||||
"serve": "python3 server.py",
|
||||
"lint:js": "eslint app/*.js",
|
||||
"lint:js:fix": "eslint app/*.js --fix",
|
||||
"format": "prettier --write app/*.{js,html,css}",
|
||||
"check": "prettier --check app/*.{js,html,css}"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.57.0",
|
||||
"prettier": "^3.2.5",
|
||||
"vnu-jar": "^24.10.17"
|
||||
}
|
||||
}
|
||||
39
server.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple HTTP server for testing the Text Corruptor PWA
|
||||
Run with: python3 server.py
|
||||
Then open http://localhost:8000/ in your browser
|
||||
"""
|
||||
|
||||
import http.server
|
||||
import socketserver
|
||||
import os
|
||||
|
||||
PORT = 8000
|
||||
# Serve from the app directory
|
||||
DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app")
|
||||
|
||||
|
||||
class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||
"""Custom HTTP request handler for serving the Text Corruptor PWA."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, directory=DIRECTORY, **kwargs)
|
||||
|
||||
def end_headers(self):
|
||||
# Add headers for PWA and CORS
|
||||
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
self.send_header("Service-Worker-Allowed", "/")
|
||||
self.send_header("X-Content-Type-Options", "nosniff")
|
||||
super().end_headers()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with socketserver.TCPServer(("", PORT), MyHTTPRequestHandler) as httpd:
|
||||
print(f"Server running at http://localhost:{PORT}/")
|
||||
print(f"Serving files from: {DIRECTORY}")
|
||||
print("Press Ctrl+C to stop the server")
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print("\nServer stopped.")
|
||||