runlab 1.0.0 → 1.2.0
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 +19 -5
- package/dist/app/core/navbar.js +33 -1
- package/dist/app/fileExplorer/FileExplorer.js +201 -10
- package/dist/app/terminal/commands.js +30 -1
- package/dist/app/terminal/terminal.js +8 -2
- package/dist/index.js +6 -3
- package/dist/reactButton.jsx +46 -0
- package/package.json +1 -1
package/dist/app/app.js
CHANGED
|
@@ -3,12 +3,18 @@ import { createNavbar } from "./core/navbar.js";
|
|
|
3
3
|
import { FileExplorer } from "./fileExplorer/FileExplorer.js";
|
|
4
4
|
import { createTerminal } from "./terminal/terminal.js";
|
|
5
5
|
|
|
6
|
-
export function generateContainer(parentId
|
|
6
|
+
export function generateContainer(parentId) {
|
|
7
7
|
const container = document.createElement("div");
|
|
8
8
|
container.id = "runlab-container";
|
|
9
9
|
Object.assign(container.style, {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
position: "fixed",
|
|
11
|
+
inset: "0",
|
|
12
|
+
left: "0",
|
|
13
|
+
bottom: "0",
|
|
14
|
+
|
|
15
|
+
width: "100vw",
|
|
16
|
+
height: "100vh",
|
|
17
|
+
|
|
12
18
|
border: "1px solid #000",
|
|
13
19
|
display: "flex",
|
|
14
20
|
justifyContent: "center",
|
|
@@ -16,7 +22,7 @@ export function generateContainer(parentId,w,h) {
|
|
|
16
22
|
overflow: "hidden"
|
|
17
23
|
});
|
|
18
24
|
document.getElementById(parentId).appendChild(container);
|
|
19
|
-
gridTemplate("runlab-container"
|
|
25
|
+
gridTemplate("runlab-container");
|
|
20
26
|
createEditor("runlab-editor","");
|
|
21
27
|
|
|
22
28
|
// View and Editor toggle
|
|
@@ -202,7 +208,7 @@ export function setEditorActive(status = true){
|
|
|
202
208
|
//================================ SVG Warning Text ================================
|
|
203
209
|
|
|
204
210
|
const warningText = document.createElement("p");
|
|
205
|
-
warningText.textContent = "No
|
|
211
|
+
warningText.textContent = "No files selected.";
|
|
206
212
|
warningText.style.color = "#aaa";
|
|
207
213
|
warningText.style.fontSize = "0.9em";
|
|
208
214
|
warningText.style.margin = "0";
|
|
@@ -253,4 +259,12 @@ export function getFeExecutables() {
|
|
|
253
259
|
|
|
254
260
|
export function getFileNode(file) {
|
|
255
261
|
return feNode.getFileNode(file);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function getNodeFromPath(path, currentPath) {
|
|
265
|
+
return feNode.getNodeFromPath(path, currentPath);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function setMoveNode(node, destNode) {
|
|
269
|
+
feNode.setMoveNode(node, destNode);
|
|
256
270
|
}
|
package/dist/app/core/navbar.js
CHANGED
|
@@ -34,7 +34,7 @@ export function createNavbar(parentId) {
|
|
|
34
34
|
});
|
|
35
35
|
ul.appendChild(leftGroup);
|
|
36
36
|
|
|
37
|
-
const menuItems = ["Run", "Help"];
|
|
37
|
+
const menuItems = ["✕", "🗗","Run", "Help"];
|
|
38
38
|
menuItems.forEach(item => {
|
|
39
39
|
const li = document.createElement("li");
|
|
40
40
|
li.textContent = item;
|
|
@@ -46,6 +46,36 @@ export function createNavbar(parentId) {
|
|
|
46
46
|
cursor: "pointer",
|
|
47
47
|
color: "#fff"
|
|
48
48
|
});
|
|
49
|
+
if (item === "✕") {
|
|
50
|
+
li.addEventListener("click", () => {
|
|
51
|
+
const app = document.getElementById("runlab-custom-div");
|
|
52
|
+
app.style.display = "none";
|
|
53
|
+
document.getElementById("runlab-custom-btn").textContent = "Show App";
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (item === "🗗"){
|
|
57
|
+
li.addEventListener("click", () => {
|
|
58
|
+
const container = document.getElementById("runlab-container");
|
|
59
|
+
let isMaximized = container.style.height === "100vh"? false: true;
|
|
60
|
+
|
|
61
|
+
if(isMaximized){
|
|
62
|
+
container.style.height = "100vh";
|
|
63
|
+
container.style.width = "100vw";
|
|
64
|
+
container.style.left = "0";
|
|
65
|
+
container.style.top = "0";
|
|
66
|
+
container.style.bottom = "0";
|
|
67
|
+
container.style.transform = "none";
|
|
68
|
+
}
|
|
69
|
+
if(!isMaximized){
|
|
70
|
+
container.style.height = "70vh";
|
|
71
|
+
container.style.width = "70vw";
|
|
72
|
+
container.style.left = "50%";
|
|
73
|
+
container.style.top = "auto";
|
|
74
|
+
container.style.bottom = "0";
|
|
75
|
+
container.style.transform = "translateX(-50%)";
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
49
79
|
if (item === "Run") {
|
|
50
80
|
li.addEventListener("click", () => {
|
|
51
81
|
sendCode(getCurrentCode(),getCurrentExtension());
|
|
@@ -73,7 +103,9 @@ export function createNavbar(parentId) {
|
|
|
73
103
|
<li><span style="font-weight: bold;">pwd</span>: Print current directory</li>
|
|
74
104
|
<li><span style="font-weight: bold;">run</span>: Run a file (e.g., run script.js)</li>
|
|
75
105
|
<li><span style="font-weight: bold;">help | h</span>: Show help message</li>
|
|
106
|
+
<li><span style="font-weight: bold;">mv</span>: Move a file or folder (e.g., mv folder1/script.js folder2)</li>
|
|
76
107
|
</ul>
|
|
108
|
+
<p>If the path starts with / it will be considered absolute (from root), otherwise it will be considered relative to the current path.</p>
|
|
77
109
|
`,
|
|
78
110
|
width: 700,
|
|
79
111
|
confirmButtonText: "Got it!"
|
|
@@ -11,6 +11,7 @@ export class FileExplorer {
|
|
|
11
11
|
this.execFiles = [];
|
|
12
12
|
this.filesPath = [];
|
|
13
13
|
this.path = [];
|
|
14
|
+
this.currentNodeDragged = null;
|
|
14
15
|
this.render();
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -42,6 +43,106 @@ export class FileExplorer {
|
|
|
42
43
|
this.execFiles = files;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
getNodeFromPath(path, currentPath) {
|
|
47
|
+
|
|
48
|
+
if (path === "/") {
|
|
49
|
+
return this.root;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (path.includes("..")) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// Absolute path
|
|
56
|
+
if (path.startsWith("/")) {
|
|
57
|
+
path = path.slice(1);
|
|
58
|
+
let currentNode = this.root;
|
|
59
|
+
|
|
60
|
+
if (!path.includes("/")) {
|
|
61
|
+
if (path.includes(".")) { //File
|
|
62
|
+
const [name, ext] = path.split(".");
|
|
63
|
+
return currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
64
|
+
}
|
|
65
|
+
return currentNode.children.find(c => c.name === path);//Folder
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let pathList = path.split("/").filter(p => p);
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < pathList.length; i++) {
|
|
71
|
+
const part = pathList[i];
|
|
72
|
+
|
|
73
|
+
if (part.includes(".")) {
|
|
74
|
+
const [name, ext] = part.split(".");
|
|
75
|
+
const nextNode = currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
76
|
+
if (!nextNode) return null;
|
|
77
|
+
currentNode = nextNode;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!part.includes(".")) {
|
|
81
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
82
|
+
if (!nextNode) return null;
|
|
83
|
+
currentNode = nextNode;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
return currentNode;
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
// Relative path
|
|
91
|
+
if (!path.startsWith("/")) {
|
|
92
|
+
let currentNode = this.root;
|
|
93
|
+
|
|
94
|
+
if (currentPath.length > 1) {
|
|
95
|
+
for (let i = 1; i < currentPath.length; i++) {
|
|
96
|
+
const part = currentPath[i];
|
|
97
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
98
|
+
if (!nextNode) return null;
|
|
99
|
+
currentNode = nextNode;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!path.includes("/")) {
|
|
104
|
+
if (path.includes(".")) { //File
|
|
105
|
+
const [name, ext] = path.split(".");
|
|
106
|
+
return currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
107
|
+
}
|
|
108
|
+
return currentNode.children.find(c => c.name === path);//Folder
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let pathList = path.split("/").filter(p => p);
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < pathList.length; i++) {
|
|
114
|
+
const part = pathList[i];
|
|
115
|
+
|
|
116
|
+
if (part.includes(".")) {
|
|
117
|
+
const [name, ext] = part.split(".");
|
|
118
|
+
const nextNode = currentNode.children.find(c => c.name === name && c.ext === ext);
|
|
119
|
+
if (!nextNode) return null;
|
|
120
|
+
currentNode = nextNode;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!part.includes(".")) {
|
|
124
|
+
const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
|
|
125
|
+
if (!nextNode) return null;
|
|
126
|
+
currentNode = nextNode;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
return currentNode;
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setMoveNode(node, destNode) {
|
|
137
|
+
if (node === this.root) return;
|
|
138
|
+
if (node instanceof File) {
|
|
139
|
+
this.moveFile(node, destNode);
|
|
140
|
+
}
|
|
141
|
+
if (node instanceof Folder) {
|
|
142
|
+
this.moveFolder(node, destNode);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
45
146
|
/* =========================
|
|
46
147
|
RENDER
|
|
47
148
|
========================== */
|
|
@@ -61,6 +162,19 @@ export class FileExplorer {
|
|
|
61
162
|
this.showRootMenu(e);
|
|
62
163
|
};
|
|
63
164
|
|
|
165
|
+
rootDiv.addEventListener("dragover", (e) => {
|
|
166
|
+
rootDiv.style.border = "1px solid #117bd1";
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
});
|
|
169
|
+
rootDiv.addEventListener("dragleave", (e) => {
|
|
170
|
+
rootDiv.style.border = "none";
|
|
171
|
+
});
|
|
172
|
+
rootDiv.addEventListener("drop", (e) => {
|
|
173
|
+
rootDiv.style.border = "none";
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
this.setMoveNode(this.currentNodeDragged, this.root);
|
|
176
|
+
this.currentNodeDragged = null;
|
|
177
|
+
});
|
|
64
178
|
|
|
65
179
|
this.renderFolder(this.root, rootDiv);
|
|
66
180
|
|
|
@@ -70,6 +184,8 @@ export class FileExplorer {
|
|
|
70
184
|
renderFolder(folder, parentUl) {
|
|
71
185
|
folder.children.forEach(node => {
|
|
72
186
|
if (node instanceof Folder) {
|
|
187
|
+
|
|
188
|
+
/* ============ BUTTON =============== */
|
|
73
189
|
const btn = document.createElement("button");
|
|
74
190
|
btn.textContent = `📁 ${node.name}`;
|
|
75
191
|
btn.style.width = "100%";
|
|
@@ -80,18 +196,18 @@ export class FileExplorer {
|
|
|
80
196
|
btn.style.paddingTop = "8px";
|
|
81
197
|
btn.style.paddingBottom = "8px";
|
|
82
198
|
btn.style.cursor = "pointer";
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
btn.onmouseover = () => btn.style.backgroundColor = "#555";
|
|
87
|
-
btn.onmouseout = () => btn.style.backgroundColor = "transparent";
|
|
88
|
-
|
|
199
|
+
btn.draggable = true;
|
|
200
|
+
/* ============ UL =============== */
|
|
89
201
|
const ul = document.createElement("ul");
|
|
90
202
|
ul.style.display = "none";
|
|
91
203
|
ul.style.listStyle = "none";
|
|
92
204
|
ul.style.margin = "0";
|
|
93
205
|
ul.style.padding = "0 0 0 24px";
|
|
94
|
-
|
|
206
|
+
/* ============ DIV =============== */
|
|
207
|
+
const div = document.createElement("div");
|
|
208
|
+
/* ============ EVENTS =============== */
|
|
209
|
+
btn.onmouseover = () => btn.style.backgroundColor = "#555";
|
|
210
|
+
btn.onmouseout = () => btn.style.backgroundColor = "transparent";
|
|
95
211
|
|
|
96
212
|
btn.onclick = () => this.toggle(ul);
|
|
97
213
|
btn.oncontextmenu = e => {
|
|
@@ -100,11 +216,46 @@ export class FileExplorer {
|
|
|
100
216
|
this.showFolderMenu(e, node);
|
|
101
217
|
};
|
|
102
218
|
|
|
103
|
-
|
|
219
|
+
btn.addEventListener("dragstart", (e) => {
|
|
220
|
+
|
|
221
|
+
this.currentNodeDragged = node;
|
|
222
|
+
});
|
|
223
|
+
btn.addEventListener("dragover", (e) => {
|
|
224
|
+
div.style.border = "1px solid #117bd1";
|
|
225
|
+
e.preventDefault();
|
|
226
|
+
});
|
|
227
|
+
btn.addEventListener("dragleave", (e) => {
|
|
228
|
+
div.style.border = "none";
|
|
229
|
+
});
|
|
230
|
+
btn.addEventListener("drop", (e) => {
|
|
231
|
+
div.style.border = "none";
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
this.setMoveNode(this.currentNodeDragged, node);
|
|
234
|
+
this.currentNodeDragged = null;
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
ul.addEventListener("dragover", (e) => {
|
|
238
|
+
div.style.border = "1px solid #117bd1";
|
|
239
|
+
e.preventDefault();
|
|
240
|
+
});
|
|
241
|
+
ul.addEventListener("dragleave", (e) => {
|
|
242
|
+
div.style.border = "none";
|
|
243
|
+
});
|
|
244
|
+
ul.addEventListener("drop", (e) => {
|
|
245
|
+
div.style.border = "none";
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
this.setMoveNode(this.currentNodeDragged, node);
|
|
248
|
+
this.currentNodeDragged = null;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/* ============ APPEND =============== */
|
|
252
|
+
div.append(btn, ul);
|
|
253
|
+
parentUl.append(div);
|
|
104
254
|
this.renderFolder(node, ul);
|
|
105
255
|
}
|
|
106
256
|
|
|
107
257
|
if (node instanceof File) {
|
|
258
|
+
/* ============ LI =============== */
|
|
108
259
|
const li = document.createElement("li");
|
|
109
260
|
li.textContent = `📄 ${node.name}.${node.ext}`;
|
|
110
261
|
li.style.width = "100%";
|
|
@@ -115,8 +266,8 @@ export class FileExplorer {
|
|
|
115
266
|
li.style.paddingTop = "8px";
|
|
116
267
|
li.style.paddingBottom = "8px";
|
|
117
268
|
li.style.cursor = "pointer";
|
|
118
|
-
|
|
119
|
-
|
|
269
|
+
li.draggable = true;
|
|
270
|
+
/* ============ EVENTS =============== */
|
|
120
271
|
li.onclick = () => {
|
|
121
272
|
setActiveFile(node);
|
|
122
273
|
setViewActive(false);
|
|
@@ -130,6 +281,23 @@ export class FileExplorer {
|
|
|
130
281
|
this.showFileMenu(e, node);
|
|
131
282
|
};
|
|
132
283
|
|
|
284
|
+
li.addEventListener("dragstart", (e) => {
|
|
285
|
+
this.currentNodeDragged = node;
|
|
286
|
+
});
|
|
287
|
+
li.addEventListener("dragover", (e) => {
|
|
288
|
+
parentUl.parentElement.style.border = "1px solid #117bd1";
|
|
289
|
+
e.preventDefault();
|
|
290
|
+
});
|
|
291
|
+
li.addEventListener("dragleave", (e) => {
|
|
292
|
+
parentUl.parentElement.style.border = "none";
|
|
293
|
+
});
|
|
294
|
+
li.addEventListener("drop", (e) => {
|
|
295
|
+
parentUl.parentElement.style.border = "none";
|
|
296
|
+
e.preventDefault();
|
|
297
|
+
this.setMoveNode(this.currentNodeDragged, node.parent);
|
|
298
|
+
this.currentNodeDragged = null;
|
|
299
|
+
});
|
|
300
|
+
/* ============ APPEND =============== */
|
|
133
301
|
parentUl.appendChild(li);
|
|
134
302
|
this.filesPath.push({ name: `${node.name}.${node.ext}`, path: node.parent.path });
|
|
135
303
|
|
|
@@ -168,6 +336,29 @@ export class FileExplorer {
|
|
|
168
336
|
this.render();
|
|
169
337
|
}
|
|
170
338
|
|
|
339
|
+
moveFile(node, newParent) {
|
|
340
|
+
if (newParent === node) return;
|
|
341
|
+
if (newParent instanceof File) return;
|
|
342
|
+
if (node === this.root) return;
|
|
343
|
+
const oldParent = node.parent;
|
|
344
|
+
oldParent.children = oldParent.children.filter(c => c !== node);
|
|
345
|
+
newParent.children.push(node);
|
|
346
|
+
node.parent = newParent;
|
|
347
|
+
this.render();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
moveFolder(node, newParent) {
|
|
351
|
+
if (newParent === node) return;
|
|
352
|
+
if (newParent instanceof File) return;
|
|
353
|
+
if (node === this.root) return;
|
|
354
|
+
const oldParent = node.parent;
|
|
355
|
+
oldParent.children = oldParent.children.filter(c => c !== node);
|
|
356
|
+
newParent.children.push(node);
|
|
357
|
+
node.parent = newParent;
|
|
358
|
+
node.path = [...newParent.path, node.name];
|
|
359
|
+
this.render();
|
|
360
|
+
}
|
|
361
|
+
|
|
171
362
|
/* =========================
|
|
172
363
|
MENUS
|
|
173
364
|
========================== */
|
|
@@ -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,5 +1,6 @@
|
|
|
1
1
|
import { generateContainer } from "./app/app.js";
|
|
2
2
|
import { appendViewContent } from "./app/app.js";
|
|
3
|
+
import { ReactRunlabButton } from "./reactButton.jsx";
|
|
3
4
|
|
|
4
5
|
let runtimeUrl = "";
|
|
5
6
|
function setRuntimeUrl(url) {
|
|
@@ -11,11 +12,9 @@ function getRuntimeUrl() {
|
|
|
11
12
|
|
|
12
13
|
export async function run({
|
|
13
14
|
parentId,
|
|
14
|
-
width = 1200,
|
|
15
|
-
height = 800,
|
|
16
15
|
runtimeUrl = ""
|
|
17
16
|
}) {
|
|
18
|
-
generateContainer(parentId
|
|
17
|
+
generateContainer(parentId);
|
|
19
18
|
setRuntimeUrl(runtimeUrl);
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -47,3 +46,7 @@ export async function sendCode(code, ext = "txt") {
|
|
|
47
46
|
console.error(err);
|
|
48
47
|
}
|
|
49
48
|
}
|
|
49
|
+
|
|
50
|
+
//=============================== Buttons ===============================
|
|
51
|
+
|
|
52
|
+
export { ReactRunlabButton };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { run } from "./index.js";
|
|
3
|
+
|
|
4
|
+
export function ReactRunlabButton(props = { runConfig: {} }) {
|
|
5
|
+
const { runConfig } = props;
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (typeof window !== 'undefined' && window.__runlab_started) return;
|
|
9
|
+
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
11
|
+
window.__runlab_started = true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run(runConfig);
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
const showApp = () => {
|
|
18
|
+
const app = document.getElementById("runlab-custom-div");
|
|
19
|
+
const btn = document.getElementById("runlab-custom-btn");
|
|
20
|
+
|
|
21
|
+
const displayStyle = window.getComputedStyle(app).display;
|
|
22
|
+
|
|
23
|
+
if (displayStyle === "none") {
|
|
24
|
+
app.style.display = "block";
|
|
25
|
+
btn.textContent = "Hide App";
|
|
26
|
+
}
|
|
27
|
+
if (displayStyle === "block") {
|
|
28
|
+
app.style.display = "none";
|
|
29
|
+
btn.textContent = "Show App";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<button
|
|
36
|
+
id="runlab-custom-btn"
|
|
37
|
+
onClick={showApp}
|
|
38
|
+
className="show-app-button"
|
|
39
|
+
>
|
|
40
|
+
Show App
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
<div id="runlab-custom-div" style={{ display: "none" }}></div>
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
}
|