Zhofang commited on
Commit
87b317d
1 Parent(s): c13b1e1

Create templates/terminal.html

Browse files
Files changed (1) hide show
  1. templates/terminal.html +187 -0
templates/terminal.html ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7
+ <title>Terminal</title>
8
+
9
+ <!-- tailwind -->
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ </head>
12
+
13
+ <body class="bg-gray-900 text-white p-6">
14
+ <div class="flex flex-col items-center justify-center">
15
+ <div class="container space-y-8 md:my-12 max-w-4xl">
16
+ <!-- banner -->
17
+ <div class="">
18
+ <h1 class="text-3xl">Terminal <span class="font-bold text-gray-400"
19
+ style="animation: blinker 1s linear infinite;">_</span></h1>
20
+ <style>
21
+ @keyframes blinker {
22
+ 50% {
23
+ opacity: 0;
24
+ }
25
+ }
26
+ </style>
27
+ </div>
28
+ <!-- history console -->
29
+ <div id="history" class=" space-y-4 w-full">
30
+ <!-- input in right -->
31
+ <div id="history-input" class="w-full flex justify-end">
32
+ <div class="bg-gray-800 rounded-lg p-4 overflow-y-auto w-fit">
33
+ <pre class="whitespace-pre-line">{{ welcome_input }}</pre>
34
+ </div>
35
+ </div>
36
+ <!-- output in left -->
37
+ <div id="history-output" class="w-full flex justify-start">
38
+ <div class="bg-gray-700 rounded-lg p-4 overflow-y-auto w-fit">
39
+ <pre class="whitespace-pre-line">{{ welcome_output }}</pre>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <!-- input -->
44
+ <div class="bg-gray-800 rounded-lg p-4 w-full shadow-lg">
45
+ <form action="/exec" method="GET" onsubmit="return executeCommand(event)">
46
+ <div class="flex items-center space-x-2">
47
+ <span class="text-gray-400 flex-shrink-0" id="pwd_placeholder">{{ pwd }}</span>
48
+ <input type="text" id="command" name="command" class="w-full p-2 bg-gray-700 rounded-md"
49
+ placeholder="{{ welcome_input }}" autocomplete="off" autofocus>
50
+ </div>
51
+ <input type="hidden" id="pwd" name="pwd" value="{{ pwd }}">
52
+ </form>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+
58
+ <script>
59
+ window.addEventListener("load", function () {
60
+ const topButton = document.querySelector(".top-button");
61
+ const bottomButton = document.querySelector(".bottom-button");
62
+
63
+ window.addEventListener("scroll", function () {
64
+ if (window.scrollY + window.innerHeight < document.body.scrollHeight - 100) {
65
+ topButton.style.display = "block";
66
+ scrollLock = true;
67
+ } else {
68
+ topButton.style.display = "none";
69
+ scrollLock = false;
70
+ }
71
+
72
+ if (window.scrollY > 100) {
73
+ bottomButton.style.display = "block";
74
+ } else {
75
+ bottomButton.style.display = "none";
76
+ }
77
+ });
78
+ });
79
+ </script>
80
+
81
+ <!-- scroll to top button -->
82
+ <div class="fixed bottom-0 right-0 m-8 top-button" style="display: none;">
83
+ <button onclick="window.scrollTo(0, document.body.scrollHeight)" class="text-3xl p-4 text-white">↓</button>
84
+ </div>
85
+ <!-- scroll to bottom button -->
86
+ <div class="fixed top-0 right-0 m-8 bottom-button" style="display: none;">
87
+ <button onclick="window.scrollTo(0, 0)" class="text-3xl p-4 text-white">↑</button>
88
+ </div>
89
+
90
+ <script>
91
+ const historyElement = document.getElementById("history");
92
+ const command = document.getElementById("command");
93
+ const pwd = document.getElementById("pwd");
94
+
95
+ let scrollLock = false;
96
+
97
+ // tambhakan history input dan output
98
+ function addHistory(input, output) {
99
+ const copyInputElement = document.getElementById("history-input").cloneNode(true);
100
+ const copyOutputElement = document.getElementById("history-output").cloneNode(true);
101
+ // set input and output content
102
+ copyInputElement.querySelector("pre").textContent = input;
103
+ copyOutputElement.querySelector("pre").textContent = output;
104
+ // add running arttribute to output
105
+ copyOutputElement.setAttribute("x-running", "");
106
+ // append input and output
107
+ document.getElementById("history").appendChild(copyInputElement);
108
+ document.getElementById("history").appendChild(copyOutputElement);
109
+ }
110
+
111
+ // ANSI removal
112
+ // example: 
113
+ function removeAnsi(text) {
114
+ return text.replace(/\[(\d+)(;\d+)*[mK]/g, "");
115
+ }
116
+
117
+ // if on focus input and press UP
118
+ command.addEventListener("focus", () => {
119
+ window.addEventListener("keydown", (event) => {
120
+ // on TAB
121
+ if (event.key === "Tab") {
122
+ event.preventDefault();
123
+ command.value = command.placeholder;
124
+ }
125
+ })
126
+ });
127
+
128
+ const executeCommand = (event) => {
129
+ event.preventDefault();
130
+ // disable input
131
+ command.disabled = true;
132
+
133
+ // add history
134
+ addHistory(command.value, "");
135
+ outputRunningElement = document.querySelector("div[x-running]");
136
+
137
+ window.scrollTo(0, document.body.scrollHeight);
138
+ scrollLock = false;
139
+
140
+ const eventSource = new EventSource("/exec?" + new URLSearchParams({
141
+ command: command.value,
142
+ pwd: pwd.value
143
+ }));
144
+
145
+ eventSource.onmessage = function (event) {
146
+ const data = JSON.parse(event.data);
147
+ if (data.pwd) {
148
+ // set pwd
149
+ document.getElementById("pwd").value = data.pwd;
150
+ document.getElementById("pwd_placeholder").textContent = data.pwd;
151
+ }
152
+
153
+ // if done stop listening
154
+ if (data.output === "[DONE]") {
155
+ eventSource.close();
156
+ // enable input
157
+ command.disabled = false;
158
+ // set command value to placeholder
159
+ command.placeholder = command.value;
160
+ // remove running arttribute
161
+ outputRunningElement.removeAttribute("x-running");
162
+ // if output is empty remove element
163
+ if (outputRunningElement.querySelector("pre").textContent === "") {
164
+ outputRunningElement.remove();
165
+ }
166
+
167
+ // clear input and make command focus
168
+ command.value = "";
169
+ command.focus();
170
+ return;
171
+ }
172
+
173
+ if (data.output) {
174
+ // update output
175
+ outputRunningElement.querySelector("pre").textContent += removeAnsi(data.output);
176
+ // if you scroll down to bottom, scroll to bottom automatically
177
+ if (!scrollLock) {
178
+ window.scrollTo(0, document.body.scrollHeight);
179
+ }
180
+ }
181
+ }
182
+
183
+ };
184
+ </script>
185
+ </body>
186
+
187
+ </html>