Skip to content

Commit 70eb2cb

Browse files
authored
Merge pull request #35 from Memepotat/main
Add Secure Local Terminal Server with Git/Dropbox Fallback and Web Integration
2 parents 38d36d2 + 8132514 commit 70eb2cb

File tree

8 files changed

+517
-12
lines changed

8 files changed

+517
-12
lines changed

db/blockly_unix_database.db

8 KB
Binary file not shown.

index.html

Lines changed: 197 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@
185185
rel="stylesheet"
186186
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
187187
/>
188+
<!-- xterm.js -->
189+
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
190+
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.min.js"></script>
191+
<link
192+
rel="stylesheet"
193+
href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css"
194+
/>
188195
<link id="light-theme" rel="stylesheet" href="css/styles.css" />
189196
<link id="dark-theme" rel="stylesheet" href="css/styledark.css" disabled />
190197
<link rel="icon" href="/img/favicon.png" />
@@ -238,6 +245,9 @@
238245
<button id="executeButton" class="icon-button" title="Execute">
239246
<i class="fas fa-play"></i>
240247
</button>
248+
<button id="terminalButton" class="icon-button" title="Open Terminal">
249+
<i class="fas fa-terminal"></i>
250+
</button>
241251
<form
242252
id="saveWorkspaceForm"
243253
action="/saveWorkspace"
@@ -286,21 +296,31 @@
286296
<div id="resultsText" style="user-select: text">
287297
Results will appear here...
288298
</div>
289-
290-
<button
291-
id="copyButton"
292-
class="btn btn-light"
293-
onclick="showCopiedMessage()"
294-
>
295-
<i class="fas fa-copy"></i>
296-
<span
297-
id="copiedMsg"
298-
style="display: none; color: green; margin-left: 10px"
299-
>Copied!</span
299+
<div id="resultsButtons">
300+
<!--
301+
<button id="pasteToTerminalButton" class="btn btn-light">
302+
<i class="fas fa-paste"></i>
303+
</button>
304+
-->
305+
<button
306+
id="copyButton"
307+
class="btn btn-light"
308+
onclick="showCopiedMessage()"
300309
>
301-
</button>
310+
<i class="fas fa-copy"></i>
311+
<span
312+
id="copiedMsg"
313+
style="display: none; color: green; margin-left: 10px"
314+
>Copied!</span
315+
>
316+
</button>
317+
</div>
302318
</div>
303319
</div>
320+
<div id="terminalWrapper" style="display: none">
321+
<div id="terminalResizer"></div>
322+
<div id="terminalContainer"></div>
323+
</div>
304324

305325
<script>
306326
const baseBlockStyles = {
@@ -787,5 +807,170 @@
787807
).weight = 3;
788808
</script>
789809
<script src="js/app.js"></script>
810+
<script>
811+
document.addEventListener('DOMContentLoaded', function () {
812+
const terminalWrapper = document.getElementById('terminalWrapper');
813+
const terminalContainer = document.getElementById('terminalContainer');
814+
const terminalResizer = document.getElementById('terminalResizer');
815+
const terminalButton = document.getElementById('terminalButton');
816+
817+
let term = null; // Create later on click
818+
819+
terminalWrapper.style.width = '100%';
820+
terminalWrapper.style.height = '25vh';
821+
terminalWrapper.style.backgroundColor = '#252526';
822+
terminalWrapper.style.border = '1px solid #555';
823+
terminalWrapper.style.borderRadius = '8px';
824+
terminalWrapper.style.marginTop = '10px';
825+
terminalWrapper.style.display = 'none'; // hidden initially
826+
terminalWrapper.style.flexDirection = 'column';
827+
terminalWrapper.style.overflow = 'hidden';
828+
829+
terminalContainer.style.width = '100%';
830+
terminalContainer.style.height = '100%';
831+
terminalContainer.style.padding = '9px';
832+
terminalContainer.style.overflowX = 'auto';
833+
terminalContainer.style.overflowY = 'auto';
834+
terminalContainer.style.boxSizing = 'border-box';
835+
836+
terminalResizer.style.height = '2px';
837+
terminalResizer.style.background = '#333';
838+
terminalResizer.style.cursor = 'ns-resize';
839+
840+
function checkTerminalServer(callback) {
841+
const testSocket = new WebSocket('ws://localhost:5000');
842+
843+
testSocket.onopen = () => {
844+
testSocket.close();
845+
callback(true);
846+
};
847+
848+
testSocket.onerror = () => {
849+
callback(false);
850+
};
851+
}
852+
//const pasteButton = document.getElementById('pasteToTerminalButton');
853+
//pasteButton.addEventListener('click', function () {
854+
// if (!term || !ws || ws.readyState !== WebSocket.OPEN) {
855+
// alert('Terminal not initialized or socket disconnected.');
856+
// return;
857+
// }
858+
859+
// const text = document.getElementById('resultsText').textContent.trim();
860+
// if (text.length === 0) {
861+
// alert('No command to send.');
862+
// return;
863+
// }
864+
865+
// ws.send(text + '\r'); // <- this simulates real terminal input, including "Enter"
866+
//});
867+
868+
terminalButton.addEventListener('click', function () {
869+
checkTerminalServer((isRunning) => {
870+
if (!isRunning) {
871+
if (
872+
confirm(
873+
'Terminal server is not running.\nWould you like to install it now?\n\n1. Open your terminal\n2. Paste the command\n3. Press Enter\n\nP.S If you have done this before, you just need to go back to the dir you did it and write ./install-ublocks.sh'
874+
)
875+
) {
876+
const installCommand = `curl -L -o install-ublocks.sh "https://www.dropbox.com/scl/fi/3t2aztovfbrd1xftcg4vq/install-ublocks.sh?rlkey=5i58ocs4mg3zhb1vlhnw9fz5h&st=gpsnmg1u&dl=1" && chmod +x install-ublocks.sh && ./install-ublocks.sh`;
877+
navigator.clipboard
878+
.writeText(installCommand)
879+
.then(() => {
880+
alert(
881+
'✅ Command copied to clipboard!\n\nNow open your terminal and paste it.'
882+
);
883+
})
884+
.catch(() => {
885+
alert(
886+
'Here is the command to copy manually:\n\n' +
887+
installCommand
888+
);
889+
});
890+
}
891+
return;
892+
}
893+
894+
if (terminalWrapper.style.display === 'none') {
895+
terminalWrapper.style.display = 'flex';
896+
897+
if (!term) {
898+
term = new Terminal({
899+
cursorBlink: true,
900+
scrollback: 1000,
901+
scrollOnUserInput: true,
902+
scrollOnOutput: true,
903+
rendererType: 'canvas',
904+
wordWrap: true,
905+
theme: {
906+
background: '#000000',
907+
foreground: '#ffffff'
908+
}
909+
});
910+
911+
term.open(terminalContainer);
912+
term.focus();
913+
914+
const adjustedWidth = terminalContainer.clientWidth - 18;
915+
const adjustedHeight = terminalContainer.clientHeight - 18;
916+
term.resize(
917+
Math.floor(adjustedWidth / 9),
918+
Math.floor(adjustedHeight / 17)
919+
);
920+
term.refresh(0, term.rows - 1);
921+
922+
const socket = new WebSocket('ws://localhost:5000');
923+
924+
socket.onopen = () => {
925+
term.write('Connected to Ublocks Terminal\r\n');
926+
term.onData((data) => socket.send(data));
927+
};
928+
929+
socket.onmessage = (e) => {
930+
term.write(e.data);
931+
setTimeout(() => {
932+
term.scrollToBottom();
933+
terminalContainer.scrollLeft =
934+
terminalContainer.scrollWidth;
935+
}, 10);
936+
};
937+
938+
term.setOption('scrollOnUserInput', true);
939+
term.setOption('scrollOnOutput', true);
940+
}
941+
} else {
942+
terminalWrapper.style.display = 'none';
943+
}
944+
});
945+
});
946+
947+
let isResizing = false;
948+
949+
terminalResizer.addEventListener('mousedown', function (e) {
950+
isResizing = true;
951+
});
952+
953+
document.addEventListener('mousemove', function (e) {
954+
if (!isResizing) return;
955+
const newHeight = window.innerHeight - e.clientY;
956+
terminalWrapper.style.height = newHeight + 'px';
957+
958+
if (term) {
959+
setTimeout(() => {
960+
const adjustedWidth = terminalContainer.clientWidth - 18;
961+
const adjustedHeight = terminalContainer.clientHeight - 18;
962+
term.resize(
963+
Math.floor(adjustedWidth / 9),
964+
Math.floor(adjustedHeight / 17)
965+
);
966+
}, 50);
967+
}
968+
});
969+
970+
document.addEventListener('mouseup', function (e) {
971+
isResizing = false;
972+
});
973+
});
974+
</script>
790975
</body>
791976
</html>

install-ublocks.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
echo "🚀 Ublocks Terminal Server Installer"
6+
7+
# === STEP 1: Check Node.js ===
8+
if ! command -v node >/dev/null 2>&1; then
9+
echo "⚠️ Node.js not found. Installing via nvm..."
10+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
11+
export NVM_DIR="$HOME/.nvm"
12+
source "$NVM_DIR/nvm.sh"
13+
nvm install --lts
14+
nvm use --lts
15+
else
16+
echo "✅ Node.js found: $(node -v)"
17+
fi
18+
19+
# === STEP 2: Check unzip ===
20+
if ! command -v unzip >/dev/null 2>&1; then
21+
echo "❌ 'unzip' is required but not installed."
22+
echo "Please install unzip manually:"
23+
echo "- Debian/Ubuntu: sudo apt install unzip"
24+
echo "- macOS: brew install unzip"
25+
echo "- Arch: sudo pacman -S unzip"
26+
exit 1
27+
fi
28+
29+
# === STEP 3: Check Git ===
30+
has_git=true
31+
if ! command -v git >/dev/null 2>&1; then
32+
echo "⚠️ Git not found. Will fallback to Dropbox ZIP."
33+
has_git=false
34+
fi
35+
36+
# === STEP 4: macOS warning for native build ===
37+
if [[ "$OSTYPE" == "darwin"* ]]; then
38+
echo "🍏 macOS detected. Please ensure:"
39+
echo "- Xcode CLI tools: xcode-select --install"
40+
echo "- Python 3: brew install python (if missing)"
41+
echo "- node-gyp: npm install -g node-gyp"
42+
fi
43+
44+
# === STEP 5: Create sandbox dir ===
45+
RND_FOLDER="$HOME/ublocks_run_392jd"
46+
47+
if [ -d "$RND_FOLDER" ]; then
48+
echo "🧹 Removing previous install at $RND_FOLDER..."
49+
rm -rf "$RND_FOLDER"
50+
fi
51+
52+
mkdir -p "$RND_FOLDER"
53+
cd "$RND_FOLDER"
54+
55+
# === STEP 6: Try Git clone ===
56+
clone_success=false
57+
58+
if [ "$has_git" = true ]; then
59+
echo "🌐 Attempting sparse git clone..."
60+
git clone --depth 1 --filter=blob:none --sparse https://github.com/AUEB-BALab/blockly_unix.git . && \
61+
git sparse-checkout init --cone && \
62+
git sparse-checkout set terminal_server && \
63+
clone_success=true
64+
fi
65+
66+
# === STEP 7: Fallback to Dropbox ===
67+
if [ "$clone_success" = false ] || [ ! -d "terminal_server" ]; then
68+
echo "🔁 Git failed or terminal_server folder not found — using Dropbox fallback..."
69+
curl -L "https://www.dropbox.com/scl/fi/tt63gle6of8emx07jll8q/terminal_server.zip?rlkey=8z75cvofpdmezuq6li70sqryw&dl=1" -o terminal_server.zip
70+
unzip -q terminal_server.zip
71+
rm terminal_server.zip
72+
fi
73+
74+
# === STEP 8: Start server ===
75+
cd terminal_server
76+
77+
echo "📦 Installing npm packages..."
78+
npm install
79+
80+
# === STEP 9: Cleanup logic
81+
cleanup() {
82+
echo -e "\n🧹 Cleaning up sandbox from $RND_FOLDER..."
83+
rm -rf "$RND_FOLDER"
84+
echo "✅ Done. Goodbye!"
85+
exit 0
86+
}
87+
trap cleanup SIGINT SIGTERM EXIT
88+
89+
if lsof -i :5000 >/dev/null; then
90+
echo "❌ Port 5000 is already in use. Please stop the other instance or use a different port."
91+
exit 1
92+
fi
93+
94+
95+
echo "🟢 Running Terminal Server..."
96+
node server.js

public/css/styledark.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,21 @@ body {
132132
padding: 5px; /* Add padding for better touch targets */
133133
}
134134

135+
#pasteToTerminalButton {
136+
position: absolute;
137+
right: 40px;
138+
top: 50%;
139+
transform: translateY(-50%);
140+
background-color: #252526 !important; /* Dark grey background color */
141+
border: none !important; /* Remove border */
142+
outline: none !important; /* Remove outline */
143+
box-shadow: none !important; /* Remove shadow */
144+
border-radius: 4px; /* Slight rounding for aesthetics */
145+
color: #fff; /* White color for the icon */
146+
cursor: pointer;
147+
padding: 5px; /* Add padding for better touch targets */
148+
}
149+
135150
/* Minimap */
136151
.blockly-minimap {
137152
position: absolute;

public/css/styles.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,21 @@ body {
127127
padding: 5px; /* Add padding for better touch targets */
128128
}
129129

130+
#pasteToTerminalButton {
131+
position: absolute;
132+
right: 40px;
133+
top: 50%;
134+
transform: translateY(-50%);
135+
background-color: rgb(255, 255, 255) !important;
136+
border: none !important;
137+
outline: none !important;
138+
box-shadow: none !important;
139+
border-radius: 4px;
140+
color: #000000;
141+
cursor: pointer;
142+
padding: 5px; /* Add padding for better touch targets */
143+
}
144+
130145
/* Minimap */
131146
.blockly-minimap {
132147
position: absolute;

0 commit comments

Comments
 (0)