|
<!DOCTYPE html> |
|
<html lang="en"> |
|
|
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
|
<title>Terminal</title> |
|
|
|
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
</head> |
|
|
|
<body class="bg-gray-900 text-white p-6"> |
|
<div class="flex flex-col items-center justify-center"> |
|
<div class="container space-y-8 md:my-12 max-w-4xl"> |
|
|
|
<div class=""> |
|
<h1 class="text-3xl">@VANO_GANZZZ INTERFACE <span class="font-bold text-gray-400" |
|
style="animation: blinker 1s linear infinite;">_</span></h1> |
|
<style> |
|
@keyframes blinker { |
|
50% { |
|
opacity: 0; |
|
} |
|
} |
|
</style> |
|
</div> |
|
|
|
<div id="history" class=" space-y-4 w-full"> |
|
|
|
<div id="history-input" class="w-full flex justify-end"> |
|
<div class="bg-gray-800 rounded-lg p-4 overflow-y-auto w-fit"> |
|
<pre class="whitespace-pre-line">{{ welcome_input }}</pre> |
|
</div> |
|
</div> |
|
|
|
<div id="history-output" class="w-full flex justify-start"> |
|
<div class="bg-gray-700 rounded-lg p-4 overflow-y-auto w-fit"> |
|
<pre class="whitespace-pre-line">{{ welcome_output }}</pre> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-gray-800 rounded-lg p-4 w-full shadow-lg"> |
|
<form action="/exec" method="GET" onsubmit="return executeCommand(event)"> |
|
<div class="flex items-center space-x-2"> |
|
<span class="text-gray-400 flex-shrink-0" id="pwd_placeholder">{{ pwd }}</span> |
|
<input type="text" id="command" name="command" class="w-full p-2 bg-gray-700 rounded-md" |
|
placeholder="{{ welcome_input }}" autocomplete="off" autofocus> |
|
</div> |
|
<input type="hidden" id="pwd" name="pwd" value="{{ pwd }}"> |
|
</form> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<script> |
|
window.addEventListener("load", function () { |
|
const topButton = document.querySelector(".top-button"); |
|
const bottomButton = document.querySelector(".bottom-button"); |
|
|
|
window.addEventListener("scroll", function () { |
|
if (window.scrollY + window.innerHeight < document.body.scrollHeight - 100) { |
|
topButton.style.display = "block"; |
|
scrollLock = true; |
|
} else { |
|
topButton.style.display = "none"; |
|
scrollLock = false; |
|
} |
|
|
|
if (window.scrollY > 100) { |
|
bottomButton.style.display = "block"; |
|
} else { |
|
bottomButton.style.display = "none"; |
|
} |
|
}); |
|
}); |
|
</script> |
|
|
|
|
|
<div class="fixed bottom-0 right-0 m-8 top-button" style="display: none;"> |
|
<button onclick="window.scrollTo(0, document.body.scrollHeight)" class="text-3xl p-4 text-white">β</button> |
|
</div> |
|
|
|
<div class="fixed top-0 right-0 m-8 bottom-button" style="display: none;"> |
|
<button onclick="window.scrollTo(0, 0)" class="text-3xl p-4 text-white">β</button> |
|
</div> |
|
|
|
<script> |
|
const historyElement = document.getElementById("history"); |
|
const command = document.getElementById("command"); |
|
const pwd = document.getElementById("pwd"); |
|
|
|
let scrollLock = false; |
|
|
|
|
|
function addHistory(input, output) { |
|
const copyInputElement = document.getElementById("history-input").cloneNode(true); |
|
const copyOutputElement = document.getElementById("history-output").cloneNode(true); |
|
|
|
copyInputElement.querySelector("pre").textContent = input; |
|
copyOutputElement.querySelector("pre").textContent = output; |
|
|
|
copyOutputElement.setAttribute("x-running", ""); |
|
|
|
document.getElementById("history").appendChild(copyInputElement); |
|
document.getElementById("history").appendChild(copyOutputElement); |
|
} |
|
|
|
|
|
|
|
function removeAnsi(text) { |
|
return text.replace(/\[(\d+)(;\d+)*[mK]/g, ""); |
|
} |
|
|
|
|
|
command.addEventListener("focus", () => { |
|
window.addEventListener("keydown", (event) => { |
|
|
|
if (event.key === "Tab") { |
|
event.preventDefault(); |
|
command.value = command.placeholder; |
|
} |
|
}) |
|
}); |
|
|
|
const executeCommand = (event) => { |
|
event.preventDefault(); |
|
|
|
command.disabled = true; |
|
|
|
|
|
addHistory(command.value, ""); |
|
outputRunningElement = document.querySelector("div[x-running]"); |
|
|
|
window.scrollTo(0, document.body.scrollHeight); |
|
scrollLock = false; |
|
|
|
const eventSource = new EventSource("/exec?" + new URLSearchParams({ |
|
command: command.value, |
|
pwd: pwd.value |
|
})); |
|
|
|
eventSource.onmessage = function (event) { |
|
const data = JSON.parse(event.data); |
|
if (data.pwd) { |
|
|
|
document.getElementById("pwd").value = data.pwd; |
|
document.getElementById("pwd_placeholder").textContent = data.pwd; |
|
} |
|
|
|
|
|
if (data.output === "[DONE]") { |
|
eventSource.close(); |
|
|
|
command.disabled = false; |
|
|
|
command.placeholder = command.value; |
|
|
|
outputRunningElement.removeAttribute("x-running"); |
|
|
|
if (outputRunningElement.querySelector("pre").textContent === "") { |
|
outputRunningElement.remove(); |
|
} |
|
|
|
|
|
command.value = ""; |
|
command.focus(); |
|
return; |
|
} |
|
|
|
if (data.output) { |
|
|
|
outputRunningElement.querySelector("pre").textContent += removeAnsi(data.output); |
|
|
|
if (!scrollLock) { |
|
window.scrollTo(0, document.body.scrollHeight); |
|
} |
|
} |
|
} |
|
|
|
}; |
|
</script> |
|
</body> |
|
|
|
</html> |