Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06be8925e8 | ||
|
|
d0dc547680 | ||
|
|
6f610a1cc6 |
51
assets/css/custom.css
Normal file
51
assets/css/custom.css
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
body {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
color: #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container, .output-container {
|
||||||
|
background-color: #252526;
|
||||||
|
border: 1px solid #333333;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #d4d4d4;
|
||||||
|
font-family: monospace;
|
||||||
|
resize: none;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#output {
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #d4d4d4;
|
||||||
|
font-family: monospace;
|
||||||
|
padding: 1rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #007acc;
|
||||||
|
border-color: #007acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
.split-container {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container, .output-container {
|
||||||
|
height: 50vh; /* Adjust as needed */
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
311
assets/js/main.js
Normal file
311
assets/js/main.js
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const editor = ace.edit("editor");
|
||||||
|
editor.session.setMode("ace/mode/lua");
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
const obfuscateBtn = document.getElementById('obfuscate-btn');
|
||||||
|
const saveBtn = document.getElementById('save-btn');
|
||||||
|
const loadBtn = document.getElementById('load-btn');
|
||||||
|
const clearOutputBtn = document.getElementById('clear-output-btn');
|
||||||
|
const runBtn = document.getElementById('run-btn');
|
||||||
|
const runOutput = document.getElementById('run-output');
|
||||||
|
const obfuscatedOutput = document.getElementById('obfuscated-output');
|
||||||
|
const loader = document.getElementById('loader');
|
||||||
|
const versionSpan = document.getElementById('luau-version');
|
||||||
|
const examplesMenu = document.getElementById('examples-menu');
|
||||||
|
const saveStatus = document.getElementById('save-status');
|
||||||
|
|
||||||
|
const LS_CODE_KEY = 'luau-saved-code';
|
||||||
|
const LS_SETTINGS_KEY = 'luau-editor-settings';
|
||||||
|
|
||||||
|
const editorThemes = [
|
||||||
|
'ambiance', 'chaos', 'chrome', 'clouds', 'cobalt',
|
||||||
|
'crimson_editor', 'dawn', 'dracula', 'dreamweaver', 'eclipse',
|
||||||
|
'github', 'gob', 'gruvbox', 'idle_fingers', 'iplastic',
|
||||||
|
'katzenmilch', 'kr_theme', 'kuroir', 'merbivore', 'merbivore_soft',
|
||||||
|
'mono_industrial', 'monokai', 'nord_dark', 'pastel_on_dark', 'solarized_dark',
|
||||||
|
'solarized_light', 'sqlserver', 'terminal', 'textmate', 'tomorrow',
|
||||||
|
'tomorrow_night', 'tomorrow_night_blue', 'tomorrow_night_bright', 'tomorrow_night_eighties',
|
||||||
|
'twilight', 'vibrant_ink', 'xcode'
|
||||||
|
];
|
||||||
|
|
||||||
|
let editorSettings = {};
|
||||||
|
|
||||||
|
// --- Editor and Snippet Setup ---
|
||||||
|
const examples = [
|
||||||
|
{
|
||||||
|
title: 'Hello World',
|
||||||
|
code: 'print("Hello, world!")'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Variables & Types',
|
||||||
|
code: '-- Luau has types that you can optionally add!\n\n' +
|
||||||
|
'local message: string = "Hello, Luau!"\n' +
|
||||||
|
'local version: number = 1.0\n' +
|
||||||
|
'local isAwesome: boolean = true\n\n' +
|
||||||
|
'print(message, type(message))\n' +
|
||||||
|
'print("Version:", version, type(version))\n' +
|
||||||
|
'print("Is it awesome?", isAwesome, type(isAwesome))'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Loops',
|
||||||
|
code: 'print("Counting from 1 to 5:")\n' +
|
||||||
|
'for i = 1, 5 do\n' +
|
||||||
|
' print("Number:", i)\n' +
|
||||||
|
'end\n\n' +
|
||||||
|
'local t = {"apple", "banana", "cherry"}\n' +
|
||||||
|
'print("\nIterating through a table:")\n' +
|
||||||
|
'for _, fruit in ipairs(t) do\n' +
|
||||||
|
' print("- " .. fruit)\n' +
|
||||||
|
'end'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Functions',
|
||||||
|
code: 'function greet(name: string): string\n' +
|
||||||
|
' return "Hello, " .. name .. "!"\n' +
|
||||||
|
'end\n\n' +
|
||||||
|
'local message = greet("User")\n' +
|
||||||
|
'print(message)\n\n' +
|
||||||
|
'-- Function with a type alias\n' +
|
||||||
|
'type Player = { name: string, score: number }\n\n' +
|
||||||
|
'local function displayPlayer(p: Player)\n' +
|
||||||
|
' print("Player: " .. p.name .. ", Score: " .. p.score)\n' +
|
||||||
|
'end\n\n' +
|
||||||
|
'local player1: Player = { name = "Alice", score = 100 }\n' +
|
||||||
|
'displayPlayer(player1)'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
examples.forEach((example, index) => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.classList.add('dropdown-item');
|
||||||
|
a.href = '#';
|
||||||
|
a.textContent = example.title;
|
||||||
|
a.dataset.index = index;
|
||||||
|
li.appendChild(a);
|
||||||
|
examplesMenu.appendChild(li);
|
||||||
|
});
|
||||||
|
|
||||||
|
examplesMenu.addEventListener('click', (e) => {
|
||||||
|
if (e.target.tagName === 'A') {
|
||||||
|
e.preventDefault();
|
||||||
|
const index = e.target.dataset.index;
|
||||||
|
if (index) {
|
||||||
|
editor.setValue(examples[index].code, 1); // 1 moves cursor to the end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Settings Modal Logic ---
|
||||||
|
const themeSelect = document.getElementById('theme-select');
|
||||||
|
const fontSizeInput = document.getElementById('font-size-input');
|
||||||
|
const softWrapCheck = document.getElementById('soft-wrap-check');
|
||||||
|
|
||||||
|
function applySettings() {
|
||||||
|
editor.setTheme("ace/theme/" + editorSettings.theme);
|
||||||
|
editor.setFontSize(editorSettings.fontSize);
|
||||||
|
editor.session.setUseWrapMode(editorSettings.softWrap);
|
||||||
|
|
||||||
|
// Update UI controls to reflect current settings
|
||||||
|
themeSelect.value = editorSettings.theme;
|
||||||
|
fontSizeInput.value = editorSettings.fontSize;
|
||||||
|
softWrapCheck.checked = editorSettings.softWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSettings() {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LS_SETTINGS_KEY, JSON.stringify(editorSettings));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to save settings:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSettings() {
|
||||||
|
const defaults = {
|
||||||
|
theme: 'tomorrow_night',
|
||||||
|
fontSize: 14,
|
||||||
|
softWrap: false
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(LS_SETTINGS_KEY);
|
||||||
|
if (saved) {
|
||||||
|
editorSettings = Object.assign({}, defaults, JSON.parse(saved));
|
||||||
|
} else {
|
||||||
|
editorSettings = defaults;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to load settings:", e);
|
||||||
|
editorSettings = defaults;
|
||||||
|
}
|
||||||
|
applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate theme dropdown
|
||||||
|
editorThemes.forEach(theme => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = theme;
|
||||||
|
option.textContent = theme.split('_').map(w => w[0].toUpperCase() + w.substr(1)).join(' ');
|
||||||
|
themeSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Settings event listeners
|
||||||
|
themeSelect.addEventListener('change', () => {
|
||||||
|
editorSettings.theme = themeSelect.value;
|
||||||
|
applySettings();
|
||||||
|
saveSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
fontSizeInput.addEventListener('input', () => {
|
||||||
|
editorSettings.fontSize = parseInt(fontSizeInput.value, 10);
|
||||||
|
applySettings();
|
||||||
|
saveSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
softWrapCheck.addEventListener('change', () => {
|
||||||
|
editorSettings.softWrap = softWrapCheck.checked;
|
||||||
|
applySettings();
|
||||||
|
saveSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// --- Local Storage and UI Logic ---
|
||||||
|
|
||||||
|
function showSaveStatus(message, duration = 2000) {
|
||||||
|
saveStatus.textContent = message;
|
||||||
|
setTimeout(() => {
|
||||||
|
saveStatus.textContent = '';
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveBtn.addEventListener('click', () => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LS_CODE_KEY, editor.getValue());
|
||||||
|
showSaveStatus('Code saved!');
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to save to localStorage:", e);
|
||||||
|
showSaveStatus('Error saving!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadBtn.addEventListener('click', () => {
|
||||||
|
const savedCode = localStorage.getItem(LS_CODE_KEY);
|
||||||
|
if (savedCode) {
|
||||||
|
editor.setValue(savedCode, 1);
|
||||||
|
showSaveStatus('Code restored!');
|
||||||
|
} else {
|
||||||
|
showSaveStatus('No code saved yet.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
clearOutputBtn.addEventListener('click', () => {
|
||||||
|
obfuscatedOutput.textContent = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Load initial code ---
|
||||||
|
const savedCode = localStorage.getItem(LS_CODE_KEY);
|
||||||
|
if (savedCode) {
|
||||||
|
editor.setValue(savedCode, 1);
|
||||||
|
} else {
|
||||||
|
// Load default example if nothing is saved
|
||||||
|
editor.setValue(examples[0].code, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
versionSpan.textContent = `Luau Obfuscator`;
|
||||||
|
|
||||||
|
obfuscateBtn.addEventListener('click', async () => {
|
||||||
|
const code = editor.getValue();
|
||||||
|
obfuscatedOutput.textContent = '';
|
||||||
|
loader.style.display = 'block';
|
||||||
|
obfuscateBtn.disabled = true;
|
||||||
|
|
||||||
|
// Give the UI a moment to update
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('code', code);
|
||||||
|
|
||||||
|
const response = await fetch('obfuscator.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.text();
|
||||||
|
obfuscatedOutput.textContent = result;
|
||||||
|
} else {
|
||||||
|
obfuscatedOutput.textContent = `Error: ${response.statusText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
obfuscatedOutput.textContent = `Error: ${err.message}`;
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
loader.style.display = 'none';
|
||||||
|
obfuscateBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Split View ---
|
||||||
|
let split;
|
||||||
|
|
||||||
|
function setupSplit() {
|
||||||
|
if (window.innerWidth < 992) {
|
||||||
|
if (split) {
|
||||||
|
split.destroy();
|
||||||
|
split = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!split) {
|
||||||
|
split = Split(['#split-0', '#split-1'], {
|
||||||
|
sizes: [50, 50],
|
||||||
|
minSize: 200,
|
||||||
|
gutterSize: 8,
|
||||||
|
onDrag: function() {
|
||||||
|
editor.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', setupSplit);
|
||||||
|
setupSplit(); // Initial call
|
||||||
|
|
||||||
|
runBtn.addEventListener('click', async () => {
|
||||||
|
const code = editor.getValue();
|
||||||
|
runOutput.textContent = '';
|
||||||
|
loader.style.display = 'block';
|
||||||
|
runBtn.disabled = true;
|
||||||
|
|
||||||
|
// Give the UI a moment to update
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('code', code);
|
||||||
|
|
||||||
|
const response = await fetch('run.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const result = await response.text();
|
||||||
|
runOutput.textContent = result;
|
||||||
|
} else {
|
||||||
|
runOutput.textContent = `Error: ${response.statusText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
runOutput.textContent = `Error: ${err.message}`;
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
loader.style.display = 'none';
|
||||||
|
runBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
8
db/migrations/001_create_snippets_table.sql
Normal file
8
db/migrations/001_create_snippets_table.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- Migration to create the snippets table for storing shared code.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `snippets` (
|
||||||
|
`id` varchar(16) NOT NULL,
|
||||||
|
`code` text NOT NULL,
|
||||||
|
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
272
index.php
272
index.php
@ -1,150 +1,154 @@
|
|||||||
<?php
|
<!DOCTYPE html>
|
||||||
declare(strict_types=1);
|
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
<title>Luau Obfuscator</title>
|
||||||
<?php
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
// Read project preview data from environment
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<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=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
#loader {
|
||||||
--bg-color-start: #6a11cb;
|
display: none;
|
||||||
--bg-color-end: #2575fc;
|
position: absolute;
|
||||||
--text-color: #ffffff;
|
top: 50%;
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
left: 50%;
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
body {
|
.split-container {
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: row;
|
||||||
align-items: center;
|
height: calc(100vh - 56px - 1rem); /* Full height minus header and margin */
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
body::before {
|
.gutter {
|
||||||
content: '';
|
background-color: #343a40;
|
||||||
position: absolute;
|
background-repeat: no-repeat;
|
||||||
top: 0;
|
background-position: 50%;
|
||||||
left: 0;
|
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 30'%3E%3Cpath d='M2 0 v30 M5 0 v30 M8 0 v30' stroke-width='1' stroke='#888'/%3E%3C/svg%3E");
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
}
|
||||||
@keyframes bg-pan {
|
.gutter.gutter-horizontal {
|
||||||
0% { background-position: 0% 0%; }
|
cursor: col-resize;
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<header class="navbar navbar-dark bg-dark">
|
||||||
<div class="card">
|
<div class="container-fluid">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<a class="navbar-brand" href="#">Luau Obfuscator</a>
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<div class="ms-auto d-flex align-items-center">
|
||||||
<span class="sr-only">Loading…</span>
|
<span id="luau-version" class="navbar-text me-3 d-none d-lg-inline">Loading Luau...</span>
|
||||||
|
<div class="btn-group me-2" role="group">
|
||||||
|
<button class="btn btn-primary" id="obfuscate-btn">Obfuscate</button>
|
||||||
|
<button class="btn btn-success" id="run-btn">Run</button>
|
||||||
|
</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item d-lg-none"><span id="luau-version-mobile" class="navbar-text">Loading Luau...</span></li>
|
||||||
|
<li class="nav-item"><span id="save-status" class="navbar-text"></span></li>
|
||||||
|
<li class="nav-item btn-group" role="group">
|
||||||
|
<button class="btn btn-outline-secondary" id="save-btn">Save</button>
|
||||||
|
<button class="btn btn-outline-secondary" id="load-btn">Restore</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="examples-dropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Load Example
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="examples-dropdown" id="examples-menu">
|
||||||
|
<!-- Examples will be populated by JS -->
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="btn btn-outline-light" id="clear-output-btn">Clear Obfuscated</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="btn btn-info ms-2" id="share-btn" data-bs-toggle="modal" data-bs-target="#share-modal">
|
||||||
|
<i class="bi bi-share-fill"></i> Share
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="btn btn-outline-light ms-2" id="settings-btn" data-bs-toggle="modal" data-bs-target="#settings-modal">
|
||||||
|
<i class="bi bi-gear-fill"></i>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container-fluid mt-3">
|
||||||
|
<div class="split-container">
|
||||||
|
<div id="split-0" class="editor-container">
|
||||||
|
<div id="editor">print("Hello, from Luau!\n")
|
||||||
|
print("Current time: " .. os.date())</div>
|
||||||
|
</div>
|
||||||
|
<div id="split-1" class="output-container">
|
||||||
|
<div id="loader" class="spinner-border text-light" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
<pre id="obfuscated-output"></pre>
|
||||||
|
<h5 class="text-light">Run Output</h5>
|
||||||
|
<pre id="run-output"></pre>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<!-- Settings Modal -->
|
||||||
</footer>
|
<div class="modal fade" id="settings-modal" tabindex="-1" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content bg-dark text-light">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="settingsModalLabel">Editor Settings</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="theme-select" class="form-label">Theme</label>
|
||||||
|
<select class="form-select" id="theme-select"></select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="font-size-input" class="form-label">Font Size</label>
|
||||||
|
<input type="number" class="form-control" id="font-size-input" min="8" max="32">
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="soft-wrap-check">
|
||||||
|
<label class="form-check-label" for="soft-wrap-check">
|
||||||
|
Enable Soft Wrap
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Share Modal -->
|
||||||
|
<div class="modal fade" id="share-modal" tabindex="-1" aria-labelledby="shareModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content bg-dark text-light">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="shareModalLabel">Share Link</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Anyone with this link can view your code.</p>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" id="share-url-input" readonly>
|
||||||
|
<button class="btn btn-outline-secondary" type="button" id="copy-link-btn">Copy</button>
|
||||||
|
</div>
|
||||||
|
<div id="copy-status" class="form-text text-success mt-2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/ace-builds@1.32.7/src-min-noconflict/ace.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/luau-web@1.2.3/dist/luau-web.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/split.js/1.6.2/split.min.js"></script>
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
26
obfuscator.php
Normal file
26
obfuscator.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
// A simple Luau obfuscator
|
||||||
|
// For now, it removes comments and extra whitespace.
|
||||||
|
|
||||||
|
if (isset($_POST['code'])) {
|
||||||
|
$code = $_POST['code'];
|
||||||
|
|
||||||
|
// Remove single-line comments
|
||||||
|
$code = preg_replace('/--.*/', '', $code);
|
||||||
|
|
||||||
|
// Remove multi-line comments (basic version, may not handle all edge cases)
|
||||||
|
$code = preg_replace('/--\[\[.*?\]\]/s', '', $code);
|
||||||
|
|
||||||
|
// Normalize whitespace: replace multiple spaces/tabs/newlines with a single space
|
||||||
|
$code = preg_replace('/\s+/', ' ', $code);
|
||||||
|
|
||||||
|
// Trim leading/trailing whitespace
|
||||||
|
$code = trim($code);
|
||||||
|
|
||||||
|
echo htmlspecialchars($code, ENT_QUOTES, 'UTF-8');
|
||||||
|
} else {
|
||||||
|
// Handle cases where 'code' is not set
|
||||||
|
http_response_code(400); // Bad Request
|
||||||
|
echo 'Error: No code provided.';
|
||||||
|
}
|
||||||
|
?>
|
||||||
15
run.php
Executable file
15
run.php
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
if (isset($_POST['code'])) {
|
||||||
|
$code = $_POST['code'];
|
||||||
|
$tmp_dir = '/home/ubuntu/.gemini/tmp/ec2c6b8cb2ac3d6ef62b888a61e62d5ab1e71e46b3c4963f2a040a3c33c7038b/';
|
||||||
|
$file = tempnam($tmp_dir, 'luau_');
|
||||||
|
file_put_contents($file, $code);
|
||||||
|
|
||||||
|
$output = shell_exec("luau " . escapeshellarg($file) . " 2>&1");
|
||||||
|
unlink($file);
|
||||||
|
|
||||||
|
echo $output;
|
||||||
|
} else {
|
||||||
|
echo "No code provided.";
|
||||||
|
}
|
||||||
|
?>
|
||||||
72
share.php
Normal file
72
share.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random, URL-friendly string of a given length.
|
||||||
|
*/
|
||||||
|
function generate_random_id($length = 8) {
|
||||||
|
return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', $length)), 0, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
if ($method === 'POST') {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$code = $input['code'] ?? null;
|
||||||
|
|
||||||
|
if (!$code) {
|
||||||
|
http_response_code(400); // Bad Request
|
||||||
|
echo json_encode(['error' => 'Code content is missing.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop to ensure the generated ID is unique
|
||||||
|
do {
|
||||||
|
$id = generate_random_id();
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM snippets WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
} while ($stmt->fetchColumn());
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO snippets (id, code) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$id, $code]);
|
||||||
|
|
||||||
|
http_response_code(201); // Created
|
||||||
|
echo json_encode(['id' => $id]);
|
||||||
|
|
||||||
|
} elseif ($method === 'GET') {
|
||||||
|
$id = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
http_response_code(400); // Bad Request
|
||||||
|
echo json_encode(['error' => 'Snippet ID is missing.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("SELECT code, created_at FROM snippets WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$snippet = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($snippet) {
|
||||||
|
echo json_encode($snippet);
|
||||||
|
} else {
|
||||||
|
http_response_code(404); // Not Found
|
||||||
|
echo json_encode(['error' => 'Snippet not found.']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(405); // Method Not Allowed
|
||||||
|
echo json_encode(['error' => 'Method not allowed.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
http_response_code(500); // Internal Server Error
|
||||||
|
error_log("Database error: " . $e->getMessage());
|
||||||
|
echo json_encode(['error' => 'An internal server error occurred.']);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500); // Internal Server Error
|
||||||
|
error_log("General error: " . $e->getMessage());
|
||||||
|
echo json_encode(['error' => 'An internal server error occurred.']);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user