File size: 7,503 Bytes
87b317d 8f9404d 87b317d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
<!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>
<!-- tailwind -->
<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">
<!-- banner -->
<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>
<!-- history console -->
<div id="history" class=" space-y-4 w-full">
<!-- input in right -->
<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>
<!-- output in left -->
<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>
<!-- input -->
<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>
<!-- scroll to top button -->
<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>
<!-- scroll to bottom button -->
<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;
// tambhakan history input dan output
function addHistory(input, output) {
const copyInputElement = document.getElementById("history-input").cloneNode(true);
const copyOutputElement = document.getElementById("history-output").cloneNode(true);
// set input and output content
copyInputElement.querySelector("pre").textContent = input;
copyOutputElement.querySelector("pre").textContent = output;
// add running arttribute to output
copyOutputElement.setAttribute("x-running", "");
// append input and output
document.getElementById("history").appendChild(copyInputElement);
document.getElementById("history").appendChild(copyOutputElement);
}
// ANSI removal
// example: [0;32m
function removeAnsi(text) {
return text.replace(/\[(\d+)(;\d+)*[mK]/g, "");
}
// if on focus input and press UP
command.addEventListener("focus", () => {
window.addEventListener("keydown", (event) => {
// on TAB
if (event.key === "Tab") {
event.preventDefault();
command.value = command.placeholder;
}
})
});
const executeCommand = (event) => {
event.preventDefault();
// disable input
command.disabled = true;
// add history
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) {
// set pwd
document.getElementById("pwd").value = data.pwd;
document.getElementById("pwd_placeholder").textContent = data.pwd;
}
// if done stop listening
if (data.output === "[DONE]") {
eventSource.close();
// enable input
command.disabled = false;
// set command value to placeholder
command.placeholder = command.value;
// remove running arttribute
outputRunningElement.removeAttribute("x-running");
// if output is empty remove element
if (outputRunningElement.querySelector("pre").textContent === "") {
outputRunningElement.remove();
}
// clear input and make command focus
command.value = "";
command.focus();
return;
}
if (data.output) {
// update output
outputRunningElement.querySelector("pre").textContent += removeAnsi(data.output);
// if you scroll down to bottom, scroll to bottom automatically
if (!scrollLock) {
window.scrollTo(0, document.body.scrollHeight);
}
}
}
};
</script>
</body>
</html> |