runlab 1.1.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/app.js +10 -0
- package/dist/app/assets/fileIconSvg.js +11 -0
- package/dist/app/assets/navbarIconSvg.js +24 -0
- package/dist/app/core/navbar.js +15 -8
- package/dist/app/fileExplorer/FileExplorer.js +250 -11
- package/dist/app/terminal/commands.js +30 -1
- package/dist/app/terminal/terminal.js +8 -2
- package/dist/index.js +22 -2
- package/dist/{reactButton.jsx → installButtons/reactButton.jsx} +1 -1
- package/dist/security/hashing.js +8 -0
- package/package.json +2 -4
package/dist/app/app.js
CHANGED
|
@@ -151,12 +151,14 @@ export function setViewActive(status = false){
|
|
|
151
151
|
if(status){
|
|
152
152
|
document.getElementById("runlab-view").style.display = "block";
|
|
153
153
|
viewBtn.style.background = "#303030";
|
|
154
|
+
viewBtn.style.borderRadius = "8px";
|
|
154
155
|
viewBtn.style.color = "#fff";
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
if (!status){
|
|
158
159
|
document.getElementById("runlab-view").style.display = "none";
|
|
159
160
|
editorBtn.style.background = "#303030";
|
|
161
|
+
editorBtn.style.borderRadius = "8px";
|
|
160
162
|
editorBtn.style.color = "#fff";
|
|
161
163
|
}
|
|
162
164
|
}
|
|
@@ -259,4 +261,12 @@ export function getFeExecutables() {
|
|
|
259
261
|
|
|
260
262
|
export function getFileNode(file) {
|
|
261
263
|
return feNode.getFileNode(file);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function getNodeFromPath(path, currentPath) {
|
|
267
|
+
return feNode.getNodeFromPath(path, currentPath);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function setMoveNode(node, destNode) {
|
|
271
|
+
feNode.setMoveNode(node, destNode);
|
|
262
272
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const standardFileIcon = `📄`;
|
|
2
|
+
|
|
3
|
+
export const jsonIcon = `<svg width="64px" height="64px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#fbc02d"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>language_json</title> <rect width="24" height="24" fill="none"></rect> <path d="M5,3H7V5H5v5a2,2,0,0,1-2,2,2,2,0,0,1,2,2v5H7v2H5c-1.07-.27-2-.9-2-2V15a2,2,0,0,0-2-2H0V11H1A2,2,0,0,0,3,9V5A2,2,0,0,1,5,3M19,3a2,2,0,0,1,2,2V9a2,2,0,0,0,2,2h1v2H23a2,2,0,0,0-2,2v4a2,2,0,0,1-2,2H17V19h2V14a2,2,0,0,1,2-2,2,2,0,0,1-2-2V5H17V3h2M12,15a1,1,0,1,1-1,1,1,1,0,0,1,1-1M8,15a1,1,0,1,1-1,1,1,1,0,0,1,1-1m8,0a1,1,0,1,1-1,1A1,1,0,0,1,16,15Z"></path> </g></svg>`;
|
|
4
|
+
export const htmlIcon = `<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M6 28L4 3H28L26 28L16 31L6 28Z" fill="#E44D26"></path> <path d="M26 5H16V29.5L24 27L26 5Z" fill="#F16529"></path> <path d="M9.5 17.5L8.5 8H24L23.5 11H11.5L12 14.5H23L22 24L16 26L10 24L9.5 19H12.5L13 21.5L16 22.5L19 21.5L19.5 17.5H9.5Z" fill="white"></path> </g></svg>`;
|
|
5
|
+
export const cssIcon = `<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M6 28L4 3H28L26 28L16 31L6 28Z" fill="#1172B8"></path> <path d="M26 5H16V29.5L24 27L26 5Z" fill="#33AADD"></path> <path d="M19.5 17.5H9.5L9 14L17 11.5H9L8.5 8.5H24L23.5 12L17 14.5H23L22 24L16 26L10 24L9.5 19H12.5L13 21.5L16 22.5L19 21.5L19.5 17.5Z" fill="white"></path> </g></svg>`;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const javascriptIcon = `<svg xmlns="http://www.w3.org/2000/svg" aria-label="JavaScript" role="img" viewBox="0 0 512 512" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><rect width="512" height="512" rx="15%" fill="#f7df1e"></rect><path d="M324 370c10 17 24 29 47 29c20 0 33-10 33 -24c0-16 -13 -22 -35 -32l-12-5c-35-15 -58 -33 -58 -72c0-36 27 -64 70 -64c31 0 53 11 68 39l-37 24c-8-15 -17 -21 -31 -21c-14 0-23 9 -23 21c0 14 9 20 30 29l12 5c41 18 64 35 64 76c0 43-34 67 -80 67c-45 0-74 -21 -88 -49zm-170 4c8 13 14 25 31 25c16 0 26-6 26 -30V203h48v164c0 50-29 72 -72 72c-39 0-61 -20 -72 -44z"></path></g></svg>`;
|
|
9
|
+
export const typescriptIcon = `<svg xmlns="http://www.w3.org/2000/svg" aria-label="TypeScript" role="img" viewBox="0 0 512 512" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><rect width="512" height="512" rx="15%" fill="#3178c6"></rect><path fill="#ffffff" d="m233 284h64v-41H118v41h64v183h51zm84 173c8.1 4.2 18 7.3 29 9.4s23 3.1 35 3.1c12 0 23-1.1 34-3.4c11-2.3 20-6.1 28-11c8.1-5.3 15-12 19-21s7.1-19 7.1-32c0-9.1-1.4-17-4.1-24s-6.6-13-12-18c-5.1-5.3-11-10-18-14s-15-8.2-24-12c-6.6-2.7-12-5.3-18-7.9c-5.2-2.6-9.7-5.2-13-7.8c-3.7-2.7-6.5-5.5-8.5-8.4c-2-3-3-6.3-3-10c0-3.4.89-6.5 2.7-9.3s4.3-5.1 7.5-7.1c3.2-2 7.2-3.5 12-4.6c4.7-1.1 9.9-1.6 16-1.6c4.2 0 8.6.31 13 .94c4.6.63 9.3 1.6 14 2.9c4.7 1.3 9.3 2.9 14 4.9c4.4 2 8.5 4.3 12 6.9v-47c-7.6-2.9-16-5.1-25-6.5s-19-2.1-31-2.1c-12 0-23 1.3-34 3.8s-20 6.5-28 12c-8.1 5.4-14 12-19 21c-4.7 8.4-7 18-7 30c0 15 4.3 28 13 38c8.6 11 22 19 39 27c6.9 2.8 13 5.6 19 8.3s11 5.5 15 8.4c4.3 2.9 7.7 6.1 10 9.5c2.5 3.4 3.8 7.4 3.8 12c0 3.2-.78 6.2-2.3 9s-3.9 5.2-7.1 7.2s-7.1 3.6-12 4.8c-4.7 1.1-10 1.7-17 1.7c-11 0-22-1.9-32-5.7c-11-3.8-21-9.5-28.1-15.44z"></path></g></svg>`;
|
|
10
|
+
export const pythonIcon = `<svg width="64px" height="64px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M13.0164 2C10.8193 2 9.03825 3.72453 9.03825 5.85185V8.51852H15.9235V9.25926H5.97814C3.78107 9.25926 2 10.9838 2 13.1111L2 18.8889C2 21.0162 3.78107 22.7407 5.97814 22.7407H8.27322V19.4815C8.27322 17.3542 10.0543 15.6296 12.2514 15.6296H19.5956C21.4547 15.6296 22.9617 14.1704 22.9617 12.3704V5.85185C22.9617 3.72453 21.1807 2 18.9836 2H13.0164ZM12.0984 6.74074C12.8589 6.74074 13.4754 6.14378 13.4754 5.40741C13.4754 4.67103 12.8589 4.07407 12.0984 4.07407C11.3378 4.07407 10.7213 4.67103 10.7213 5.40741C10.7213 6.14378 11.3378 6.74074 12.0984 6.74074Z" fill="url(#paint0_linear_87_8204)"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M18.9834 30C21.1805 30 22.9616 28.2755 22.9616 26.1482V23.4815L16.0763 23.4815L16.0763 22.7408L26.0217 22.7408C28.2188 22.7408 29.9998 21.0162 29.9998 18.8889V13.1111C29.9998 10.9838 28.2188 9.25928 26.0217 9.25928L23.7266 9.25928V12.5185C23.7266 14.6459 21.9455 16.3704 19.7485 16.3704L12.4042 16.3704C10.5451 16.3704 9.03809 17.8296 9.03809 19.6296L9.03809 26.1482C9.03809 28.2755 10.8192 30 13.0162 30H18.9834ZM19.9015 25.2593C19.1409 25.2593 18.5244 25.8562 18.5244 26.5926C18.5244 27.329 19.1409 27.9259 19.9015 27.9259C20.662 27.9259 21.2785 27.329 21.2785 26.5926C21.2785 25.8562 20.662 25.2593 19.9015 25.2593Z" fill="url(#paint1_linear_87_8204)"></path> <defs> <linearGradient id="paint0_linear_87_8204" x1="12.4809" y1="2" x2="12.4809" y2="22.7407" gradientUnits="userSpaceOnUse"> <stop stop-color="#327EBD"></stop> <stop offset="1" stop-color="#1565A7"></stop> </linearGradient> <linearGradient id="paint1_linear_87_8204" x1="19.519" y1="9.25928" x2="19.519" y2="30" gradientUnits="userSpaceOnUse"> <stop stop-color="#FFDA4B"></stop> <stop offset="1" stop-color="#F9C600"></stop> </linearGradient> </defs> </g></svg>`;
|
|
11
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const closeIcon = `<svg width="16" height="16" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" stroke="#ffffff"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill="#ffffff" d="M195.2 195.2a64 64 0 0 1 90.496 0L512 421.504 738.304 195.2a64 64 0 0 1 90.496 90.496L602.496 512 828.8 738.304a64 64 0 0 1-90.496 90.496L512 602.496 285.696 828.8a64 64 0 0 1-90.496-90.496L421.504 512 195.2 285.696a64 64 0 0 1 0-90.496z"></path></g></svg>`;
|
|
2
|
+
export const minimizeIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M23 4C23 2.34315 21.6569 1 20 1H8C6.34315 1 5 2.34315 5 4V5H4C2.34315 5 1 6.34315 1 8V20C1 21.6569 2.34315 23 4 23H16C17.6569 23 19 21.6569 19 20V19H20C21.6569 19 23 17.6569 23 16V4ZM19 17H20C20.5523 17 21 16.5523 21 16V4C21 3.44772 20.5523 3 20 3H8C7.44772 3 7 3.44772 7 4V5H16C17.6569 5 19 6.34315 19 8V17ZM16 7C16.5523 7 17 7.44772 17 8V20C17 20.5523 16.5523 21 16 21H4C3.44772 21 3 20.5523 3 20V8C3 7.44772 3.44772 7 4 7H16Z" fill="#FFFFFF"/>
|
|
4
|
+
</svg>`;
|
|
5
|
+
export const runIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
7
|
+
<path
|
|
8
|
+
d="M16.6582 9.28638C18.098 10.1862 18.8178 10.6361 19.0647 11.2122C19.2803 11.7152 19.2803 12.2847 19.0647 12.7878C18.8178 13.3638 18.098 13.8137 16.6582 14.7136L9.896 18.94C8.29805 19.9387 7.49907 20.4381 6.83973 20.385C6.26501 20.3388 5.73818 20.0469 5.3944 19.584C5 19.053 5 18.1108 5 16.2264V7.77357C5 5.88919 5 4.94701 5.3944 4.41598C5.73818 3.9531 6.26501 3.66111 6.83973 3.6149C7.49907 3.5619 8.29805 4.06126 9.896 5.05998L16.6582 9.28638Z"
|
|
9
|
+
stroke="#ffffff"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
/>
|
|
13
|
+
</svg>`;
|
|
14
|
+
export const helpIcon = `<svg
|
|
15
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
width="16"
|
|
18
|
+
height="16"
|
|
19
|
+
fill="none"
|
|
20
|
+
>
|
|
21
|
+
<path d="M23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12ZM3.00683 12C3.00683 16.9668 7.03321 20.9932 12 20.9932C16.9668 20.9932 20.9932 16.9668 20.9932 12C20.9932 7.03321 16.9668 3.00683 12 3.00683C7.03321 3.00683 3.00683 7.03321 3.00683 12Z" fill="#FFFFFF"/>
|
|
22
|
+
<path d="M13.5 18C13.5 18.8284 12.8284 19.5 12 19.5C11.1716 19.5 10.5 18.8284 10.5 18C10.5 17.1716 11.1716 16.5 12 16.5C12.8284 16.5 13.5 17.1716 13.5 18Z" fill="#FFFFFF"/>
|
|
23
|
+
<path d="M11 12V14C11 14 11 15 12 15C13 15 13 14 13 14V12C13 12 13.4792 11.8629 13.6629 11.7883C13.6629 11.7883 13.9969 11.6691 14.2307 11.4896C14.4646 11.3102 14.6761 11.097 14.8654 10.8503C15.0658 10.6035 15.2217 10.3175 15.333 9.99221C15.4443 9.66693 15.5 9.4038 15.5 9C15.5 8.32701 15.3497 7.63675 15.0491 7.132C14.7596 6.61604 14.3476 6.21786 13.8132 5.93745C13.2788 5.64582 12.6553 5.5 11.9427 5.5C11.4974 5.5 11.1021 5.55608 10.757 5.66825C10.4118 5.7692 10.1057 5.9094 9.83844 6.08887C9.58236 6.25712 9.36525 6.4478 9.18711 6.66091C9.02011 6.86281 8.8865 7.0591 8.78629 7.24978C8.68609 7.44046 8.61929 7.6087 8.58589 7.75452C8.51908 7.96763 8.49125 8.14149 8.50238 8.27609C8.52465 8.41069 8.59145 8.52285 8.70279 8.61258C8.81413 8.70231 8.9867 8.79765 9.22051 8.8986C9.46546 8.97712 9.65473 9.00516 9.78834 8.98273C9.93308 8.96029 10.05 8.89299 10.1391 8.78083C10.1391 8.78083 10.6138 8.10569 10.7474 7.97109C10.8922 7.82528 11.0703 7.71312 11.2819 7.6346C11.4934 7.54487 11.7328 7.5 12 7.5C12.579 7.5 13.0076 7.64021 13.286 7.92062C13.5754 8.18982 13.6629 8.41629 13.6629 8.93225C13.6629 9.27996 13.6017 9.56038 13.4792 9.77349C13.3567 9.9866 13.1953 10.1605 12.9949 10.2951C12.9949 10.2951 12.7227 10.3991 12.5 10.5C12.2885 10.5897 11.9001 10.7381 11.6997 10.8503C11.5104 10.9512 11.4043 11.0573 11.2819 11.2144C11.1594 11.3714 11 11.7308 11 12Z" fill="#FFFFFF"/>
|
|
24
|
+
</svg>`;
|
package/dist/app/core/navbar.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sendCode } from "../../index.js";
|
|
2
2
|
import { setViewActive } from "../app.js";
|
|
3
3
|
import { getCurrentCode, getCurrentExtension } from "./editor.js";
|
|
4
|
+
import { closeIcon, minimizeIcon, runIcon, helpIcon } from "../assets/navbarIconSvg.js";
|
|
4
5
|
import Swal from "sweetalert2";
|
|
5
6
|
|
|
6
7
|
export function createNavbar(parentId) {
|
|
@@ -34,11 +35,9 @@ export function createNavbar(parentId) {
|
|
|
34
35
|
});
|
|
35
36
|
ul.appendChild(leftGroup);
|
|
36
37
|
|
|
37
|
-
const menuItems = ["
|
|
38
|
+
const menuItems = ["close", "minimize","help", "run"];
|
|
38
39
|
menuItems.forEach(item => {
|
|
39
40
|
const li = document.createElement("li");
|
|
40
|
-
li.textContent = item;
|
|
41
|
-
|
|
42
41
|
Object.assign(li.style, {
|
|
43
42
|
marginRight: "15px",
|
|
44
43
|
marginLeft: "15px",
|
|
@@ -46,14 +45,16 @@ export function createNavbar(parentId) {
|
|
|
46
45
|
cursor: "pointer",
|
|
47
46
|
color: "#fff"
|
|
48
47
|
});
|
|
49
|
-
if (item === "
|
|
48
|
+
if (item === "close") {
|
|
49
|
+
li.innerHTML = closeIcon;
|
|
50
50
|
li.addEventListener("click", () => {
|
|
51
51
|
const app = document.getElementById("runlab-custom-div");
|
|
52
52
|
app.style.display = "none";
|
|
53
53
|
document.getElementById("runlab-custom-btn").textContent = "Show App";
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
if (item === "
|
|
56
|
+
if (item === "minimize") {
|
|
57
|
+
li.innerHTML = minimizeIcon;
|
|
57
58
|
li.addEventListener("click", () => {
|
|
58
59
|
const container = document.getElementById("runlab-container");
|
|
59
60
|
let isMaximized = container.style.height === "100vh"? false: true;
|
|
@@ -76,13 +77,15 @@ export function createNavbar(parentId) {
|
|
|
76
77
|
}
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
|
-
if (item === "
|
|
80
|
+
if (item === "run") {
|
|
81
|
+
li.innerHTML = runIcon;
|
|
80
82
|
li.addEventListener("click", () => {
|
|
81
83
|
sendCode(getCurrentCode(),getCurrentExtension());
|
|
82
84
|
setViewActive(true);
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
|
-
if (item === "
|
|
87
|
+
if (item === "help") {
|
|
88
|
+
li.innerHTML = helpIcon;
|
|
86
89
|
li.addEventListener("click", () => {
|
|
87
90
|
Swal.fire({
|
|
88
91
|
title: "RunLab Help",
|
|
@@ -103,7 +106,9 @@ export function createNavbar(parentId) {
|
|
|
103
106
|
<li><span style="font-weight: bold;">pwd</span>: Print current directory</li>
|
|
104
107
|
<li><span style="font-weight: bold;">run</span>: Run a file (e.g., run script.js)</li>
|
|
105
108
|
<li><span style="font-weight: bold;">help | h</span>: Show help message</li>
|
|
109
|
+
<li><span style="font-weight: bold;">mv</span>: Move a file or folder (e.g., mv folder1/script.js folder2)</li>
|
|
106
110
|
</ul>
|
|
111
|
+
<p>If the path starts with / it will be considered absolute (from root), otherwise it will be considered relative to the current path.</p>
|
|
107
112
|
`,
|
|
108
113
|
width: 700,
|
|
109
114
|
confirmButtonText: "Got it!"
|
|
@@ -127,7 +132,8 @@ export function createNavbar(parentId) {
|
|
|
127
132
|
padding: "10px 14px",
|
|
128
133
|
fontWeight: "600",
|
|
129
134
|
color: "#FFDE21",
|
|
130
|
-
whiteSpace: "nowrap"
|
|
135
|
+
whiteSpace: "nowrap",
|
|
136
|
+
letterSpacing: "2px",
|
|
131
137
|
});
|
|
132
138
|
|
|
133
139
|
ul.appendChild(activeFileName);
|
|
@@ -142,6 +148,7 @@ export function createNavbar(parentId) {
|
|
|
142
148
|
const toggle = document.createElement("div");
|
|
143
149
|
Object.assign(toggle.style, {
|
|
144
150
|
display: "grid",
|
|
151
|
+
padding: "2px",
|
|
145
152
|
gridTemplateColumns: "1fr 1fr",
|
|
146
153
|
width: "180px",
|
|
147
154
|
height: "28px",
|
|
@@ -3,6 +3,7 @@ import { File } from "./File.js";
|
|
|
3
3
|
import { updateEditorContentById, setActiveFile } from "../core/editor.js";
|
|
4
4
|
import Swal from "sweetalert2";
|
|
5
5
|
import { setEditorActive, setViewActive } from "../app.js";
|
|
6
|
+
import { standardFileIcon, jsonIcon, htmlIcon, cssIcon, javascriptIcon, typescriptIcon, pythonIcon } from "../assets/fileIconSvg.js";
|
|
6
7
|
|
|
7
8
|
export class FileExplorer {
|
|
8
9
|
constructor(parentId) {
|
|
@@ -11,6 +12,7 @@ export class FileExplorer {
|
|
|
11
12
|
this.execFiles = [];
|
|
12
13
|
this.filesPath = [];
|
|
13
14
|
this.path = [];
|
|
15
|
+
this.currentNodeDragged = null;
|
|
14
16
|
this.render();
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -42,6 +44,106 @@ export class FileExplorer {
|
|
|
42
44
|
this.execFiles = files;
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
getNodeFromPath(path, currentPath) {
|
|
48
|
+
|
|
49
|
+
if (path === "/") {
|
|
50
|
+
return this.root;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (path.includes("..")) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Absolute path
|
|
57
|
+
if (path.startsWith("/")) {
|
|
58
|
+
path = path.slice(1);
|
|
59
|
+
let currentNode = this.root;
|
|
60
|
+
|
|
61
|
+
if (!path.includes("/")) {
|
|
62
|
+
if (path.includes(".")) { //File
|
|
63
|
+
const [name, ext] = path.split(".");
|
|
64
|
+
return currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
65
|
+
}
|
|
66
|
+
return currentNode.children.find(c => c.name === path);//Folder
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let pathList = path.split("/").filter(p => p);
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < pathList.length; i++) {
|
|
72
|
+
const part = pathList[i];
|
|
73
|
+
|
|
74
|
+
if (part.includes(".")) {
|
|
75
|
+
const [name, ext] = part.split(".");
|
|
76
|
+
const nextNode = currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
77
|
+
if (!nextNode) return null;
|
|
78
|
+
currentNode = nextNode;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!part.includes(".")) {
|
|
82
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
83
|
+
if (!nextNode) return null;
|
|
84
|
+
currentNode = nextNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
return currentNode;
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
// Relative path
|
|
92
|
+
if (!path.startsWith("/")) {
|
|
93
|
+
let currentNode = this.root;
|
|
94
|
+
|
|
95
|
+
if (currentPath.length > 1) {
|
|
96
|
+
for (let i = 1; i < currentPath.length; i++) {
|
|
97
|
+
const part = currentPath[i];
|
|
98
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
99
|
+
if (!nextNode) return null;
|
|
100
|
+
currentNode = nextNode;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!path.includes("/")) {
|
|
105
|
+
if (path.includes(".")) { //File
|
|
106
|
+
const [name, ext] = path.split(".");
|
|
107
|
+
return currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
108
|
+
}
|
|
109
|
+
return currentNode.children.find(c => c.name === path);//Folder
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let pathList = path.split("/").filter(p => p);
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < pathList.length; i++) {
|
|
115
|
+
const part = pathList[i];
|
|
116
|
+
|
|
117
|
+
if (part.includes(".")) {
|
|
118
|
+
const [name, ext] = part.split(".");
|
|
119
|
+
const nextNode = currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
120
|
+
if (!nextNode) return null;
|
|
121
|
+
currentNode = nextNode;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!part.includes(".")) {
|
|
125
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
126
|
+
if (!nextNode) return null;
|
|
127
|
+
currentNode = nextNode;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
return currentNode;
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
setMoveNode(node, destNode) {
|
|
138
|
+
if (node === this.root) return;
|
|
139
|
+
if (node instanceof File) {
|
|
140
|
+
this.moveFile(node, destNode);
|
|
141
|
+
}
|
|
142
|
+
if (node instanceof Folder) {
|
|
143
|
+
this.moveFolder(node, destNode);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
45
147
|
/* =========================
|
|
46
148
|
RENDER
|
|
47
149
|
========================== */
|
|
@@ -61,6 +163,22 @@ export class FileExplorer {
|
|
|
61
163
|
this.showRootMenu(e);
|
|
62
164
|
};
|
|
63
165
|
|
|
166
|
+
rootDiv.addEventListener("dragover", (e) => {
|
|
167
|
+
if (e.target !== rootDiv) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
rootDiv.style.backgroundColor = "#084475a4";
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
});
|
|
173
|
+
rootDiv.addEventListener("dragleave", (e) => {
|
|
174
|
+
rootDiv.style.backgroundColor = "transparent";
|
|
175
|
+
});
|
|
176
|
+
rootDiv.addEventListener("drop", (e) => {
|
|
177
|
+
rootDiv.style.backgroundColor = "transparent";
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
this.setMoveNode(this.currentNodeDragged, this.root);
|
|
180
|
+
this.currentNodeDragged = null;
|
|
181
|
+
});
|
|
64
182
|
|
|
65
183
|
this.renderFolder(this.root, rootDiv);
|
|
66
184
|
|
|
@@ -70,6 +188,8 @@ export class FileExplorer {
|
|
|
70
188
|
renderFolder(folder, parentUl) {
|
|
71
189
|
folder.children.forEach(node => {
|
|
72
190
|
if (node instanceof Folder) {
|
|
191
|
+
|
|
192
|
+
/* ============ BUTTON =============== */
|
|
73
193
|
const btn = document.createElement("button");
|
|
74
194
|
btn.textContent = `📁 ${node.name}`;
|
|
75
195
|
btn.style.width = "100%";
|
|
@@ -80,18 +200,18 @@ export class FileExplorer {
|
|
|
80
200
|
btn.style.paddingTop = "8px";
|
|
81
201
|
btn.style.paddingBottom = "8px";
|
|
82
202
|
btn.style.cursor = "pointer";
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
btn.onmouseover = () => btn.style.backgroundColor = "#555";
|
|
87
|
-
btn.onmouseout = () => btn.style.backgroundColor = "transparent";
|
|
88
|
-
|
|
203
|
+
btn.draggable = true;
|
|
204
|
+
/* ============ UL =============== */
|
|
89
205
|
const ul = document.createElement("ul");
|
|
90
206
|
ul.style.display = "none";
|
|
91
207
|
ul.style.listStyle = "none";
|
|
92
208
|
ul.style.margin = "0";
|
|
93
209
|
ul.style.padding = "0 0 0 24px";
|
|
94
|
-
|
|
210
|
+
/* ============ DIV =============== */
|
|
211
|
+
const div = document.createElement("div");
|
|
212
|
+
/* ============ EVENTS =============== */
|
|
213
|
+
btn.onmouseover = () => btn.style.backgroundColor = "#555";
|
|
214
|
+
btn.onmouseout = () => btn.style.backgroundColor = "transparent";
|
|
95
215
|
|
|
96
216
|
btn.onclick = () => this.toggle(ul);
|
|
97
217
|
btn.oncontextmenu = e => {
|
|
@@ -100,13 +220,69 @@ export class FileExplorer {
|
|
|
100
220
|
this.showFolderMenu(e, node);
|
|
101
221
|
};
|
|
102
222
|
|
|
103
|
-
|
|
223
|
+
btn.addEventListener("dragstart", (e) => {
|
|
224
|
+
|
|
225
|
+
this.currentNodeDragged = node;
|
|
226
|
+
});
|
|
227
|
+
btn.addEventListener("dragover", (e) => {
|
|
228
|
+
if (e.target !== btn) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
div.style.backgroundColor = "#084475a4";
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
});
|
|
234
|
+
btn.addEventListener("dragleave", (e) => {
|
|
235
|
+
div.style.backgroundColor = "transparent";
|
|
236
|
+
});
|
|
237
|
+
btn.addEventListener("drop", (e) => {
|
|
238
|
+
div.style.backgroundColor = "transparent";
|
|
239
|
+
e.preventDefault();
|
|
240
|
+
this.setMoveNode(this.currentNodeDragged, node);
|
|
241
|
+
this.currentNodeDragged = null;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
ul.addEventListener("dragover", (e) => {
|
|
245
|
+
if (e.target !== ul) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
div.style.backgroundColor = "#084475a4";
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
});
|
|
251
|
+
ul.addEventListener("dragleave", (e) => {
|
|
252
|
+
div.style.backgroundColor = "transparent";
|
|
253
|
+
});
|
|
254
|
+
ul.addEventListener("drop", (e) => {
|
|
255
|
+
div.style.backgroundColor = "transparent";
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
this.setMoveNode(this.currentNodeDragged, node);
|
|
258
|
+
this.currentNodeDragged = null;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
/* ============ APPEND =============== */
|
|
262
|
+
div.append(btn, ul);
|
|
263
|
+
parentUl.append(div);
|
|
104
264
|
this.renderFolder(node, ul);
|
|
105
265
|
}
|
|
106
266
|
|
|
107
267
|
if (node instanceof File) {
|
|
268
|
+
/* ============ LI =============== */
|
|
108
269
|
const li = document.createElement("li");
|
|
109
|
-
|
|
270
|
+
const icon = this.getSvgIcon(node.ext);
|
|
271
|
+
li.innerHTML = `
|
|
272
|
+
<div style="display:flex; align-items:center; gap:4px;">
|
|
273
|
+
<div style="
|
|
274
|
+
width:16px;
|
|
275
|
+
height:16px;
|
|
276
|
+
display:flex;
|
|
277
|
+
align-items:center;
|
|
278
|
+
justify-content:center;
|
|
279
|
+
flex-shrink:0;
|
|
280
|
+
">
|
|
281
|
+
${icon}
|
|
282
|
+
</div>
|
|
283
|
+
<span>${node.name}.${node.ext}</span>
|
|
284
|
+
</div>
|
|
285
|
+
`;
|
|
110
286
|
li.style.width = "100%";
|
|
111
287
|
li.style.textAlign = "left";
|
|
112
288
|
li.style.border = "none";
|
|
@@ -114,9 +290,10 @@ export class FileExplorer {
|
|
|
114
290
|
li.style.color = "white";
|
|
115
291
|
li.style.paddingTop = "8px";
|
|
116
292
|
li.style.paddingBottom = "8px";
|
|
293
|
+
li.style.paddingLeft = "8px";
|
|
117
294
|
li.style.cursor = "pointer";
|
|
118
|
-
|
|
119
|
-
|
|
295
|
+
li.draggable = true;
|
|
296
|
+
/* ============ EVENTS =============== */
|
|
120
297
|
li.onclick = () => {
|
|
121
298
|
setActiveFile(node);
|
|
122
299
|
setViewActive(false);
|
|
@@ -130,6 +307,26 @@ export class FileExplorer {
|
|
|
130
307
|
this.showFileMenu(e, node);
|
|
131
308
|
};
|
|
132
309
|
|
|
310
|
+
li.addEventListener("dragstart", (e) => {
|
|
311
|
+
this.currentNodeDragged = node;
|
|
312
|
+
});
|
|
313
|
+
li.addEventListener("dragover", (e) => {
|
|
314
|
+
if (e.target !== li) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
parentUl.parentElement.style.backgroundColor = "#084475a4";
|
|
318
|
+
e.preventDefault();
|
|
319
|
+
});
|
|
320
|
+
li.addEventListener("dragleave", (e) => {
|
|
321
|
+
parentUl.parentElement.style.backgroundColor = "transparent";
|
|
322
|
+
});
|
|
323
|
+
li.addEventListener("drop", (e) => {
|
|
324
|
+
parentUl.parentElement.style.backgroundColor = "transparent";
|
|
325
|
+
e.preventDefault();
|
|
326
|
+
this.setMoveNode(this.currentNodeDragged, node.parent);
|
|
327
|
+
this.currentNodeDragged = null;
|
|
328
|
+
});
|
|
329
|
+
/* ============ APPEND =============== */
|
|
133
330
|
parentUl.appendChild(li);
|
|
134
331
|
this.filesPath.push({ name: `${node.name}.${node.ext}`, path: node.parent.path });
|
|
135
332
|
|
|
@@ -168,6 +365,29 @@ export class FileExplorer {
|
|
|
168
365
|
this.render();
|
|
169
366
|
}
|
|
170
367
|
|
|
368
|
+
moveFile(node, newParent) {
|
|
369
|
+
if (newParent === node) return;
|
|
370
|
+
if (newParent instanceof File) return;
|
|
371
|
+
if (node === this.root) return;
|
|
372
|
+
const oldParent = node.parent;
|
|
373
|
+
oldParent.children = oldParent.children.filter(c => c !== node);
|
|
374
|
+
newParent.children.push(node);
|
|
375
|
+
node.parent = newParent;
|
|
376
|
+
this.render();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
moveFolder(node, newParent) {
|
|
380
|
+
if (newParent === node) return;
|
|
381
|
+
if (newParent instanceof File) return;
|
|
382
|
+
if (node === this.root) return;
|
|
383
|
+
const oldParent = node.parent;
|
|
384
|
+
oldParent.children = oldParent.children.filter(c => c !== node);
|
|
385
|
+
newParent.children.push(node);
|
|
386
|
+
node.parent = newParent;
|
|
387
|
+
node.path = [...newParent.path, node.name];
|
|
388
|
+
this.render();
|
|
389
|
+
}
|
|
390
|
+
|
|
171
391
|
/* =========================
|
|
172
392
|
MENUS
|
|
173
393
|
========================== */
|
|
@@ -402,4 +622,23 @@ export class FileExplorer {
|
|
|
402
622
|
|
|
403
623
|
document.body.appendChild(menu);
|
|
404
624
|
}
|
|
625
|
+
|
|
626
|
+
getSvgIcon(ext) {
|
|
627
|
+
switch (ext) {
|
|
628
|
+
case 'json':
|
|
629
|
+
return jsonIcon;
|
|
630
|
+
case 'html':
|
|
631
|
+
return htmlIcon;
|
|
632
|
+
case 'css':
|
|
633
|
+
return cssIcon;
|
|
634
|
+
case 'js':
|
|
635
|
+
return javascriptIcon;
|
|
636
|
+
case 'ts':
|
|
637
|
+
return typescriptIcon;
|
|
638
|
+
case 'py':
|
|
639
|
+
return pythonIcon;
|
|
640
|
+
default:
|
|
641
|
+
return standardFileIcon;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
405
644
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getCurrentDir, getCurrentPath, setCurrentDir, setCurrentPath } from "./terminal.js"
|
|
2
2
|
import { sendCode } from "../../index.js";
|
|
3
|
-
import { setViewActive, getFeExecutables, getFileNode } from "../app.js";
|
|
3
|
+
import { setViewActive, getFeExecutables, getFileNode, getNodeFromPath, setMoveNode } from "../app.js";
|
|
4
4
|
|
|
5
5
|
export function print(text = "") {
|
|
6
6
|
const div = document.createElement("div")
|
|
@@ -114,4 +114,33 @@ export function handleRun(cmd) {
|
|
|
114
114
|
const fileNode = getFileNode(file)
|
|
115
115
|
sendCode(fileNode.content, fileNode.ext)
|
|
116
116
|
setViewActive(true)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function handleMove(cmd) {
|
|
120
|
+
|
|
121
|
+
if (cmd.split(" ").length !== 3) {
|
|
122
|
+
print("Invalid move command format! Use: mv <source> <destination>")
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const nodePath = cmd.split(" ")[1];
|
|
127
|
+
const destPath = cmd.split(" ")[2];
|
|
128
|
+
|
|
129
|
+
const currentPath = getCurrentPath();
|
|
130
|
+
|
|
131
|
+
const node = getNodeFromPath(nodePath, currentPath);
|
|
132
|
+
const destNode = getNodeFromPath(destPath, currentPath);
|
|
133
|
+
|
|
134
|
+
if (!node) {
|
|
135
|
+
print("File or folder to be moved not found!")
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!destNode) {
|
|
140
|
+
print("Folder to move into not found!")
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setMoveNode(node, destNode)
|
|
145
|
+
|
|
117
146
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {print, handleLS, handleClear, handleChangeDirectory, handleRun} from "./commands.js"
|
|
1
|
+
import {print, handleLS, handleClear, handleChangeDirectory, handleRun, handleMove} from "./commands.js"
|
|
2
2
|
|
|
3
3
|
export let currentDirNode = null
|
|
4
4
|
export let currentPath = []
|
|
@@ -120,7 +120,13 @@ function handleCommand(cmd) {
|
|
|
120
120
|
print("- pwd: Print current directory")
|
|
121
121
|
print("- run: Run a file (e.g., run script.js)")
|
|
122
122
|
print("- help | h: Show this help message")
|
|
123
|
-
|
|
123
|
+
print("- mv: Move a file or folder (e.g., mv <source> <destination>)")
|
|
124
|
+
print(" If the path starts with / it will be considered absolute (from root), otherwise it will be considered relative to the current path.")
|
|
125
|
+
|
|
126
|
+
} else if (split[0] === "mv") {
|
|
127
|
+
handleMove(trimmed)
|
|
128
|
+
|
|
129
|
+
}else if (trimmed !== "") {
|
|
124
130
|
print(`command not found: ${trimmed}`)
|
|
125
131
|
}
|
|
126
132
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { generateContainer } from "./app/app.js";
|
|
2
2
|
import { appendViewContent } from "./app/app.js";
|
|
3
|
-
import {
|
|
3
|
+
import { setCurrentDir } from "./app/terminal/terminal.js";
|
|
4
|
+
import { ReactRunlabButton } from "./installButtons/reactButton.jsx";
|
|
5
|
+
import { generateHash } from "./security/hashing.js";
|
|
4
6
|
|
|
5
7
|
let runtimeUrl = "";
|
|
6
8
|
function setRuntimeUrl(url) {
|
|
@@ -10,6 +12,21 @@ function getRuntimeUrl() {
|
|
|
10
12
|
return runtimeUrl;
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
let currentAuthHash = "";
|
|
16
|
+
function setAuthHash(hash) {
|
|
17
|
+
currentAuthHash = hash;
|
|
18
|
+
}
|
|
19
|
+
function getAuthHash() {
|
|
20
|
+
return currentAuthHash;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function RunlabSignature(secretKey) {
|
|
24
|
+
const timestamp = Math.floor(Date.now() / 10000).toString();
|
|
25
|
+
const hash = await generateHash(secretKey, timestamp);
|
|
26
|
+
setAuthHash(hash);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
export async function run({
|
|
14
31
|
parentId,
|
|
15
32
|
runtimeUrl = ""
|
|
@@ -27,10 +44,13 @@ export async function sendCode(code, ext = "txt") {
|
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
try {
|
|
47
|
+
const timestamp = Date.now().toString();
|
|
30
48
|
const response = await fetch(getRuntimeUrl(), {
|
|
31
49
|
method: "POST",
|
|
32
50
|
headers: {
|
|
33
|
-
"Content-Type": "application/json"
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"X-Timestamp": timestamp,
|
|
53
|
+
"X-Signature": await getSignature(timestamp)
|
|
34
54
|
},
|
|
35
55
|
body: JSON.stringify({ code, ext })
|
|
36
56
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export async function generateHash(secretKey, timestamp) {
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const data = encoder.encode(secretKey + timestamp);
|
|
4
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
5
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
6
|
+
const hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
|
|
7
|
+
return hashHex;
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runlab",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
|
|
8
8
|
"exports": {
|
|
9
|
-
".": "./dist/index.js"
|
|
10
|
-
"./wasm": "./dist/runlab.wasm",
|
|
11
|
-
"./wasm_exec": "./dist/wasm_exec.js"
|
|
9
|
+
".": "./dist/index.js"
|
|
12
10
|
},
|
|
13
11
|
|
|
14
12
|
"files": ["dist/**/*", "LICENSE"],
|