runlab 0.1.0 → 0.1.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 +80 -0
- package/dist/app/core/editor.js +108 -0
- package/dist/app/core/navbar.js +35 -0
- package/dist/app/fileExplorer/FSNode.js +10 -0
- package/dist/app/fileExplorer/File.js +12 -0
- package/dist/app/fileExplorer/FileExplorer.js +322 -0
- package/dist/app/fileExplorer/Folder.js +11 -0
- package/dist/index.js +19 -9
- package/package.json +37 -10
- /package/dist/{runlab.wasm → wasm/runlab.wasm} +0 -0
- /package/dist/{wasm_exec.js → wasm/wasm_exec.js} +0 -0
package/dist/app/app.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createEditor } from "./core/editor.js";
|
|
2
|
+
import { createNavbar } from "./core/navbar.js";
|
|
3
|
+
import { FileExplorer } from "./fileExplorer/FileExplorer.js";
|
|
4
|
+
|
|
5
|
+
export function generateContainer(parentId,w,h) {
|
|
6
|
+
const container = document.createElement("div");
|
|
7
|
+
container.id = "runlab-container";
|
|
8
|
+
Object.assign(container.style, {
|
|
9
|
+
width: w + "px",
|
|
10
|
+
height: h + "px",
|
|
11
|
+
border: "1px solid #000",
|
|
12
|
+
display: "flex",
|
|
13
|
+
justifyContent: "center",
|
|
14
|
+
alignItems: "center",
|
|
15
|
+
overflow: "hidden"
|
|
16
|
+
});
|
|
17
|
+
document.getElementById(parentId).appendChild(container);
|
|
18
|
+
gridTemplate("runlab-container", w, h);
|
|
19
|
+
createNavbar("runlab-navbar");
|
|
20
|
+
createEditor("runlab-editor","");
|
|
21
|
+
|
|
22
|
+
new FileExplorer("runlab-file-explorer");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function gridTemplate(parent) {
|
|
26
|
+
const grid = document.createElement('div');
|
|
27
|
+
grid.id = "runlab-grid";
|
|
28
|
+
Object.assign(grid.style, {
|
|
29
|
+
display: 'grid',
|
|
30
|
+
width: "100%",
|
|
31
|
+
height: "100%",
|
|
32
|
+
gridTemplateColumns: "1fr 4fr", // coluna esquerda menor
|
|
33
|
+
gridTemplateRows: "0.15fr 2.5fr 1fr", // top maior, bottom menor
|
|
34
|
+
gap: "2px",
|
|
35
|
+
padding: "2px",
|
|
36
|
+
boxSizing: "border-box",
|
|
37
|
+
backgroundColor: "#303030"
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const navbar = document.createElement("div");
|
|
41
|
+
navbar.id = "runlab-navbar";
|
|
42
|
+
Object.assign(navbar.style, {
|
|
43
|
+
gridColumn: "1 / 3", // ocupa as duas colunas
|
|
44
|
+
gridRow: "1 / 2", // fica na linha 1
|
|
45
|
+
border: "1px solid #09090b"
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
//Left
|
|
49
|
+
const fileExplorer = document.createElement("div");
|
|
50
|
+
fileExplorer.id = "runlab-file-explorer";
|
|
51
|
+
Object.assign(fileExplorer.style, {
|
|
52
|
+
gridColumn: "1 / 2",
|
|
53
|
+
gridRow: "2 / 4", // ocupa a linha 2 e 3
|
|
54
|
+
border: "1px solid #09090b"
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
//Right Top
|
|
58
|
+
const editor = document.createElement("div");
|
|
59
|
+
editor.id = "runlab-editor";
|
|
60
|
+
Object.assign(editor.style, {
|
|
61
|
+
gridColumn: "2 / 3",
|
|
62
|
+
gridRow: "2 / 3", // linha logo após a navbar
|
|
63
|
+
border: "1px solid #09090b"
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Right Bottom
|
|
67
|
+
const terminal = document.createElement("div");
|
|
68
|
+
terminal.id = "runlab-terminal";
|
|
69
|
+
Object.assign(terminal.style, {
|
|
70
|
+
gridColumn: "2 / 3",
|
|
71
|
+
gridRow: "3 / 4", // última linha
|
|
72
|
+
border: "1px solid #09090b"
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
grid.appendChild(navbar);
|
|
76
|
+
grid.appendChild(fileExplorer);
|
|
77
|
+
grid.appendChild(editor);
|
|
78
|
+
grid.appendChild(terminal);
|
|
79
|
+
document.getElementById(parent).appendChild(grid);
|
|
80
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Core CM6
|
|
2
|
+
import { EditorState } from "@codemirror/state";
|
|
3
|
+
import { EditorView, keymap } from "@codemirror/view";
|
|
4
|
+
import { Compartment } from "@codemirror/state";
|
|
5
|
+
|
|
6
|
+
// Language
|
|
7
|
+
import { javascript } from "@codemirror/lang-javascript";
|
|
8
|
+
import { html } from "@codemirror/lang-html";
|
|
9
|
+
import { css } from "@codemirror/lang-css";
|
|
10
|
+
import { json } from "@codemirror/lang-json";
|
|
11
|
+
import { markdown } from "@codemirror/lang-markdown";
|
|
12
|
+
import { xml } from "@codemirror/lang-xml";
|
|
13
|
+
import { yaml } from "@codemirror/lang-yaml";
|
|
14
|
+
import { go } from "@codemirror/lang-go";
|
|
15
|
+
|
|
16
|
+
// VS Code look & feel
|
|
17
|
+
import { vscodeDark } from "@uiw/codemirror-theme-vscode";
|
|
18
|
+
import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
|
|
19
|
+
|
|
20
|
+
// IDE features
|
|
21
|
+
import { autocompletion, closeBrackets } from "@codemirror/autocomplete";
|
|
22
|
+
import { foldGutter, codeFolding } from "@codemirror/language";
|
|
23
|
+
|
|
24
|
+
const editors = new Map();
|
|
25
|
+
|
|
26
|
+
let activeFile = null;
|
|
27
|
+
|
|
28
|
+
export const languageCompartment = new Compartment();
|
|
29
|
+
|
|
30
|
+
export function setActiveFile(file) {
|
|
31
|
+
activeFile = file;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getActiveFile() {
|
|
35
|
+
return activeFile;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function languageFromExtension(ext) {
|
|
39
|
+
switch (ext) {
|
|
40
|
+
case "txt": return [];
|
|
41
|
+
case "md": return markdown();
|
|
42
|
+
case "json": return json();
|
|
43
|
+
case "yaml": return yaml();
|
|
44
|
+
case "yml": return yaml();
|
|
45
|
+
case "toml":return [];
|
|
46
|
+
case "html":return html();
|
|
47
|
+
case "xml":return xml();
|
|
48
|
+
case "css":return css();
|
|
49
|
+
case "js":return javascript({ typescript: false });
|
|
50
|
+
case "ts":return javascript({ typescript: true });
|
|
51
|
+
case "go":return go();
|
|
52
|
+
default:return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export function createEditor(parentId, initialCode = "") {
|
|
58
|
+
const parent = document.getElementById(parentId);
|
|
59
|
+
if (!parent) throw new Error(`Elemento #${parentId} não encontrado`);
|
|
60
|
+
|
|
61
|
+
const state = EditorState.create({
|
|
62
|
+
doc: initialCode,
|
|
63
|
+
extensions: [
|
|
64
|
+
keymap.of(vscodeKeymap),
|
|
65
|
+
vscodeDark,
|
|
66
|
+
languageCompartment.of(languageFromExtension(getActiveFile() ? getActiveFile().ext : "")),
|
|
67
|
+
autocompletion(),
|
|
68
|
+
closeBrackets(),
|
|
69
|
+
codeFolding(),
|
|
70
|
+
foldGutter(),
|
|
71
|
+
EditorView.updateListener.of(update => {
|
|
72
|
+
if (update.docChanged) {
|
|
73
|
+
const file = getActiveFile();
|
|
74
|
+
if (file) {
|
|
75
|
+
file.content = update.state.doc.toString();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const view = new EditorView({
|
|
83
|
+
state,
|
|
84
|
+
parent
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
editors.set(parentId, view);
|
|
88
|
+
return view;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function updateEditorContentById(parentId, content) {
|
|
92
|
+
const view = editors.get(parentId);
|
|
93
|
+
if (!view) {
|
|
94
|
+
console.warn(`Editor ${parentId} não inicializado`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const langFactory = languageFromExtension(getActiveFile() ? getActiveFile().ext : "");
|
|
99
|
+
|
|
100
|
+
view.dispatch({
|
|
101
|
+
effects: languageCompartment.reconfigure(langFactory),
|
|
102
|
+
changes: {
|
|
103
|
+
from: 0,
|
|
104
|
+
to: view.state.doc.length,
|
|
105
|
+
insert: content
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export function createNavbar(parentId) {
|
|
4
|
+
const navbar = document.createElement("nav");
|
|
5
|
+
navbar.id = "runlab-navbar-div";
|
|
6
|
+
Object.assign(navbar.style, {
|
|
7
|
+
width: "100%",
|
|
8
|
+
height: "100%"
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const ul = document.createElement("ul");
|
|
12
|
+
Object.assign(ul.style, {
|
|
13
|
+
listStyleType: "none",
|
|
14
|
+
margin: "0",
|
|
15
|
+
padding: "0",
|
|
16
|
+
display: "flex"
|
|
17
|
+
});
|
|
18
|
+
navbar.appendChild(ul);
|
|
19
|
+
|
|
20
|
+
const menuItems = ["File", "Edit", "View", "Help"];
|
|
21
|
+
menuItems.forEach(item => {
|
|
22
|
+
const li = document.createElement("li");
|
|
23
|
+
li.textContent = item;
|
|
24
|
+
ul.appendChild(li);
|
|
25
|
+
Object.assign(li.style, {
|
|
26
|
+
marginRight: "20px",
|
|
27
|
+
padding: "10px 14px",
|
|
28
|
+
cursor: "pointer",
|
|
29
|
+
color: "#fff",
|
|
30
|
+
textDecoration: "none"
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
document.getElementById(parentId).appendChild(navbar);
|
|
35
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { Folder } from "./Folder.js";
|
|
2
|
+
import { File } from "./File.js";
|
|
3
|
+
import { updateEditorContentById, setActiveFile } from "../core/editor.js";
|
|
4
|
+
import Swal from "sweetalert2";
|
|
5
|
+
|
|
6
|
+
export class FileExplorer {
|
|
7
|
+
constructor(parentId) {
|
|
8
|
+
this.container = document.getElementById(parentId);
|
|
9
|
+
this.root = new Folder("root", null);
|
|
10
|
+
|
|
11
|
+
this.render();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* =========================
|
|
15
|
+
RENDER
|
|
16
|
+
========================== */
|
|
17
|
+
|
|
18
|
+
render() {
|
|
19
|
+
this.container.innerHTML = "";
|
|
20
|
+
|
|
21
|
+
const rootDiv = document.createElement("div");
|
|
22
|
+
rootDiv.style.width = "100%";
|
|
23
|
+
rootDiv.style.height = "100%";
|
|
24
|
+
rootDiv.style.display = "block";
|
|
25
|
+
rootDiv.style.pointerEvents = "auto";
|
|
26
|
+
rootDiv.style.position = "relative";
|
|
27
|
+
|
|
28
|
+
rootDiv.oncontextmenu = e => {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
this.showRootMenu(e);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
this.renderFolder(this.root, rootDiv);
|
|
35
|
+
|
|
36
|
+
this.container.append(rootDiv);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
renderFolder(folder, parentUl) {
|
|
40
|
+
folder.children.forEach(node => {
|
|
41
|
+
if (node instanceof Folder) {
|
|
42
|
+
const btn = document.createElement("button");
|
|
43
|
+
btn.textContent = `📁 ${node.name}`;
|
|
44
|
+
btn.style.width = "100%";
|
|
45
|
+
btn.style.textAlign = "left";
|
|
46
|
+
btn.style.border = "none";
|
|
47
|
+
btn.style.background = "none";
|
|
48
|
+
btn.style.color = "white";
|
|
49
|
+
btn.style.paddingTop = "8px";
|
|
50
|
+
btn.style.paddingBottom = "8px";
|
|
51
|
+
btn.style.cursor = "pointer";
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
btn.onmouseover = () => btn.style.backgroundColor = "#555";
|
|
55
|
+
btn.onmouseout = () => btn.style.backgroundColor = "transparent";
|
|
56
|
+
|
|
57
|
+
const ul = document.createElement("ul");
|
|
58
|
+
ul.style.display = "none";
|
|
59
|
+
|
|
60
|
+
btn.onclick = () => this.toggle(ul);
|
|
61
|
+
btn.oncontextmenu = e => {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
this.showFolderMenu(e, node);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
parentUl.append(btn, ul);
|
|
68
|
+
this.renderFolder(node, ul);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (node instanceof File) {
|
|
72
|
+
const li = document.createElement("li");
|
|
73
|
+
li.textContent = `📄 ${node.name}.${node.ext}`;
|
|
74
|
+
li.style.width = "100%";
|
|
75
|
+
li.style.textAlign = "left";
|
|
76
|
+
li.style.border = "none";
|
|
77
|
+
li.style.background = "none";
|
|
78
|
+
li.style.color = "white";
|
|
79
|
+
li.style.paddingTop = "8px";
|
|
80
|
+
li.style.paddingBottom = "8px";
|
|
81
|
+
li.style.cursor = "pointer";
|
|
82
|
+
|
|
83
|
+
li.onclick = () => {
|
|
84
|
+
setActiveFile(node);
|
|
85
|
+
updateEditorContentById("runlab-editor", node.content);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
li.oncontextmenu = e => {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
this.showFileMenu(e, node);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
parentUl.appendChild(li);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
toggle(el) {
|
|
100
|
+
el.style.display = el.style.display === "none" ? "block" : "none";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* =========================
|
|
104
|
+
AÇÕES (MODEL)
|
|
105
|
+
========================== */
|
|
106
|
+
|
|
107
|
+
addFolder(parent, name) {
|
|
108
|
+
parent.children.push(new Folder(name, parent));
|
|
109
|
+
this.render();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
addFile(parent, name, ext=".txt") {
|
|
113
|
+
parent.children.push(new File(name, parent, ext));
|
|
114
|
+
this.render();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
rename(node, newName) {
|
|
118
|
+
node.name = newName;
|
|
119
|
+
this.render();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
delete(node) {
|
|
123
|
+
const parent = node.parent;
|
|
124
|
+
parent.children = parent.children.filter(c => c !== node);
|
|
125
|
+
this.render();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* =========================
|
|
129
|
+
MENUS
|
|
130
|
+
========================== */
|
|
131
|
+
|
|
132
|
+
showRootMenu(e) {
|
|
133
|
+
const menu = this.createMenu([
|
|
134
|
+
{
|
|
135
|
+
label: "New Folder",
|
|
136
|
+
action: () => Swal.fire({
|
|
137
|
+
title: 'Folder name',
|
|
138
|
+
input: 'text',
|
|
139
|
+
showCancelButton: true,
|
|
140
|
+
confirmButtonText: 'Create',
|
|
141
|
+
preConfirm: (name) => {
|
|
142
|
+
if (name) {
|
|
143
|
+
this.addFolder(this.root, name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
label: "New File",
|
|
150
|
+
action: () => Swal.fire({
|
|
151
|
+
title: 'Choose extension and filename',
|
|
152
|
+
html:
|
|
153
|
+
'<select id="swal-select" class="swal2-input">' +
|
|
154
|
+
'<option value="txt">.txt - Text</option>' +
|
|
155
|
+
'<option value="md">.md - Text</option>' +
|
|
156
|
+
'<option value="json">.json - Data</option>' +
|
|
157
|
+
'<option value="yaml">.yaml - Data</option>' +
|
|
158
|
+
'<option value="toml">.toml - Data</option>' +
|
|
159
|
+
'<option value="html">.html - Markup</option>' +
|
|
160
|
+
'<option value="xml">.xml - Markup</option>' +
|
|
161
|
+
'<option value="css">.css - Style</option>' +
|
|
162
|
+
'<option value="js">.js - JavaScript</option>' +
|
|
163
|
+
'<option value="ts">.ts - TypeScript</option>' +
|
|
164
|
+
'<option value="go">.go - GO</option>' +
|
|
165
|
+
'</select>' +
|
|
166
|
+
'<input id="swal-input" class="swal2-input" placeholder="filename">',
|
|
167
|
+
focusConfirm: false,
|
|
168
|
+
preConfirm: () => {
|
|
169
|
+
return [
|
|
170
|
+
document.getElementById('swal-select').value,
|
|
171
|
+
document.getElementById('swal-input').value
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
}).then((result) => {
|
|
175
|
+
if (result.value) {
|
|
176
|
+
const [selection, text] = result.value;
|
|
177
|
+
this.addFile(this.root, text, selection);
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
this.openMenu(e, menu);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
showFolderMenu(e, folder) {
|
|
187
|
+
const menu = this.createMenu([
|
|
188
|
+
{ label: "New Folder", action: () => Swal.fire({
|
|
189
|
+
title: 'Folder name',
|
|
190
|
+
input: 'text',
|
|
191
|
+
showCancelButton: true,
|
|
192
|
+
confirmButtonText: 'Create',
|
|
193
|
+
preConfirm: (name) => {
|
|
194
|
+
if (name) {
|
|
195
|
+
this.addFolder(folder, name);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
})},
|
|
199
|
+
{ label: "New File", action: () => Swal.fire({
|
|
200
|
+
title: 'Choose extension and filename',
|
|
201
|
+
html:
|
|
202
|
+
'<select id="swal-select" class="swal2-input">' +
|
|
203
|
+
'<option value="txt">.txt - Text</option>' +
|
|
204
|
+
'<option value="md">.md - Text</option>' +
|
|
205
|
+
'<option value="json">.json - Data</option>' +
|
|
206
|
+
'<option value="yaml">.yaml - Data</option>' +
|
|
207
|
+
'<option value="toml">.toml - Data</option>' +
|
|
208
|
+
'<option value="html">.html - Markup</option>' +
|
|
209
|
+
'<option value="xml">.xml - Markup</option>' +
|
|
210
|
+
'<option value="css">.css - Style</option>' +
|
|
211
|
+
'<option value="js">.js - JavaScript</option>' +
|
|
212
|
+
'<option value="ts">.ts - TypeScript</option>' +
|
|
213
|
+
'<option value="go">.go - GO</option>' +
|
|
214
|
+
'</select>' +
|
|
215
|
+
'<input id="swal-input" class="swal2-input" placeholder="filename">',
|
|
216
|
+
focusConfirm: false,
|
|
217
|
+
preConfirm: () => {
|
|
218
|
+
return [
|
|
219
|
+
document.getElementById('swal-select').value,
|
|
220
|
+
document.getElementById('swal-input').value
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
}).then((result) => {
|
|
224
|
+
if (result.value) {
|
|
225
|
+
const [selection, text] = result.value;
|
|
226
|
+
this.addFile(this.root, text, selection);
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
},
|
|
230
|
+
{ label: "Rename", action: () => Swal.fire({
|
|
231
|
+
title: 'New name',
|
|
232
|
+
input: 'text',
|
|
233
|
+
showCancelButton: true,
|
|
234
|
+
confirmButtonText: 'Rename',
|
|
235
|
+
preConfirm: (name) => {
|
|
236
|
+
if (name) {
|
|
237
|
+
this.rename(folder, name);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}) },
|
|
241
|
+
{ label: "Delete", action: () => this.delete(folder) }
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
this.openMenu(e, menu);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
showFileMenu(e, file) {
|
|
248
|
+
const menu = this.createMenu([
|
|
249
|
+
{ label: "Rename", action: () => Swal.fire({
|
|
250
|
+
title: 'New name',
|
|
251
|
+
input: 'text',
|
|
252
|
+
showCancelButton: true,
|
|
253
|
+
confirmButtonText: 'Rename',
|
|
254
|
+
preConfirm: (name) => {
|
|
255
|
+
if (name) {
|
|
256
|
+
this.rename(file, name);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}) },
|
|
260
|
+
{ label: "Delete", action: () => Swal.fire({
|
|
261
|
+
title: '',
|
|
262
|
+
showCancelButton: true,
|
|
263
|
+
confirmButtonText: 'Delete',
|
|
264
|
+
preConfirm: (name) => {
|
|
265
|
+
if (name) {
|
|
266
|
+
this.delete(file);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}) }
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
this.openMenu(e, menu);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* =========================
|
|
276
|
+
HELPERS
|
|
277
|
+
========================== */
|
|
278
|
+
|
|
279
|
+
prompt(label, callback) {
|
|
280
|
+
const value = prompt(label);
|
|
281
|
+
if (value) callback(value);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
createMenu(items) {
|
|
285
|
+
const div = document.createElement("div");
|
|
286
|
+
div.className = "context-menu";
|
|
287
|
+
div.style.position = "absolute";
|
|
288
|
+
div.style.backgroundColor = "#333";
|
|
289
|
+
div.style.border = "1px solid #222";
|
|
290
|
+
div.style.padding = "8px";
|
|
291
|
+
div.style.zIndex = "3000";
|
|
292
|
+
div.style.color = "white";
|
|
293
|
+
div.style.minWidth = "100px";
|
|
294
|
+
|
|
295
|
+
items.forEach(i => {
|
|
296
|
+
const item = document.createElement("div");
|
|
297
|
+
item.textContent = i.label;
|
|
298
|
+
item.style.padding = "4px 8px";
|
|
299
|
+
item.style.cursor = "pointer";
|
|
300
|
+
item.onmouseover = () => item.style.backgroundColor = "#555";
|
|
301
|
+
item.onmouseout = () => item.style.backgroundColor = "transparent";
|
|
302
|
+
item.style.border = "1px solid #444";
|
|
303
|
+
item.onclick = () => {
|
|
304
|
+
i.action();
|
|
305
|
+
div.remove();
|
|
306
|
+
};
|
|
307
|
+
div.appendChild(item);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return div;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
openMenu(e, menu) {
|
|
314
|
+
document.querySelectorAll(".context-menu").forEach(m => m.remove());
|
|
315
|
+
|
|
316
|
+
menu.style.position = "absolute";
|
|
317
|
+
menu.style.top = `${e.clientY}px`;
|
|
318
|
+
menu.style.left = `${e.clientX}px`;
|
|
319
|
+
|
|
320
|
+
document.body.appendChild(menu);
|
|
321
|
+
}
|
|
322
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
import "./wasm_exec.js";
|
|
1
|
+
import "./wasm/wasm_exec.js";
|
|
2
|
+
import wasmUrl from "./wasm/runlab.wasm?url";
|
|
3
|
+
import { generateContainer } from "./app/app.js";
|
|
2
4
|
|
|
3
5
|
const go = new Go();
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
export async function run(wasmPath = "./runlab.wasm") {
|
|
10
|
-
console.log("Carregando WASM:", wasmPath);
|
|
11
|
-
|
|
12
|
-
const response = await fetch(wasmPath);
|
|
7
|
+
export async function run(parentId, width = 1200, height = 800) {
|
|
8
|
+
console.log("WASM URL resolvido:", wasmUrl);
|
|
9
|
+
const response = await fetch(wasmUrl);
|
|
13
10
|
const bytes = await response.arrayBuffer();
|
|
14
11
|
|
|
15
12
|
const result = await WebAssembly.instantiate(bytes, go.importObject);
|
|
13
|
+
generateContainer(parentId, width, height);
|
|
16
14
|
go.run(result.instance);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function updateFileExplorer() {
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function loadTerminal() {
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function loadEditor() {
|
|
26
|
+
|
|
17
27
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runlab",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "dist/index.js",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
|
|
6
8
|
"exports": {
|
|
7
|
-
"
|
|
8
|
-
"./
|
|
9
|
-
"./
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./wasm": "./dist/runlab.wasm",
|
|
11
|
+
"./wasm_exec": "./dist/wasm_exec.js"
|
|
10
12
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
|
|
14
|
+
"files": ["dist/**/*", "LICENSE"],
|
|
15
|
+
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@codemirror/state": "^6",
|
|
18
|
+
"@codemirror/view": "^6",
|
|
19
|
+
"@codemirror/language": "^6",
|
|
20
|
+
"@codemirror/autocomplete": "^6",
|
|
21
|
+
|
|
22
|
+
"@codemirror/lang-javascript": "^6",
|
|
23
|
+
"@codemirror/lang-html": "^6",
|
|
24
|
+
"@codemirror/lang-css": "^6",
|
|
25
|
+
"@codemirror/lang-json": "^6",
|
|
26
|
+
"@codemirror/lang-markdown": "^6",
|
|
27
|
+
"@codemirror/lang-xml": "^6",
|
|
28
|
+
"@codemirror/lang-yaml": "^6",
|
|
29
|
+
"@codemirror/lang-go": "^6",
|
|
30
|
+
|
|
31
|
+
"@uiw/codemirror-theme-vscode": "^4.21.0",
|
|
32
|
+
"@replit/codemirror-vscode-keymap": "^6.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@babel/runtime": "^7.24.0",
|
|
36
|
+
"sweetalert2": "^11.10.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@uiw/codemirror-theme-vscode": "^4.21.0",
|
|
40
|
+
"@replit/codemirror-vscode-keymap": "^6.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
File without changes
|
|
File without changes
|