Refined UI using a completely reconstructed Glassmorphism aesthetic and modern UX updates. Updated support for up to 26.3.
This commit is contained in:
Miguel Villegas
2026-04-20 15:21:23 -05:00
parent ea06488810
commit 7ff6b67362
4 changed files with 1017 additions and 690 deletions
+68 -115
View File
@@ -1,145 +1,98 @@
# MobaXterm Keygen & Customizer 🚀
<div align="center">
<img src="https://i.imgur.com/FtcF9ZP.png" alt="MobaXterm Keygen Logo" width="120" />
<h1>MobaXterm Keygen & Customizer 🚀</h1>
<p><strong>Fast, and secure license generator and customizer.</strong></p>
![License](https://img.shields.io/badge/License-MIT-blue.svg)
![Version](https://img.shields.io/badge/Version-v2.7-green.svg)
![Compatibility](https://img.shields.io/badge/Compatible-v20.X%20|%20v25.4%20|%20v26.0-orange)
<p>
<a href="#"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="License" /></a>
<a href="#"><img src="https://img.shields.io/badge/Version-v3.0-green.svg?style=for-the-badge" alt="Version" /></a>
<a href="#"><img src="https://img.shields.io/badge/Compatible-v20.X%20|%20v26.2%20|%20v26.3-orange?style=for-the-badge" alt="Compatibility" /></a>
</p>
> ⭐ **Please star this repository if you find it helpful!** ⭐
<p><em>⭐ Please star this repository if you find it helpful! ⭐</em></p>
</div>
## Overview
---
A modern, user-friendly web tool for generating activation keys and customizing MobaXterm, the ultimate terminal emulator for Windows with an integrated X11 server, SSH client, and comprehensive network toolset.
## 🔥 Overview
## Features
A modern, user-friendly, and beautifully designed web tool for generating activation keys and customizing **MobaXterm**—the ultimate terminal emulator for Windows with an integrated X11 server, SSH client, and comprehensive network toolset.
- **✨ Clean, Modern UI** - Responsive design optimized for both desktop and mobile
- **🌓 Dark/Light Mode** - Toggle between themes with a click
- **🔒 Multiple Editions Support** - Generate keys for different MobaXterm editions
- **👥 Custom User Count** - Specify the number of concurrent users
- **💾 One-Click Downloads** - Instantly download your custom activation file
- **🔧 Complete Customizer** - Create personalized settings without the official customizer tool
- **🖼️ Custom Logo Support** - Replace the default MobaXterm logo with your own
- **📑 Bookmarks Manager** - Pre-define SSH, FTP, and other connection profiles
- **🔌 Plugin Integration** - Include plugins in your custom configuration
All processes run **100% locally** in your browser, ensuring maximum privacy and security.
## Compatibility
## ✨ Key Features
- Works with **MobaXterm versions 20.X, 25.4, and 26.0**
- Supports both portable and installer editions
- **💎 Premium Glassmorphism UI** - Enjoy a sleek, modern, and completely responsive experience.
- **🌓 Adaptive Theme** - Seamless dark and light mode toggle.
- **🔒 Multi-Edition Support** - Generate licenses for various MobaXterm editions effortlessly.
- **👥 Flexible User Allocation** - Define your own custom concurrent user limit.
- **💾 One-Click Downloads** - Get your `.mxtpro` files instantly in a neat package.
- **🔧 Built-in Customizer Integration** - No need for the official cli tool; apply advanced setups visually.
- **🖼️ Brand Personalization** - Easily switch the default MobaXterm logo with your company's own.
- **🔌 Plugins & Bookmarks** - Embed `.mxt3` plugins and predefined SSH/FTP profiles instantly.
## How to Use
## 🚀 Compatibility
### Key Generator
- Official support tested extensively with MobaXterm versions **20.X, 26.2, and 26.3**.
- Compatible with both **Portable** and **Installer (Desktop)** editions.
1. **Visit the Generator**: Go to the [MobaXterm Key Generator](https://moba-xterm-keygen.vercel.app/)
2. **Fill in the Form**:
- Select the desired MobaXterm edition
- Enter a username (letters only)
- Choose your MobaXterm version (20.X or 26.X)
- Specify the number of users
3. **Download**: Click "Download Key" to get your Custom.mxtpro file
4. **Activate**: Place the generated file in your MobaXterm installation directory:
```
## 📖 How to Use
### 1️⃣ The Key Generator
1. **Access the Generator**: Open the [MobaXterm Key Generator](https://moba-xterm-keygen.vercel.app/) web application.
2. **Setup your License**:
- Choose your preferred MobaXterm Edition from the dropdown.
- Enter your username (alphabetical characters only).
- Input your current target version (e.g., `26.3`).
- Define your desired number of users.
3. **Generate**: Click the big **"Generate License"** button to start downloading your `Custom.mxtpro` key.
4. **Deploy**: Drop this file inside your MobaXterm installation folder:
```text
C:\Program Files (x86)\Mobatek\MobaXterm
```
### Customizer
### 2️⃣ Advanced: Settings Merger
1. **Go to the Customizer Tab** in the web interface
2. **Configure Your Settings**:
- **Banner**: Create a personalized welcome message
- **Profile**: Configure a custom bash profile (similar to /etc/profile in Linux)
- **Settings**: Toggle MobaXterm features on/off
- **Logo**: Upload your custom logo image
- **Bookmarks**: Create predefined connections for your users
- **Plugins**: Add optional MobaXterm plugins (.mxt3 files)
3. **Generate Settings**: Click "Generate Configuration File" to download your settings
4. **Apply Settings**: Use either method below to apply your customization
If you have personalized MobaXterm preferences (Customizer tweaks, logos, bash profiles):
### Settings Merger
1. Switch to the **Settings Merger** tab inside our web app.
2. **Export your settings**: Run `.\MobaXterm.exe -customizer` and select the option to export to `MobaXterm customization.custom`.
3. **Upload both files**:
- The license key file (`Custom.mxtpro`).
- The exported configuration file (`MobaXterm customization.custom`).
4. Click **Merge Files** to inject everything into a single, unified `Custom.mxtpro` file to deploy normally.
1. **Go to the Custom Settings Merger Tab**
2. **Upload Files**:
- Your license key file (Custom.mxtpro)
- Your customization file (MobaXterm customization.custom)
3. **Merge**: Create a single file with both your license and custom settings
4. **Deploy**: Copy the merged Custom.mxtpro to your MobaXterm installation directory
## 🛠 Troubleshooting
## Installation Guide
<details>
<summary><strong>Activation isn't being recognized?</strong></summary>
Ensure you are running a supported version range (v20.X, 26.2, or 26.3). Also, make sure the file is strictly named `Custom.mxtpro` without any duplicates like `Custom (1).mxtpro`.
</details>
### Step 1: Download MobaXterm
Download the latest version from the [official MobaXterm website](https://mobaxterm.mobatek.net/download-home-edition.html).
<details>
<summary><strong>File generation fails?</strong></summary>
Double check that your provided username only encompasses letters. Spaces and unique symbols are currently not supported by the underlying cryptography validation checks.
</details>
### Step 2: Generate Your Key & Customization
Use our tool to:
1. Create your activation key
2. Customize your settings (optional)
3. Merge your license with your customization (optional)
## 🔐 Technical & Security Details
### Step 3: Activate MobaXterm
1. Locate your MobaXterm installation folder:
- **Default Path**: `C:\Program Files (x86)\Mobatek\MobaXterm`
- Or right-click the MobaXterm shortcut and select "Open file location"
2. Copy the `Custom.mxtpro` file to this directory
3. Start MobaXterm - your license and customizations should now be applied!
This tool operates completely **Client-Side** leveraging `Vue.js` and pure DOM manipulations. **Not a single byte of your data or generated licenses travels to any backend server.**
### Alternative Customization Method
> **Disclaimer:** This project is provided exclusively for educational concepts and reverse-engineering study cases. For commercial environments, please consider supporting the official developers by purchasing an enterprise license.
MobaXterm.exe has a customizer mode that you can access by adding the parameter "-customizer":
## 📈 Version History
```
.\MobaXterm.exe -customizer
```
This official tool also allows you to export your settings to a file named "MobaXterm customization.custom". Our web-based customizer provides similar functionality but is accessible directly in your browser.
## Troubleshooting
1. **Activation Not Working?**
- Verify you're using a compatible version (20.X, 25.4, or 26.0)
- Make sure the file is in the correct location
- Check that the file is named exactly `Custom.mxtpro`
2. **Error During Key Generation?**
- Ensure all form fields are filled correctly
- Username must contain only letters (no spaces, numbers, or special characters)
- Try refreshing the page and generating again
3. **Customization Not Applying?**
- Verify that you've merged the customization with your license file
- Make sure the merged file is named exactly `Custom.mxtpro`
- Try restarting MobaXterm after placing the file in the installation directory
## Technical Details
This tool uses client-side processing to generate valid license keys and customization files based on your inputs. Everything is generated directly in your browser - no data is sent to any server, ensuring your privacy and security.
## Disclaimer
This tool is provided for educational purposes only. Please support the developers by purchasing a license for commercial use.
## Development
### Built With
- Vue.js
- Tailwind CSS
- Modern JavaScript
### Version History
- **v2.7** - Added support for version 26,0
- **v3.0** - Refined UI using a completely reconstructed Glassmorphism aesthetic and modern UX updates. Updated support for up to `26.3`.
- **v2.7** - Added support for version 26.0
- **v2.6** - Added support for version 25.4
- **v2.5** - Added support for version 25.3
- **v2.4** - Added full MobaXterm customizer functionality
- **v2.3** - Added custom settings merger
- **v2.2** - Dark mode and mobile optimization
- **v2.1** - Added support for version 25.2
- **v2.4** - Brought full MobaXterm customizer functionality
- **v2.0** - Major UI redesign
- **v1.0** - Initial release
---
Created with ❤️ by Zarfala
---
⭐ [Star this repository on GitHub!](https://github.com/zarfadev/MobaXterm-Keygen) ⭐
<div align="center">
<strong>Crafted with ❤️ by Zarfala</strong><br>
<a href="https://github.com/zarfadev/MobaXterm-Keygen">⭐ Star us on GitHub</a>
</div>
+17 -37
View File
@@ -1,19 +1,7 @@
/*
NOTE: This is just an experiment and
could use some changes to improve performance:
/* VIP Aurora Stripes Background */
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap');
https://x.com/actualTimWilson/status/1730753841795158313
See a production optimized version here: https://charmui.com
*/
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
body {
font-family: Inter, sans-serif;
}
@keyframes jumbo {
@keyframes aurora {
from {
background-position: 50% 50%, 50% 50%;
}
@@ -22,22 +10,18 @@ body {
}
}
/* New Styles for Form Isolation */
.box-main {
position: relative;
z-index: 1;
z-index: 10;
}
.jumbo {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
inset: 0;
width: 100vw;
height: 100vh;
z-index: 0;
}
.jumbo {
pointer-events: none;
--stripes: repeating-linear-gradient(
100deg,
#fff 0%,
@@ -62,15 +46,16 @@ body {
#5eead4 25%,
#60a5fa 30%
);
mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
background-image: var(--stripes), var(--rainbow);
background-size: 300%, 200%;
background-position: 50% 50%, 50% 50%;
filter: blur(10px) invert(100%);
mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 70%);
pointer-events: none;
filter: blur(15px) invert(100%);
opacity: 0.9;
}
.jumbo::after {
@@ -79,20 +64,15 @@ body {
inset: 0;
background-image: var(--stripes), var(--rainbow);
background-size: 200%, 100%;
animation: jumbo 60s linear infinite;
background-attachment: fixed;
animation: aurora 40s linear infinite;
mix-blend-mode: difference;
}
.dark .jumbo {
background-image: var(--stripesDark), var(--rainbow);
filter: blur(10px) opacity(50%) saturate(200%);
filter: blur(15px) opacity(60%) saturate(200%);
}
.dark .jumbo::after {
background-image: var(--stripesDark), var(--rainbow);
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255);
}
+547 -183
View File
@@ -1,12 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="antialiased">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MobaXterm Keygen</title>
<meta name="description" content="Website created with love by Zarfala" />
<meta name="description" content="Professional Key Generator for MobaXterm - Designed by Zarfala" />
<link rel="icon" href="https://i.imgur.com/FtcF9ZP.png" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link href="./css/pure-min.css?=2" rel="stylesheet" />
<link href="./css/style.css" rel="stylesheet" />
<link href="./css/fire.css" rel="stylesheet" />
@@ -16,261 +20,623 @@
darkMode: "class",
theme: {
extend: {
screens: {
'xs': '475px',
fontFamily: {
sans: ['Outfit', 'sans-serif'],
},
colors: {
primary: {
50: '#eef2ff',
100: '#e0e7ff',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
200: '#c3dafe',
300: '#a3bffa',
400: '#7f9cf5',
500: '#667eea',
600: '#5a67d8',
700: '#4c51bf',
800: '#434190',
900: '#3c366b',
}
},
animation: {
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
'blob': 'blob 7s infinite',
'fade-in-up': 'fadeInUp 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards',
'float': 'float 6s ease-in-out infinite',
},
keyframes: {
blob: {
'0%': { transform: 'translate(0px, 0px) scale(1)' },
'33%': { transform: 'translate(30px, -50px) scale(1.1)' },
'66%': { transform: 'translate(-20px, 20px) scale(0.9)' },
'100%': { transform: 'translate(0px, 0px) scale(1)' },
},
fadeInUp: {
'0%': { opacity: '0', transform: 'translateY(40px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
float: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
}
}
}
}
}
</script>
<!-- Preloader style -->
<style>
body {
font-family: 'Outfit', sans-serif;
}
/* Custom modern scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(156, 163, 175, 0.5);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(107, 114, 128, 0.8);
}
/* Preloader */
#preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #111;
inset: 0;
background: #f8fafc;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
transition: background-color 0.3s, opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), visibility 0.6s;
}
.dark #preloader {
background: #000000;
}
#preloader.fade-out {
opacity: 0;
visibility: hidden;
}
.spinner {
width: 60px;
height: 60px;
border: 5px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #6366f1;
animation: spin 1s ease-in-out infinite;
position: relative;
}
.spinner::before,
.spinner::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
border: 4px solid transparent;
}
.spinner::before {
border-top-color: #667eea;
border-right-color: #667eea;
animation: spin 1.5s linear infinite;
}
.spinner::after {
border-bottom-color: #ed64a6;
border-left-color: #ed64a6;
animation: spin 1s linear infinite reverse;
}
@keyframes spin {
to {
100% {
transform: rotate(360deg);
}
}
.content-hidden {
visibility: hidden;
opacity: 0;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
transition: opacity 0.8s ease-in-out, visibility 0.8s;
}
.content-visible {
visibility: visible;
opacity: 1;
}
/* Glass panel effect */
.glass-panel {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(40px);
-webkit-backdrop-filter: blur(40px);
border: 1px solid rgba(255, 255, 255, 0.6);
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
}
.dark .glass-panel {
background: rgba(15, 23, 42, 0.75);
backdrop-filter: blur(40px);
-webkit-backdrop-filter: blur(40px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 30px 60px -12px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05) inset;
}
/* Modern Forms */
.input-modern {
background: rgba(255, 255, 255, 0.7);
border: 2px solid rgba(209, 213, 219, 0.6);
color: #1f2937;
transition: all 0.3s ease;
}
.input-modern:focus {
background: #ffffff;
border-color: #667eea;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2);
}
.dark .input-modern {
background: rgba(15, 23, 42, 0.7);
border: 2px solid rgba(71, 85, 105, 0.6);
color: #f8fafc;
}
.dark .input-modern:focus {
background: rgba(15, 23, 42, 0.95);
border-color: #7f9cf5;
box-shadow: 0 0 0 4px rgba(127, 156, 245, 0.2);
}
/* Button Gradient */
.btn-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background-size: 200% auto;
color: white;
transition: 0.5s ease;
box-shadow: 0 10px 20px -5px rgba(102, 126, 234, 0.4);
}
.btn-gradient:hover {
background-position: right center;
box-shadow: 0 15px 25px -5px rgba(102, 126, 234, 0.5);
transform: translateY(-2px);
}
.btn-gradient:active {
transform: translateY(1px);
box-shadow: 0 5px 10px -5px rgba(102, 126, 234, 0.4);
}
/* Toggle Active Tab */
.tab-active {
position: relative;
color: #667eea;
font-weight: 600;
}
.dark .tab-active {
color: #a3bffa;
}
.tab-active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, #667eea, #ed64a6);
border-radius: 3px 3px 0 0;
animation: tabSlide 0.3s ease-out forwards;
}
@keyframes tabSlide {
from {
width: 0;
opacity: 0;
left: 50%;
transform: translateX(-50%);
}
to {
width: 100%;
opacity: 1;
left: 0;
transform: translateX(0);
}
}
select option {
background-color: white;
color: black;
}
.dark select option {
background-color: #1e293b;
color: white;
}
.animate-gradient-text {
background-size: 300%;
animation: gradientText 6s ease-in-out infinite;
}
@keyframes gradientText {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
</style>
<!-- Meta tags for better SEO and sharing -->
<meta property="og:title" content="MobaXterm Keygen" />
<meta property="og:type" content="website" />
<meta property="og:description" content="Website created with love by Zarfala" />
<meta property="og:image" content="https://i.imgur.com/FtcF9ZP.png" />
<meta property="og:description" content="Professional Key Generator for MobaXterm" />
<meta property="og:site_name" content="MobaXterm Keygen" />
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="MobaXterm Keygen">
<meta name="twitter:description" content="Website created with love by Zarfala">
<meta name="twitter:image" content="https://i.imgur.com/FtcF9ZP.png">
<meta name="twitter:description" content="Professional Key Generator for MobaXterm created by Zarfala">
<script>
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
</script>
</head>
<body class="transition-colors duration-300">
<body class="transition-colors duration-500 bg-slate-50 dark:bg-black font-sans text-slate-800 dark:text-slate-200">
<!-- Preloader -->
<div id="preloader">
<div class="spinner"></div>
</div>
<main id="main-content" class="content-hidden">
<div class="relative flex min-h-screen flex-col items-center justify-center bg-white dark:bg-black transition-colors duration-300 overflow-hidden px-4">
<!-- Background effect -->
<div class="flex inset-0 overflow-hidden">
<div class="jumbo absolute opacity-60"></div>
<main id="main-content" class="content-hidden antialiased">
<div
class="relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 sm:px-6 py-12 lg:py-24">
<!-- Abstract Animated Background Elements -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<!-- Enhanced original VIP Jumbo effect -->
<div class="jumbo"></div>
<!-- Modern animated blobs -->
<div
class="absolute top-0 -left-4 w-72 h-72 bg-purple-400 rounded-full mix-blend-multiply filter blur-2xl opacity-20 dark:opacity-20 animate-blob">
</div>
<div class="box-main w-full max-w-xl mx-auto">
<div id="app" class="relative z-10 p-6 sm:p-10 w-full text-left shadow-lg rounded-xl bg-white dark:bg-slate-900/80 dark:backdrop-blur dark:ring-1 dark:ring-inset dark:ring-white/10 transition-all duration-300">
<div
class="absolute top-0 -right-4 w-72 h-72 bg-blue-400 rounded-full mix-blend-multiply filter blur-2xl opacity-20 dark:opacity-20 animate-blob animate-delay-2000">
</div>
<div
class="absolute -bottom-8 left-20 w-72 h-72 bg-pink-400 rounded-full mix-blend-multiply filter blur-2xl opacity-20 dark:opacity-20 animate-blob animate-delay-4000">
</div>
</div>
<div class="z-10 w-full max-w-2xl mx-auto animate-fade-in-up">
<div id="app" class="glass-panel rounded-3xl p-8 md:p-12 transition-all duration-300 relative overflow-hidden">
<!-- Shimmer effect inside card -->
<div
class="absolute inset-0 bg-gradient-to-tr from-white/5 to-white/0 dark:from-white/5 dark:to-transparent pointer-events-none rounded-3xl">
</div>
<!-- Header Section -->
<header class="mb-8 text-center">
<h1 class="relative flex flex-col xs:flex-row items-center justify-center text-4xl sm:text-5xl font-bold text-gray-800 dark:text-white dark:opacity-95 transition-colors">
<header class="mb-10 text-center relative z-10">
<!-- Top Badge -->
<div
class="inline-flex items-center justify-center space-x-2 px-3 py-1 mb-6 rounded-full bg-primary-100 dark:bg-primary-900/40 border border-primary-200 dark:border-primary-800 text-xs font-semibold text-primary-700 dark:text-primary-300">
<span class="flex h-2 w-2 relative">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary-500"></span>
</span>
<span>v20.X, 26.2 & 26.3 Compatible</span>
</div>
<!-- Main Title -->
<h1 class="flex flex-wrap items-center justify-center gap-x-3 text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tight text-slate-900 dark:text-white mb-2">
<span>MobaXterm</span>
<span class="mt-2 xs:mt-0 xs:ml-2 rounded-xl bg-current p-2 text-[0.7em] leading-none">
<span class="text-white dark:text-black">Keygen</span>
<span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-600 via-pink-500 to-indigo-600 animate-gradient-text inline-block py-2 pr-2">
Keygen
</span>
</h1>
<!-- Version Badge -->
<div class="mt-4 inline-flex items-center px-3 py-1 rounded-full bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-sm font-medium text-white"> Compatible with versions 20.X, 25.4 and 26.0 </div>
<p class="text-slate-500 dark:text-slate-400 mt-2 text-sm sm:text-base max-w-sm mx-auto">
Fast, and secure license generator.
</p>
<!-- Theme Toggle -->
<div class="mt-4">
<button onclick="toggleTheme()" class="px-3 py-1 border border-stone-200 rounded-full drop-shadow-sm text-sm text-stone-800 dark:text-white bg-white/40 dark:bg-black/40 backdrop-blur-lg hover:border-stone-300 transition-colors dark:border-stone-500 dark:hover:border-stone-400"> Toggle Theme </button>
</div>
<!-- Tab Navigation -->
<div class="mt-6 border-b border-gray-200 dark:border-gray-700">
<nav class="flex space-x-4" aria-label="Tabs">
<button @click="activeTab = 'keyGenerator'"
:class="[activeTab === 'keyGenerator' ? 'border-primary-500 text-primary-600 dark:text-primary-400' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300', 'px-1 pb-4 text-sm font-medium border-b-2']">
Key Generator
<div class="absolute top-0 right-0 sm:-top-4 sm:-right-4 p-2 sm:p-0">
<button onclick="toggleTheme()"
class="p-3 rounded-full bg-white/60 dark:bg-slate-800/60 border border-slate-200 dark:border-slate-700 shadow-sm text-slate-600 dark:text-slate-300 hover:bg-white dark:hover:bg-slate-800 transition-all focus:outline-none focus:ring-2 focus:ring-primary-500"
aria-label="Toggle Theme">
<svg id="theme-icon-dark" class="hidden w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
<svg id="theme-icon-light" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
</button>
<button @click="activeTab = 'customMerger'"
:class="[activeTab === 'customMerger' ? 'border-primary-500 text-primary-600 dark:text-primary-400' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300', 'px-1 pb-4 text-sm font-medium border-b-2']">
Custom Settings Merger
</button>
</nav>
</div>
</header>
<!-- Tab Navigation -->
<div class="mb-10 relative z-10 flex justify-center w-full">
<nav
class="flex space-x-2 bg-slate-200/60 dark:bg-slate-800/60 p-1.5 rounded-2xl w-full max-w-sm mx-auto shadow-inner border border-slate-300/30 dark:border-slate-700/50"
aria-label="Tabs">
<button @click="activeTab = 'keyGenerator'"
:class="[activeTab === 'keyGenerator' ? 'bg-white dark:bg-slate-700 text-primary-600 dark:text-primary-400 shadow-sm' : 'text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200', 'w-1/2 py-2.5 text-sm sm:text-base font-bold transition-all duration-300 rounded-xl tracking-wide']">
Key Generator
</button>
<button @click="activeTab = 'customMerger'"
:class="[activeTab === 'customMerger' ? 'bg-white dark:bg-slate-700 text-primary-600 dark:text-primary-400 shadow-sm' : 'text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200', 'w-1/2 py-2.5 text-sm sm:text-base font-bold transition-all duration-300 rounded-xl tracking-wide']">
Merger
</button>
</nav>
</div>
<!-- Key Generator Tab -->
<div v-if="activeTab === 'keyGenerator'">
<div v-show="activeTab === 'keyGenerator'" class="relative z-10 animate-fade-in-up"
style="animation-duration: 0.4s;">
<form @submit.prevent="validateAndGenerate">
<div class="space-y-6">
<div>
<label for="licenseType" class="block text-lg text-gray-700 dark:text-gray-300">Edition:</label>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Edition selection -->
<div class="col-span-1 md:col-span-2 group">
<label for="licenseType"
class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-1.5 ml-1">Edition
Type</label>
<div class="relative">
<select name="licenseType" id="licenseType" v-model="licenseType" class="w-full mt-1 p-2 pr-8 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors appearance-none">
<option value="" disabled>Select an option</option>
<div
class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none text-slate-400 group-focus-within:text-primary-500 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z">
</path>
</svg>
</div>
<select name="licenseType" id="licenseType" v-model="licenseType"
class="input-modern block w-full pl-11 pr-10 py-3.5 rounded-xl outline-none appearance-none font-medium cursor-pointer">
<option value="" disabled>Select Edition</option>
<option v-for="(val, key) in LICENSE_TYPES" :value="val">{{key}}</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 mt-1 text-gray-700 dark:text-gray-300">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"></path>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4 text-slate-400 group-focus-within:text-primary-500 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</div>
</div>
<p v-if="errors.licenseType" class="mt-1 text-red-500 text-sm">{{ errors.licenseType }}</p>
<p v-if="errors.licenseType" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{
errors.licenseType }}</p>
</div>
<div>
<label for="userName" class="block text-lg text-gray-700 dark:text-gray-300">Username:</label>
<!-- Username -->
<div class="group">
<label for="userName"
class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-1.5 ml-1">Username</label>
<div class="relative">
<input type="text" id="userName" v-model="userName" @input="validateUserName" placeholder="Example defaultUser" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.userName" class="mt-1 text-red-500 text-sm">{{ errors.userName }}</p>
<div
class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none text-slate-400 group-focus-within:text-primary-500 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
<input type="text" id="userName" v-model="userName" @input="validateUserName"
placeholder="e.g. JohnDoe"
class="input-modern block w-full pl-11 pr-4 py-3.5 rounded-xl outline-none" />
</div>
<div>
<label for="versionName" class="block text-lg text-gray-700 dark:text-gray-300">Version:</label>
<p v-if="errors.userName" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{ errors.userName }}
</p>
</div>
<!-- Version -->
<div class="group">
<label for="versionName"
class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-1.5 ml-1">Version</label>
<div class="relative">
<input type="text" id="versionName" v-model="versionName" @input="validateVersion" placeholder="Example 26.0" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.versionName" class="mt-1 text-red-500 text-sm">{{ errors.versionName }}</p>
<div
class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none text-slate-400 group-focus-within:text-primary-500 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path>
</svg>
</div>
<input type="text" id="versionName" v-model="versionName" @input="validateVersion"
placeholder="26.3" class="input-modern block w-full pl-11 pr-4 py-3.5 rounded-xl outline-none" />
</div>
<div>
<label for="userNum" class="block text-lg text-gray-700 dark:text-gray-300">Number of users:</label>
<p v-if="errors.versionName" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{
errors.versionName }}</p>
</div>
<!-- Number of Users -->
<div class="col-span-1 md:col-span-2 group">
<label for="userNum"
class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-1.5 ml-1">Number of
Users</label>
<div class="relative">
<input type="number" id="userNum" v-model="userNum" @input="validateUserNum" placeholder="Example 1" min="1" class="w-full mt-1 p-2 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-slate-800 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition-colors" />
<p v-if="errors.userNum" class="mt-1 text-red-500 text-sm">{{ errors.userNum }}</p>
<div
class="absolute inset-y-0 left-0 flex items-center pl-4 pointer-events-none text-slate-400 group-focus-within:text-primary-500 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z">
</path>
</svg>
</div>
<input type="number" id="userNum" v-model="userNum" @input="validateUserNum" placeholder="1" min="1"
class="input-modern block w-full pl-11 pr-4 py-3.5 rounded-xl outline-none" />
</div>
<!-- Download Button -->
<div class="text-center pt-4">
<button type="submit" class="w-full sm:w-auto px-6 py-3 bg-primary-600 text-white rounded-lg shadow-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors duration-200"> Download Key </button>
<p v-if="errors.userNum" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{ errors.userNum }}</p>
</div>
<!-- Submit Button -->
<div class="col-span-1 md:col-span-2 pt-2">
<button type="submit"
class="btn-gradient w-full py-4 px-6 rounded-xl text-lg font-bold uppercase tracking-wider flex items-center justify-center gap-2 focus:outline-none focus:ring-4 focus:ring-primary-500/50">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
Generate License
</button>
</div>
</div>
</form>
</div>
<!-- Custom Settings Merger Tab -->
<div v-if="activeTab === 'customMerger'" class="space-y-6">
<div class="bg-slate-100 dark:bg-slate-800 p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">How to Use Custom Settings Merger</h3>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-700 dark:text-gray-300">
<li>Generate a license key file using the Key Generator tab</li>
<li>Run MobaXterm with the <code class="bg-slate-200 dark:bg-slate-700 px-1 py-0.5 rounded">-customizer</code> parameter: <br>
<code class="bg-slate-200 dark:bg-slate-700 px-2 py-1 rounded block mt-1">.\MobaXterm.exe -customizer</code>
<div v-show="activeTab === 'customMerger'" class="relative z-10 space-y-8 animate-fade-in-up"
style="animation-duration: 0.4s;">
<!-- Info Box -->
<div
class="bg-primary-50/80 dark:bg-primary-900/10 border border-primary-100 dark:border-primary-800/30 p-5 rounded-2xl">
<div class="flex items-start gap-4">
<div
class="bg-primary-100 dark:bg-primary-800 p-2 rounded-lg text-primary-600 dark:text-primary-300 shrink-0">
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h3 class="text-base font-bold text-primary-900 dark:text-primary-100 mb-1">How it works</h3>
<ul
class="text-sm text-primary-700 dark:text-primary-300 space-y-1.5 ml-4 list-decimal marker:text-primary-500 font-medium">
<li>Generate a license using the <span class="font-bold">Key Generator</span>.</li>
<li>Run <code
class="bg-white/60 dark:bg-black/40 px-1.5 py-0.5 rounded font-mono text-xs">.\MobaXterm.exe -customizer</code>
</li>
<li>Export your custom settings to a file named <code class="bg-slate-200 dark:bg-slate-700 px-1 py-0.5 rounded">MobaXterm customization.custom</code></li>
<li>Upload both your license key file and custom settings file below</li>
<li>Click "Merge Files" to create a combined activation file</li>
</ol>
<li>Export settings to <code
class="bg-white/60 dark:bg-black/40 px-1.5 py-0.5 rounded font-mono text-xs">MobaXterm customization.custom</code>
</li>
<li>Upload both files below to merge.</li>
</ul>
</div>
</div>
</div>
<div class="space-y-6">
<!-- License File Upload -->
<div>
<label for="licenseFileInput" class="block text-lg font-medium text-gray-700 dark:text-gray-300 mb-2">
License Key File (Custom.mxtpro):
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2 ml-1">
License (.mxtpro)
</label>
<div class="relative">
<input
type="file"
id="licenseFileInput"
@change="handleLicenseFileUpload"
accept=".mxtpro"
class="block w-full text-sm text-gray-700 dark:text-gray-300
file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-primary-50 file:text-primary-700
hover:file:bg-primary-100 hover:file:text-primary-800
focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-200
transition-colors duration-200
dark:file:bg-slate-700 dark:file:text-slate-100
dark:hover:file:bg-slate-600 dark:hover:file:text-white"
/>
<p v-if="errors.licenseFile" class="mt-1 text-red-500 text-sm">{{ errors.licenseFile }}</p>
<label for="licenseFileInput"
class="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-2xl cursor-pointer bg-white/30 dark:bg-slate-800/30 hover:bg-white/60 dark:hover:bg-slate-800/60 transition-colors group">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg class="w-8 h-8 mb-3 text-slate-400 group-hover:text-primary-500 transition-colors" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12">
</path>
</svg>
<p class="mb-1 text-sm text-slate-500 dark:text-slate-400 font-semibold truncate px-4 max-w-full">
<span class="text-primary-600 dark:text-primary-400">{{ licenseFile ? licenseFile.name : 'Click to upload' }}</span></p>
<p v-if="!licenseFile" class="text-xs text-slate-400 dark:text-slate-500">MAX size 10MB</p>
</div>
<input type="file" id="licenseFileInput" @change="handleLicenseFileUpload" accept=".mxtpro"
class="hidden" />
</label>
<p v-if="errors.licenseFile" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{ errors.licenseFile
}}</p>
</div>
<!-- Custom File Upload -->
<div>
<label for="customSettingsFileInput" class="block text-lg text-gray-700 dark:text-gray-300">Custom Settings File (.custom):</label>
<input type="file" id="customSettingsFileInput" @change="handleCustomSettingsFileUpload" accept=".custom" class="block w-full text-sm text-gray-700 dark:text-gray-300
file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-primary-50 file:text-primary-700
hover:file:bg-primary-100 hover:file:text-primary-800
focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-200
transition-colors duration-200
dark:file:bg-slate-700 dark:file:text-slate-100
dark:hover:file:bg-slate-600 dark:hover:file:text-white" />
<p v-if="errors.customSettingsFile" class="mt-1 text-red-500 text-sm">{{ errors.customSettingsFile }}</p>
<label class="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2 ml-1">
Custom Settings (.custom)
</label>
<label for="customSettingsFileInput"
class="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-2xl cursor-pointer bg-white/30 dark:bg-slate-800/30 hover:bg-white/60 dark:hover:bg-slate-800/60 transition-colors group">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg class="w-8 h-8 mb-3 text-slate-400 group-hover:text-primary-500 transition-colors" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
</path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
<p class="mb-1 text-sm text-slate-500 dark:text-slate-400 font-semibold truncate px-4 max-w-full">
<span class="text-primary-600 dark:text-primary-400">{{ customSettingsFile ? customSettingsFile.name : 'Click to upload' }}</span></p>
<p v-if="!customSettingsFile" class="text-xs text-slate-400 dark:text-slate-500">MAX size 10MB</p>
</div>
<input type="file" id="customSettingsFileInput" @change="handleCustomSettingsFileUpload"
accept=".custom" class="hidden" />
</label>
<p v-if="errors.customSettingsFile" class="mt-1.5 ml-1 text-red-500 text-xs font-medium">{{
errors.customSettingsFile }}</p>
</div>
</div>
<div class="text-center pt-4">
<button @click="mergeFiles" :disabled="!licenseFile || !customSettingsFile" class="w-full sm:w-auto px-6 py-3 bg-primary-600 text-white rounded-lg shadow-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-colors duration-200 disabled:bg-gray-400 disabled:cursor-not-allowed"> Merge Files </button>
<div class="pt-2">
<button @click="mergeFiles" :disabled="!licenseFile || !customSettingsFile"
class="btn-gradient w-full py-4 px-6 rounded-xl text-lg font-bold uppercase tracking-wider flex items-center justify-center gap-2 focus:outline-none focus:ring-4 focus:ring-primary-500/50 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0 disabled:hover:shadow-none">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"></path>
</svg>
Merge Files
</button>
</div>
</div>
<!-- Footer -->
<footer class="mt-8 pt-6 border-t border-slate-200 dark:border-slate-700">
<div class="flex flex-col items-center space-y-4">
<div class="flex items-center space-x-2">
<span class="text-sm text-slate-600 dark:text-slate-400">Created with</span>
<footer class="mt-14 relative z-10">
<div class="flex flex-col items-center space-y-3">
<div
class="flex items-center space-x-2 bg-white/40 dark:bg-slate-800/40 px-4 py-1.5 rounded-full border border-slate-200 dark:border-slate-700 backdrop-blur-sm">
<span class="text-xs font-medium text-slate-600 dark:text-slate-400">Handcrafted with</span>
<span class="animate-pulse text-red-500"></span>
<span class="text-sm text-slate-600 dark:text-slate-400">by</span>
<span class="text-sm font-semibold bg-clip-text text-transparent bg-gradient-to-r from-purple-500 to-pink-500"> Zarfala </span>
<span class="text-xs font-medium text-slate-600 dark:text-slate-400">by</span>
<span
class="text-xs font-extrabold bg-clip-text text-transparent bg-gradient-to-r from-purple-500 to-pink-500">
Zarfala </span>
</div>
<div class="text-[11px] font-medium text-slate-500 tracking-wide">
v3.0 © 2026 All rights reserved
</div>
<p class="text-sm text-slate-600 dark:text-slate-400" style="margin-top: 0;"> UI/UX Design optimized for <span class="inline-block">
<span class="text-blue-500 font-semibold">Computer</span>
<span class="text-green-500 font-semibold">& Mobile</span>
</span>
</p>
<div class="text-xs text-slate-500 dark:text-slate-500" style="margin-top: 5px;"> v2.6 © 2025 All rights reserved </div>
</div>
</footer>
</div>
</div>
</div>
<!-- Toast notification -->
<div id="toast" class="fixed bottom-4 right-4 px-4 py-2 rounded-lg bg-green-500 text-white transform translate-y-16 opacity-0 transition-all duration-300 z-50 hidden">
License generated successfully!
<div id="toast"
class="fixed bottom-6 right-6 px-6 py-4 rounded-xl shadow-2xl transform translate-y-24 opacity-0 transition-all duration-400 cubic-bezier(0.16, 1, 0.3, 1) z-50 hidden flex items-center gap-3 backdrop-blur-md font-semibold text-sm">
<svg id="toast-icon-success" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
<svg id="toast-icon-error" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span id="toast-text"></span>
</div>
</main>
<!-- Scripts -->
<script src="./js/vue.min-2.6.12.js"></script>
<script src="./js/FileSaver.js"></script>
<script src="./js/jszip.js"></script>
<script src="./js/fire.js"></script>
<script src="./js/alert.js"></script>
<script>
// Preloader functionality
window.addEventListener('load', function () {
// Hide preloader
setTimeout(function () {
const preloader = document.getElementById('preloader');
const mainContent = document.getElementById('main-content');
@@ -279,14 +645,19 @@
mainContent.classList.remove('content-hidden');
mainContent.classList.add('content-visible');
// Remove preloader from DOM after transition completes
setTimeout(function () {
preloader.style.display = 'none';
}, 500);
}, 300);
}, 600);
}, 400); // slight artificial delay for premium feel
});
// Theme toggle functionality
function updateThemeIcons() {
const isDark = document.documentElement.classList.contains('dark');
document.getElementById('theme-icon-dark').classList.toggle('hidden', isDark);
document.getElementById('theme-icon-light').classList.toggle('hidden', !isDark);
}
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
@@ -295,49 +666,55 @@
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
updateThemeIcons();
}
// Check for saved theme preference or prefer-color-scheme
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// Theme is initialized in the <head> to prevent loader flash
updateThemeIcons();
// Toast notification function
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
toast.textContent = message;
const text = document.getElementById('toast-text');
const iconSuccess = document.getElementById('toast-icon-success');
const iconError = document.getElementById('toast-icon-error');
text.textContent = message;
toast.classList.remove('hidden');
if (type === 'success') {
toast.classList.add('bg-green-500');
toast.classList.remove('bg-red-500');
toast.className = toast.className.replace(/bg-\w+-50\/\d+/g, '');
toast.className = toast.className.replace(/text-\w+-600/g, '');
toast.classList.add('bg-green-50/90', 'dark:bg-green-900/90', 'text-green-700', 'dark:text-green-100', 'border', 'border-green-200', 'dark:border-green-800');
toast.classList.remove('bg-red-50/90', 'dark:bg-red-900/90', 'text-red-700', 'dark:text-red-100', 'border-red-200', 'dark:border-red-800');
iconSuccess.classList.remove('hidden');
iconError.classList.add('hidden');
} else {
toast.classList.add('bg-red-500');
toast.classList.remove('bg-green-500');
toast.className = toast.className.replace(/bg-\w+-50\/\d+/g, '');
toast.className = toast.className.replace(/text-\w+-600/g, '');
toast.classList.add('bg-red-50/90', 'dark:bg-red-900/90', 'text-red-700', 'dark:text-red-100', 'border', 'border-red-200', 'dark:border-red-800');
toast.classList.remove('bg-green-50/90', 'dark:bg-green-900/90', 'text-green-700', 'dark:text-green-100', 'border-green-200', 'dark:border-green-800');
iconError.classList.remove('hidden');
iconSuccess.classList.add('hidden');
}
setTimeout(() => {
toast.classList.remove('translate-y-16', 'opacity-0');
}, 100);
// Trigger animation
requestAnimationFrame(() => {
toast.classList.remove('translate-y-24', 'opacity-0');
});
// Hide after delay
setTimeout(() => {
toast.classList.add('translate-y-16', 'opacity-0');
toast.classList.add('translate-y-24', 'opacity-0');
setTimeout(() => {
toast.classList.add('hidden');
}, 300);
}, 3000);
}, 400);
}, 4000);
}
</script>
<script type="module">
import {
LicenseType,
generateLicense
} from './js/mobaXtermGenerater.js'
import {
mergeZipFiles
} from './js/mergeZipFiles.js'
import { LicenseType, generateLicense } from './js/mobaXtermGenerater.js'
import { mergeZipFiles } from './js/mergeZipFiles.js'
new Vue({
el: '#app',
@@ -346,7 +723,7 @@
LICENSE_TYPES: LicenseType,
licenseType: '',
userName: '',
versionName: '',
versionName: '26.3',
userNum: '',
activeTab: 'keyGenerator',
licenseFile: null,
@@ -375,10 +752,9 @@
this.errors.userName = 'Username is required';
return false;
}
const regex = /^[a-zA-Z]+$/;
if (!regex.test(this.userName)) {
this.errors.userName = 'Only letters are allowed (no spaces, numbers or special characters)';
this.errors.userName = 'Only letters allowed (no spaces, numbers or special chars)';
return false;
}
this.errors.userName = '';
@@ -389,20 +765,17 @@
this.errors.versionName = 'Version is required';
return false;
}
const regex = /^[1-9][0-9]*\.?\d{0,1}$/;
if (!regex.test(this.versionName)) {
this.errors.versionName = 'Enter a valid version format (e.g., 26.0)';
this.errors.versionName = 'Enter a valid version format (e.g., 26.3)';
return false;
}
// Check if version is supported
const majorVersion = parseInt(this.versionName.split('.')[0]);
if (majorVersion !== 20 && majorVersion !== 25 && majorVersion !== 26) {
this.errors.versionName = 'Currently only versions 20.X, 25.X and 26 are supported';
this.errors.versionName = 'Currently only versions 20.X, 25.X and 26.X are supported';
return false;
}
this.errors.versionName = '';
return true;
},
@@ -411,7 +784,6 @@
this.errors.userNum = 'Number of users is required';
return false;
}
const regex = /^[1-9]+\d*$/;
if (!regex.test(this.userNum)) {
this.errors.userNum = 'Enter a valid number greater than 0';
@@ -421,13 +793,11 @@
return true;
},
validateAndGenerate() {
// Validate all fields
const isLicenseTypeValid = this.validateLicenseType();
const isUserNameValid = this.validateUserName();
const isVersionValid = this.validateVersion();
const isUserNumValid = this.validateUserNum();
// Only generate if all validations pass
if (isLicenseTypeValid && isUserNameValid && isVersionValid && isUserNumValid) {
try {
let versionNameArr = this.versionName.split('.');
@@ -446,11 +816,9 @@
generateLicenseFile(licenseStr) {
let zip = new JSZip();
zip.file("Pro.key", licenseStr);
zip.generateAsync({
type: "blob"
}).then(function(content) {
zip.generateAsync({ type: "blob" }).then((content) => {
saveAs(content, "Custom.mxtpro");
}).catch(function(error) {
}).catch((error) => {
console.error('Error creating zip file:', error);
showToast('Error creating license file. Please try again.', 'error');
});
@@ -484,7 +852,6 @@
showToast('Please upload both required files', 'error');
return;
}
mergeZipFiles(this.licenseFile, this.customSettingsFile)
.then((mergedContent) => {
saveAs(mergedContent, "Custom.mxtpro");
@@ -495,12 +862,9 @@
showToast('Error merging files. Please try again.', 'error');
});
}
},
mounted() {
// Set default version to 26.0
this.versionName = '26.0';
}
});
</script>
</body>
</html>
+55 -25
View File
@@ -1,16 +1,4 @@
var estiloConsola = `
font-size: 16px;
font-family: "Courier New", monospace;
color: red;
white-space: pre;
`;
var estiloZarfala = `
color: red;
white-space: pre;
`;
var mensajeZarfala = `
const asciiArt = `
@@@@@@@@ @@@@@@ @@@@@@@ @@@@@@@@ @@@@@@ @@@ @@@@@@
@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@@@@ @@@ @@@@@@@@
@@! @@! @@@ @@! @@@ @@! @@! @@@ @@! @@! @@@
@@ -23,18 +11,60 @@ var mensajeZarfala = `
: :: : : : : : : : : : : : : : :: : : : : :
`;
function mostrarMensaje() {
const asciiAuraStyle = `
font-family: "Courier New", monospace;
font-weight: 900;
background: linear-gradient(90deg, #60a5fa, #ec4899, #8b5cf6);
-webkit-background-clip: text;
color: transparent;
display: block;
white-space: pre;
`;
const warningHeadingStyle = `
color: #ff0055;
font-size: 44px;
font-weight: 900;
font-family: 'Outfit', sans-serif, system-ui;
text-shadow: 0px 4px 15px rgba(255, 0, 85, 0.4);
padding: 10px 0;
`;
const warningBodyStyle = `
font-size: 16px;
color: #d1d5db;
font-weight: 500;
line-height: 1.6;
font-family: system-ui, -apple-system, sans-serif;
padding: 10px 0 20px 0;
`;
const signatureStyle = `
font-size: 14px;
font-weight: bold;
background: linear-gradient(90deg, #10b981, #3b82f6);
-webkit-background-clip: text;
color: transparent;
font-family: 'Outfit', sans-serif;
`;
function renderSecureAura() {
// Clear any existing logs to present a clean canvas
console.clear();
console.log('%c' + mensajeZarfala, estiloZarfala);
console.log("%cSTOP!", "color:red;font-size:xx-large;font-weight:bold;"),
console.log(`%cThis is a browser feature intended for developers. Do not enter or paste code which you don't understand. It may allow attackers to steal your information or impersonate you. See https://en.wikipedia.org/wiki/Self-XSS for more details`, "font-size:large;"),
localStorage.do_NOT_share_jwt || localStorage.setItem("do_NOT_share_jwt", "do NOT share the JWT value. someone could steal your account");
// Print Aura-style ASCII Art
console.log('%c' + asciiArt, asciiAuraStyle);
// Print Warning Message
console.log("%cSTOP! SECURE ZONE", warningHeadingStyle);
console.log(
"%cThis is a browser feature intended strictly for developers.\nDo not enter or paste any code you do not fully understand.\nDoing so could grant attackers access to hijack your session, steal your licenses, or impersonate you.\n\nProtect your data and stay safe! 🔒",
warningBodyStyle
);
console.log("%c✨ MobaXterm Keygen System - Shield Active", signatureStyle);
}
var intermitente = true;
setInterval(function () {
mostrarMensaje();
intermitente = !intermitente;
}, 1000);
mostrarMensaje();
// Render the protective aura once
renderSecureAura();