@makano/rew 1.2.1 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +3 -0
- package/bin/rew +1 -8
- package/lib/rew/cli/cli.js +48 -50
- package/lib/rew/cli/run.js +4 -11
- package/lib/rew/cli/utils.js +136 -51
- package/lib/rew/const/default.js +6 -1
- package/lib/rew/const/files.js +3 -9
- package/lib/rew/const/pre-exec.js +3 -0
- package/lib/rew/functions/import.js +4 -3
- package/lib/rew/functions/misc.js +5 -0
- package/lib/rew/functions/sleep.js +1 -1
- package/lib/rew/functions/stdout.js +7 -4
- package/lib/rew/functions/wait.js +11 -0
- package/lib/rew/modules/compiler.js +9 -7
- package/lib/rew/modules/context.js +1 -1
- package/lib/rew/modules/runtime.js +21 -2
- package/lib/rew/pkgs/conf.js +8 -1
- package/lib/rew/pkgs/rune.js +12 -6
- package/main.js +13 -0
- package/package.json +5 -6
- package/bin/ui +0 -0
- package/bin/ui_ws +0 -0
- package/bin/webkit_app +0 -0
- package/build.sh +0 -8
- package/cpp/ui.cpp +0 -217
- package/cpp/ui1.cpp +0 -101
- package/cpp/ui2.cpp +0 -105
- package/lib/rew/css/theme.css +0 -3
- package/lib/rew/html/ui.html +0 -18
- package/lib/rew/html/ui.js +0 -245
- package/lib/rew/pkgs/modules/ui/classes.js +0 -184
- package/lib/rew/pkgs/ui.js +0 -157
- package/meson.build +0 -13
package/cpp/ui2.cpp
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
#include <gtk/gtk.h>
|
2
|
-
#include <libwebsockets.h>
|
3
|
-
#include <json/json.h>
|
4
|
-
#include <iostream>
|
5
|
-
#include <string>
|
6
|
-
#include <map>
|
7
|
-
|
8
|
-
std::map<std::string, GtkWidget *> widgets;
|
9
|
-
|
10
|
-
static void on_button_clicked(GtkWidget *widget, gpointer data)
|
11
|
-
{
|
12
|
-
// Send event back to Node.js server
|
13
|
-
std::string id = static_cast<char *>(data);
|
14
|
-
std::string message = "{\"event\": \"buttonClicked\", \"id\": \"" + id + "\"}";
|
15
|
-
lws_write(static_cast<lws *>(data), (unsigned char *)message.c_str(), message.length(), LWS_WRITE_TEXT);
|
16
|
-
}
|
17
|
-
|
18
|
-
static int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
|
19
|
-
{
|
20
|
-
switch (reason)
|
21
|
-
{
|
22
|
-
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
23
|
-
std::cout << "Connected to server" << std::endl;
|
24
|
-
break;
|
25
|
-
case LWS_CALLBACK_CLIENT_RECEIVE:
|
26
|
-
{
|
27
|
-
std::string message(static_cast<char *>(in), len);
|
28
|
-
Json::CharReaderBuilder rbuilder;
|
29
|
-
Json::Value root;
|
30
|
-
std::string errors;
|
31
|
-
|
32
|
-
std::istringstream s(message);
|
33
|
-
std::string doc;
|
34
|
-
std::getline(s, doc);
|
35
|
-
std::istringstream ss(doc);
|
36
|
-
if (Json::parseFromStream(rbuilder, ss, &root, &errors))
|
37
|
-
{
|
38
|
-
std::string action = root["action"].asString();
|
39
|
-
|
40
|
-
if (action == "createButton")
|
41
|
-
{
|
42
|
-
GtkWidget *button = gtk_button_new_with_label(root["label"].asString().c_str());
|
43
|
-
g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), strdup(root["id"].asString().c_str()));
|
44
|
-
widgets[root["id"].asString()] = button;
|
45
|
-
gtk_container_add(GTK_CONTAINER(gtk_window_new(GTK_WINDOW_TOPLEVEL)), button);
|
46
|
-
gtk_widget_show_all(button);
|
47
|
-
}
|
48
|
-
}
|
49
|
-
break;
|
50
|
-
}
|
51
|
-
case LWS_CALLBACK_CLIENT_CLOSED:
|
52
|
-
std::cout << "Disconnected from server" << std::endl;
|
53
|
-
break;
|
54
|
-
default:
|
55
|
-
break;
|
56
|
-
}
|
57
|
-
return 0;
|
58
|
-
}
|
59
|
-
|
60
|
-
static const struct lws_protocols protocols[] = {
|
61
|
-
{
|
62
|
-
"example-protocol",
|
63
|
-
callback_websocket,
|
64
|
-
0,
|
65
|
-
1024,
|
66
|
-
},
|
67
|
-
{NULL, NULL, 0, 0}};
|
68
|
-
|
69
|
-
int main(int argc, char **argv)
|
70
|
-
{
|
71
|
-
std::cout << "Starting" << std::endl;
|
72
|
-
if (argc != 2)
|
73
|
-
{
|
74
|
-
std::cerr << "Usage: " << argv[0] << " <WebSocket server URI>" << std::endl;
|
75
|
-
return 1;
|
76
|
-
}
|
77
|
-
|
78
|
-
gtk_init(&argc, &argv);
|
79
|
-
|
80
|
-
struct lws_context_creation_info info;
|
81
|
-
memset(&info, 0, sizeof info);
|
82
|
-
info.port = CONTEXT_PORT_NO_LISTEN;
|
83
|
-
info.protocols = protocols;
|
84
|
-
|
85
|
-
struct lws_context *context = lws_create_context(&info);
|
86
|
-
|
87
|
-
struct lws_client_connect_info ccinfo = {0};
|
88
|
-
ccinfo.context = context;
|
89
|
-
ccinfo.address = argv[1];
|
90
|
-
ccinfo.port = 8080;
|
91
|
-
ccinfo.path = "/";
|
92
|
-
ccinfo.host = lws_canonical_hostname(context);
|
93
|
-
ccinfo.origin = "origin";
|
94
|
-
ccinfo.protocol = protocols[0].name;
|
95
|
-
|
96
|
-
struct lws *wsi = lws_client_connect_via_info(&ccinfo);
|
97
|
-
|
98
|
-
while (lws_service(context, 1000) >= 0)
|
99
|
-
{
|
100
|
-
gtk_main_iteration_do(false);
|
101
|
-
}
|
102
|
-
|
103
|
-
lws_context_destroy(context);
|
104
|
-
return 0;
|
105
|
-
}
|
package/lib/rew/css/theme.css
DELETED
package/lib/rew/html/ui.html
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
<!doctype html>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8" />
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6
|
-
<title>$OPTIONS(title)</title>
|
7
|
-
|
8
|
-
<style>
|
9
|
-
/* $OPTIONS(style) */
|
10
|
-
</style>
|
11
|
-
|
12
|
-
<script>
|
13
|
-
window.onerror = () => alert('ERror');
|
14
|
-
</script>
|
15
|
-
</head>
|
16
|
-
|
17
|
-
<body></body>
|
18
|
-
</html>
|
package/lib/rew/html/ui.js
DELETED
@@ -1,245 +0,0 @@
|
|
1
|
-
try {
|
2
|
-
window.execContext = $OPTIONS(json.execContext);
|
3
|
-
} catch (e) {
|
4
|
-
window.execContext = {};
|
5
|
-
}
|
6
|
-
|
7
|
-
try {
|
8
|
-
window.exec = $OPTIONS(exec);
|
9
|
-
} catch (e) {
|
10
|
-
window.exec = function () {};
|
11
|
-
}
|
12
|
-
|
13
|
-
const DOM = [];
|
14
|
-
|
15
|
-
const findInDom = (id) => DOM.find((el) => el.widgetOptions.uuid == id) || DOM.find((el) => el.id == id);
|
16
|
-
|
17
|
-
const parseStyleValue = (val) => val;
|
18
|
-
|
19
|
-
const addTo = (el, parent) => {
|
20
|
-
if (parent == 'null') {
|
21
|
-
document.body.appendChild(el);
|
22
|
-
} else {
|
23
|
-
findInDom(parent).appendChild(el);
|
24
|
-
}
|
25
|
-
};
|
26
|
-
|
27
|
-
const initElement = (el, options, update = false) => {
|
28
|
-
if (el.widgetOptions) {
|
29
|
-
if (el.widgetOptions.style) {
|
30
|
-
for (let i in options.style) {
|
31
|
-
el.style.removeProperty(i, el.widgetOptions.style[i]);
|
32
|
-
}
|
33
|
-
}
|
34
|
-
if (el.widgetOptions.attr) {
|
35
|
-
for (let i in el.widgetOptions.attr) {
|
36
|
-
el.removeAttribute(i);
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
|
41
|
-
el.widgetOptions = options;
|
42
|
-
el.id = options.id;
|
43
|
-
el.textContent = options.data.text;
|
44
|
-
|
45
|
-
if (options.style) {
|
46
|
-
for (let i in options.style) {
|
47
|
-
el.style.setProperty(i, options.style[i]);
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
|
-
if (options.attr) {
|
52
|
-
for (let i in options.attr) {
|
53
|
-
el.setAttribute(i, options.attr[i]);
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
if (options.children.length) {
|
58
|
-
options.children.forEach((option) => {
|
59
|
-
option.parent = options.uuid;
|
60
|
-
if (update) updateElement(findInDom(option.uuid), option);
|
61
|
-
else createElement(option);
|
62
|
-
});
|
63
|
-
}
|
64
|
-
|
65
|
-
if (options.parent) {
|
66
|
-
addTo(el, options.parent);
|
67
|
-
}
|
68
|
-
};
|
69
|
-
|
70
|
-
const updateElement = (el, options) => {
|
71
|
-
if (!el) return;
|
72
|
-
initElement(el, options, true);
|
73
|
-
return el;
|
74
|
-
};
|
75
|
-
|
76
|
-
const events = [
|
77
|
-
'click',
|
78
|
-
'dblclick',
|
79
|
-
'mousedown',
|
80
|
-
'mouseup',
|
81
|
-
'mouseover',
|
82
|
-
'mouseout',
|
83
|
-
'mousemove',
|
84
|
-
'mouseenter',
|
85
|
-
'mouseleave',
|
86
|
-
'keydown',
|
87
|
-
'keypress',
|
88
|
-
'keyup',
|
89
|
-
'change',
|
90
|
-
'input',
|
91
|
-
'submit',
|
92
|
-
'focus',
|
93
|
-
'blur',
|
94
|
-
'copy',
|
95
|
-
'cut',
|
96
|
-
'paste',
|
97
|
-
'scroll',
|
98
|
-
'wheel',
|
99
|
-
'resize',
|
100
|
-
'contextmenu',
|
101
|
-
'drag',
|
102
|
-
'dragstart',
|
103
|
-
'dragend',
|
104
|
-
'dragenter',
|
105
|
-
'dragleave',
|
106
|
-
'dragover',
|
107
|
-
'drop',
|
108
|
-
'error',
|
109
|
-
'load',
|
110
|
-
'abort',
|
111
|
-
];
|
112
|
-
const handleListeners = (el) => {
|
113
|
-
events.forEach((event) => {
|
114
|
-
el.addEventListener(event, (e) => {
|
115
|
-
sendData({
|
116
|
-
action: 'hook:eventTrigger',
|
117
|
-
data: {
|
118
|
-
rid: 'event_trigger',
|
119
|
-
object: {
|
120
|
-
uuid: el.widgetOptions.uuid,
|
121
|
-
event,
|
122
|
-
data: {
|
123
|
-
mouse: { x: e.clientX, y: e.clientY },
|
124
|
-
key: { code: e.keyCode, key: e.key },
|
125
|
-
},
|
126
|
-
},
|
127
|
-
},
|
128
|
-
});
|
129
|
-
});
|
130
|
-
});
|
131
|
-
};
|
132
|
-
|
133
|
-
function eventHandlerFunction({ uuid, hookID, event }) {
|
134
|
-
return function (e) {
|
135
|
-
sendData({
|
136
|
-
action: 'hook:event_' + event,
|
137
|
-
data: {
|
138
|
-
rid: hookID,
|
139
|
-
object: {
|
140
|
-
uuid,
|
141
|
-
event,
|
142
|
-
data: {
|
143
|
-
mouse: { x: e.clientX, y: e.clientY },
|
144
|
-
key: { code: e.keyCode, key: e.key },
|
145
|
-
},
|
146
|
-
},
|
147
|
-
},
|
148
|
-
});
|
149
|
-
};
|
150
|
-
}
|
151
|
-
|
152
|
-
const createElement = (options) => {
|
153
|
-
const el = document.createElement(options.element);
|
154
|
-
DOM.push(el);
|
155
|
-
initElement(el, options);
|
156
|
-
return el;
|
157
|
-
};
|
158
|
-
|
159
|
-
const stringifyJSON = (json) => {
|
160
|
-
try {
|
161
|
-
return JSON.stringify(json, null, 4);
|
162
|
-
} catch (e) {
|
163
|
-
return json.toString();
|
164
|
-
}
|
165
|
-
};
|
166
|
-
|
167
|
-
const log = (...strings) => {
|
168
|
-
window.webkit.messageHandlers.external.postMessage(
|
169
|
-
JSON.stringify(
|
170
|
-
{
|
171
|
-
action: 'log',
|
172
|
-
data: strings
|
173
|
-
.map((r) => (typeof r == 'object' ? stringifyJSON(r) : `${r.toString()}`))
|
174
|
-
// .map((i) => i.replace(/\"/g, '\\\\"').replace(/\n/g, "\\\\n"))
|
175
|
-
.join('\n'),
|
176
|
-
},
|
177
|
-
null,
|
178
|
-
4,
|
179
|
-
),
|
180
|
-
);
|
181
|
-
};
|
182
|
-
|
183
|
-
const sendData = (data) => {
|
184
|
-
log('RESPONSE::' + stringifyJSON(data));
|
185
|
-
};
|
186
|
-
|
187
|
-
function process_data(data) {
|
188
|
-
return JSON.parse(data);
|
189
|
-
}
|
190
|
-
|
191
|
-
window.recieveMessage = (data) => {
|
192
|
-
const edata = data;
|
193
|
-
if (edata.action == 'eventListen') {
|
194
|
-
const el = findInDom(edata.data.uuid);
|
195
|
-
if (el) {
|
196
|
-
el.addEventListener(edata.data.event, eventHandlerFunction(edata.data));
|
197
|
-
}
|
198
|
-
} else if (edata.action == 'createElement') {
|
199
|
-
const options = edata.data;
|
200
|
-
try {
|
201
|
-
createElement(options);
|
202
|
-
} catch (e) {
|
203
|
-
log(e.toString());
|
204
|
-
}
|
205
|
-
} else if (edata.action == 'addStyleSheet') {
|
206
|
-
const style = document.createElement('style');
|
207
|
-
style.textContent = edata.data;
|
208
|
-
document.head.appendChild(style);
|
209
|
-
} else if (edata.action == 'updateElement') {
|
210
|
-
const options = edata.data;
|
211
|
-
try {
|
212
|
-
updateElement(findInDom(options.uuid), options);
|
213
|
-
} catch (e) {
|
214
|
-
log(e.toString());
|
215
|
-
}
|
216
|
-
} else if (edata.action == 'findElement') {
|
217
|
-
const id = edata.data.id;
|
218
|
-
const rid = edata.data.rid;
|
219
|
-
try {
|
220
|
-
sendData({
|
221
|
-
action: 'hook:findElement',
|
222
|
-
data: { rid, object: findInDom(id)?.widgetOptions },
|
223
|
-
});
|
224
|
-
} catch (e) {
|
225
|
-
log(e.toString());
|
226
|
-
}
|
227
|
-
} else if (edata.action == 'message') {
|
228
|
-
window.dispatchEvent(
|
229
|
-
new CustomEvent('message', {
|
230
|
-
detail: edata.data,
|
231
|
-
}),
|
232
|
-
);
|
233
|
-
}
|
234
|
-
};
|
235
|
-
|
236
|
-
window.addEventListener('load', () => {
|
237
|
-
window.exec({
|
238
|
-
...window.execContext,
|
239
|
-
window,
|
240
|
-
log,
|
241
|
-
send: (data) => sendData({ action: 'message', data }),
|
242
|
-
onRecieve: (cb) => window.addEventListener('message', (e) => cb(e.detail || {})),
|
243
|
-
});
|
244
|
-
log('SETUP::READY');
|
245
|
-
});
|
@@ -1,184 +0,0 @@
|
|
1
|
-
const emitter = require('../../../functions/emitter');
|
2
|
-
const { struct } = require('../../../models/struct');
|
3
|
-
const { generateRandomID } = require('../../../functions/id');
|
4
|
-
|
5
|
-
module.exports.uiClasses = (context, options, send, recieve, hook, rmHook) => {
|
6
|
-
const _sanitizeOptions = (options) => {
|
7
|
-
return {
|
8
|
-
...options,
|
9
|
-
children: options.children.map((i) => i.options),
|
10
|
-
};
|
11
|
-
};
|
12
|
-
const RemWidgetOptions = struct({
|
13
|
-
element: 'div',
|
14
|
-
class: '',
|
15
|
-
attr: {},
|
16
|
-
id: '',
|
17
|
-
data: {
|
18
|
-
text: '',
|
19
|
-
},
|
20
|
-
children: [],
|
21
|
-
uuid: '',
|
22
|
-
parent: '!any',
|
23
|
-
style: {},
|
24
|
-
});
|
25
|
-
|
26
|
-
const CreatedElements = [];
|
27
|
-
|
28
|
-
class RewWidget {
|
29
|
-
_emitter = emitter();
|
30
|
-
on(event, callback) {
|
31
|
-
const hookID = this.uuid + '_' + generateRandomID(4);
|
32
|
-
this._emitter.on(event, callback, { hookID });
|
33
|
-
hook(
|
34
|
-
hookID,
|
35
|
-
'event_' + event,
|
36
|
-
(data) => {
|
37
|
-
this.emit(event, data);
|
38
|
-
},
|
39
|
-
false,
|
40
|
-
);
|
41
|
-
send({ action: 'eventListen', data: { uuid: this.uuid, event, hookID } });
|
42
|
-
return this;
|
43
|
-
}
|
44
|
-
off(event, callback) {
|
45
|
-
this._emitter.off(event, callback, (e) => rmHook(e.hookID));
|
46
|
-
return this;
|
47
|
-
}
|
48
|
-
emit(event, callback) {
|
49
|
-
this._emitter.emit(event, callback);
|
50
|
-
return this;
|
51
|
-
}
|
52
|
-
|
53
|
-
options = RemWidgetOptions();
|
54
|
-
constructor(options = RemWidgetOptions()) {
|
55
|
-
const config = RemWidgetOptions(options);
|
56
|
-
config.uuid = generateRandomID();
|
57
|
-
this.options = config;
|
58
|
-
this.options.children.forEach((child) => (child.parent = this));
|
59
|
-
this.init();
|
60
|
-
CreatedElements.push(this);
|
61
|
-
}
|
62
|
-
|
63
|
-
init() {
|
64
|
-
send({ action: 'createElement', data: _sanitizeOptions(this.options) });
|
65
|
-
}
|
66
|
-
|
67
|
-
parent = null;
|
68
|
-
|
69
|
-
get uuid() {
|
70
|
-
return this.options.uuid;
|
71
|
-
}
|
72
|
-
|
73
|
-
get id() {
|
74
|
-
return this.options.id;
|
75
|
-
}
|
76
|
-
|
77
|
-
get children() {
|
78
|
-
return this.options.children;
|
79
|
-
}
|
80
|
-
|
81
|
-
find(id, recursive = true) {
|
82
|
-
let childFound = this.children.find((e) => e.id == id) || this.children.find((e) => e.uuid == id);
|
83
|
-
if (childFound) return childFound;
|
84
|
-
else if (!recursive) return null;
|
85
|
-
for (let child of this.children) {
|
86
|
-
let subchild = child.find(id);
|
87
|
-
if (subchild) {
|
88
|
-
return subchild;
|
89
|
-
}
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
update() {
|
94
|
-
send({ action: 'updateElement', data: _sanitizeOptions(this.options) });
|
95
|
-
return this;
|
96
|
-
}
|
97
|
-
|
98
|
-
text(text) {
|
99
|
-
this.options.data.text = text;
|
100
|
-
return this.update();
|
101
|
-
}
|
102
|
-
|
103
|
-
data(key, value) {
|
104
|
-
if (!value) return this.options.data[key];
|
105
|
-
this.options.data[key] = value;
|
106
|
-
return this.update();
|
107
|
-
}
|
108
|
-
|
109
|
-
attr(attr, reset = false) {
|
110
|
-
if (reset) this.options.attr = attr;
|
111
|
-
else this.options.attr = { ...this.options.attr, ...attr };
|
112
|
-
return this.update();
|
113
|
-
}
|
114
|
-
|
115
|
-
style(style, reset = false) {
|
116
|
-
if (reset) this.options.style = style;
|
117
|
-
else this.options.style = { ...this.options.style, ...style };
|
118
|
-
return this.update();
|
119
|
-
}
|
120
|
-
|
121
|
-
add(child) {
|
122
|
-
this.options.children.push(child);
|
123
|
-
return this.update();
|
124
|
-
}
|
125
|
-
|
126
|
-
remove(childId, recursive = true) {
|
127
|
-
const child = typeof childId == 'string' ? this.find(childId, recursive) : childId;
|
128
|
-
if (!child) return this;
|
129
|
-
if (recursive && child.parent !== this) {
|
130
|
-
child.parent.remove(child);
|
131
|
-
} else {
|
132
|
-
this.options.children.splice(this.options.children.indexOf(child), 1);
|
133
|
-
this.update();
|
134
|
-
}
|
135
|
-
return this;
|
136
|
-
}
|
137
|
-
}
|
138
|
-
|
139
|
-
class RewTextWidget extends RewWidget {
|
140
|
-
constructor(text = '', options = RemWidgetOptions({})) {
|
141
|
-
super({
|
142
|
-
...options,
|
143
|
-
data: { ...options.data, text },
|
144
|
-
});
|
145
|
-
}
|
146
|
-
}
|
147
|
-
|
148
|
-
class StyleSheet {
|
149
|
-
constructor(css = '') {
|
150
|
-
send({ action: 'addStyleSheet', data: css });
|
151
|
-
}
|
152
|
-
}
|
153
|
-
|
154
|
-
function findElement(id) {
|
155
|
-
return new Promise((r) => {
|
156
|
-
const rid = generateRandomID();
|
157
|
-
hook(rid, 'findElement', (data) => {
|
158
|
-
r(CreatedElements.find((e) => e.uuid == data.uuid) || data);
|
159
|
-
});
|
160
|
-
send({ action: 'findElement', data: { id, rid } });
|
161
|
-
});
|
162
|
-
}
|
163
|
-
|
164
|
-
// hook('event_trigger', 'eventTrigger', (data) => {
|
165
|
-
// const el = CreatedElements.find(e => e.uuid = data.uuid);
|
166
|
-
// if(el){
|
167
|
-
// el.emit(data.event, data.data);
|
168
|
-
// }
|
169
|
-
// }, false);
|
170
|
-
|
171
|
-
const Transmitter = {
|
172
|
-
send: (data) => send({ action: 'message', data }),
|
173
|
-
recieve: (cb) => recieve((data) => cb(data.data)),
|
174
|
-
};
|
175
|
-
|
176
|
-
return {
|
177
|
-
Widget: RewWidget,
|
178
|
-
Text: RewTextWidget,
|
179
|
-
WidgetOptions: RemWidgetOptions,
|
180
|
-
findElement,
|
181
|
-
StyleSheet: StyleSheet,
|
182
|
-
Transmitter,
|
183
|
-
};
|
184
|
-
};
|
package/lib/rew/pkgs/ui.js
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
const path = require('path');
|
2
|
-
const { spawn, exec } = require('child_process');
|
3
|
-
const fs = require('fs');
|
4
|
-
const { uiClasses } = require('./modules/ui/classes');
|
5
|
-
const { generateRandomID } = require('../functions/id');
|
6
|
-
const { THEME_PATH } = require('../const/files');
|
7
|
-
const readline = require('readline');
|
8
|
-
const emitter = require('../functions/emitter');
|
9
|
-
|
10
|
-
const BIN_PATH = path.resolve(__dirname, '../../../bin/ui');
|
11
|
-
const HTML_STRING = fs.readFileSync(path.resolve(__dirname, '../html/ui.html'), { encoding: 'utf-8' });
|
12
|
-
const JS_STRING = fs.readFileSync(path.resolve(__dirname, '../html/ui.js'), {
|
13
|
-
encoding: 'utf-8',
|
14
|
-
});
|
15
|
-
|
16
|
-
const replaceString = (string, options) =>
|
17
|
-
string.replace(/\$OPTIONS\(([^)]+)\)/g, (_, n) =>
|
18
|
-
n.startsWith('json.') ? JSON.stringify(options[n.split('json.')[1]] || '{}') : options[n] || _,
|
19
|
-
);
|
20
|
-
|
21
|
-
const defaultOptions = {
|
22
|
-
title: 'Title',
|
23
|
-
onExit: () => process.exit(),
|
24
|
-
style: '',
|
25
|
-
stylePath: THEME_PATH,
|
26
|
-
exec: () => {},
|
27
|
-
execContext: {},
|
28
|
-
};
|
29
|
-
|
30
|
-
module.exports = (context) => ({
|
31
|
-
start: (o = {}) => {
|
32
|
-
const options = {
|
33
|
-
...defaultOptions,
|
34
|
-
...o,
|
35
|
-
};
|
36
|
-
|
37
|
-
const hookedSocketListeners = {};
|
38
|
-
|
39
|
-
const runId = generateRandomID();
|
40
|
-
const tmpFile = '/tmp/' + runId + '.ruw.ui.socket';
|
41
|
-
|
42
|
-
options.runId = runId;
|
43
|
-
|
44
|
-
if (fs.existsSync(options.stylePath)) options.style = fs.readFileSync(options.stylePath, { encoding: 'utf-8' }) + '\n' + options.style;
|
45
|
-
|
46
|
-
options.style = ' */\n' + options.style + '\n/* ';
|
47
|
-
|
48
|
-
const HTML = replaceString(HTML_STRING, options);
|
49
|
-
const JS = replaceString(JS_STRING, options);
|
50
|
-
|
51
|
-
/**
|
52
|
-
* Queue for future writes
|
53
|
-
* @type {string[]}
|
54
|
-
* */
|
55
|
-
const queue = [];
|
56
|
-
|
57
|
-
const send = (data) => {
|
58
|
-
const content = fs.readFileSync(tmpFile, { encoding: 'utf-8' });
|
59
|
-
if (content) {
|
60
|
-
queue.push(data);
|
61
|
-
} else {
|
62
|
-
fs.writeFileSync(tmpFile, typeof data !== 'string' ? JSON.stringify(data) : data);
|
63
|
-
}
|
64
|
-
};
|
65
|
-
|
66
|
-
const sendEvent = (data) => {
|
67
|
-
send({
|
68
|
-
action: 'JS',
|
69
|
-
data: `window.recieveMessage(${JSON.stringify(data)})`,
|
70
|
-
});
|
71
|
-
};
|
72
|
-
|
73
|
-
const g_emitter = emitter();
|
74
|
-
|
75
|
-
const recieve = (data) => {
|
76
|
-
g_emitter.emit('recieve', data);
|
77
|
-
};
|
78
|
-
|
79
|
-
const rl = readline.createInterface({
|
80
|
-
input: process.stdin,
|
81
|
-
output: process.stdout,
|
82
|
-
});
|
83
|
-
|
84
|
-
rl.question('', () => {});
|
85
|
-
|
86
|
-
fs.writeFileSync(tmpFile, '');
|
87
|
-
|
88
|
-
fs.watch(tmpFile, { encoding: 'utf-8' }).on('change', () => {
|
89
|
-
if (queue.length) {
|
90
|
-
send(queue.pop());
|
91
|
-
}
|
92
|
-
});
|
93
|
-
|
94
|
-
const p = spawn(options.bin || BIN_PATH, [runId]);
|
95
|
-
|
96
|
-
p.on('close', (code) => {
|
97
|
-
rl.close();
|
98
|
-
options.onExit(code);
|
99
|
-
});
|
100
|
-
|
101
|
-
process.on('beforeExit', () => {
|
102
|
-
p.kill();
|
103
|
-
fs.unlinkSync(tmpFile);
|
104
|
-
});
|
105
|
-
|
106
|
-
g_emitter.on('recieve', (edata) => {
|
107
|
-
if (edata.action.startsWith('hook:')) {
|
108
|
-
const hook = hookedSocketListeners[edata.data.rid];
|
109
|
-
const type = edata.action.split('hook:')[1];
|
110
|
-
if (hook && hook.type == type) {
|
111
|
-
hookedSocketListeners[edata.data.rid].cb(edata.data.object);
|
112
|
-
if (hook.once) delete hookedSocketListeners[edata.data.rid];
|
113
|
-
}
|
114
|
-
}
|
115
|
-
});
|
116
|
-
|
117
|
-
return new Promise((r) => {
|
118
|
-
p.stdout.on('data', (data) => {
|
119
|
-
if (data.toString().startsWith('RESPONSE::')) {
|
120
|
-
const d = data.toString().split('RESPONSE::')[1];
|
121
|
-
const jd = JSON.parse(d);
|
122
|
-
recieve(jd);
|
123
|
-
} else if (data.toString().trim().endsWith('SETUP::READY')) {
|
124
|
-
console.log('READY');
|
125
|
-
r(
|
126
|
-
uiClasses(
|
127
|
-
context,
|
128
|
-
options,
|
129
|
-
sendEvent,
|
130
|
-
(cb) => {
|
131
|
-
g_emitter.on('recieve', cb);
|
132
|
-
},
|
133
|
-
(rid, type, cb, once = true) => {
|
134
|
-
// Add hook
|
135
|
-
hookedSocketListeners[rid] = { type, cb, once };
|
136
|
-
},
|
137
|
-
(rid) => {
|
138
|
-
// Remove hook
|
139
|
-
delete hookedSocketListeners[rid];
|
140
|
-
},
|
141
|
-
),
|
142
|
-
);
|
143
|
-
} else if (data.toString().endsWith('SETUP::HTML')) {
|
144
|
-
send({ action: 'JS2', data: JS, isSetup: true });
|
145
|
-
} else if (data.toString() == 'INIT::READY') {
|
146
|
-
send({ action: 'HTML', data: HTML });
|
147
|
-
} else {
|
148
|
-
console.log(data.toString());
|
149
|
-
}
|
150
|
-
});
|
151
|
-
|
152
|
-
p.stderr.on('data', (data) => {
|
153
|
-
console.error(data.toString());
|
154
|
-
});
|
155
|
-
});
|
156
|
-
},
|
157
|
-
});
|