pinokiod 1.3.55 → 1.3.61
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/kernel/api/env/index.js +22 -0
- package/kernel/api/fs/index.js +5 -3
- package/kernel/api/index.js +23 -10
- package/kernel/environment.js +0 -1
- package/kernel/index.js +0 -1
- package/kernel/shell.js +12 -9
- package/kernel/shells.js +0 -3
- package/kernel/util.js +107 -1
- package/package.json +2 -1
- package/server/index.js +80 -10
- package/server/public/style.css +6 -7
- package/server/views/browser-experimental.ejs +18 -3
- package/server/views/env_editor.ejs +183 -0
- package/server/views/general_editor.ejs +2 -2
- package/server/views/help.ejs +4 -0
- package/server/views/index.ejs +34 -17
- package/server/views/partials/menu.ejs +3 -1
- package/server/views/settings.ejs +5 -1
- package/server/views/share_editor.ejs +196 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const set = require("../set")
|
|
2
|
+
const rm = require("../rm")
|
|
3
|
+
const Environment = require('../../environment')
|
|
4
|
+
const Util = require("../../util")
|
|
5
|
+
class Env {
|
|
6
|
+
async set(req, ondata, kernel) {
|
|
7
|
+
/*
|
|
8
|
+
req := {
|
|
9
|
+
"method": "env.set",
|
|
10
|
+
"params": {
|
|
11
|
+
<key>: <val>,
|
|
12
|
+
<key>: <val>,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
*/
|
|
16
|
+
// write to current app folder's ENVIRONMENT
|
|
17
|
+
|
|
18
|
+
let api_path = await Util.api_path(req.parent.path, kernel)
|
|
19
|
+
await Util.update_env(api_path, req.params)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
module.exports = Env
|
package/kernel/api/fs/index.js
CHANGED
|
@@ -129,11 +129,13 @@ class FS {
|
|
|
129
129
|
ondata({ raw: "\r\ncreating a shared drive:\r\n" + JSON.stringify(req.params, null, 2).replace(/\n/g, "\r\n") })
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
let current_env = await Environment.get(api_path)
|
|
132
|
+
// start with the proces.env => merge pinokio global ENVIRONMENT => merge app ENVIRONMENT
|
|
134
133
|
let default_env = await Environment.get(kernel.homedir)
|
|
135
|
-
|
|
134
|
+
let api_path = await Util.api_path(req.parent.path, kernel)
|
|
135
|
+
let api_env = await Environment.get(api_path)
|
|
136
|
+
let current_env = Object.assign(process.env, default_env, api_env)
|
|
136
137
|
|
|
138
|
+
// if PINOKIO_DRIVE environment variable is specified, use this custom value for the drive path instead of ~/pinokio/drive
|
|
137
139
|
let env_drive_path = current_env.PINOKIO_DRIVE
|
|
138
140
|
let drive_home
|
|
139
141
|
if (env_drive_path) {
|
package/kernel/api/index.js
CHANGED
|
@@ -560,7 +560,6 @@ class Api {
|
|
|
560
560
|
// 6. rpc must have method names
|
|
561
561
|
if (rpc.method) {
|
|
562
562
|
|
|
563
|
-
|
|
564
563
|
// 7. resolve the rpc
|
|
565
564
|
let resolved = await this.resolveMethod(rpc, cwd)
|
|
566
565
|
|
|
@@ -1051,20 +1050,34 @@ class Api {
|
|
|
1051
1050
|
}
|
|
1052
1051
|
}
|
|
1053
1052
|
} else if (request.method) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1053
|
+
let pass = 0;
|
|
1054
|
+
while(true) {
|
|
1055
|
+
request = this.kernel.template.render(request, { kernel: this.kernel })
|
|
1056
|
+
if (this.kernel.template.istemplate(request)) {
|
|
1057
|
+
pass++;
|
|
1058
|
+
if (pass >= 4) {
|
|
1059
|
+
// only try 4 times
|
|
1060
|
+
break;
|
|
1061
|
+
} else {
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1061
1064
|
} else {
|
|
1062
|
-
|
|
1065
|
+
break;
|
|
1063
1066
|
}
|
|
1064
1067
|
}
|
|
1068
|
+
if (this.kernel.template.istemplate(request)) {
|
|
1069
|
+
console.log("something wrong with the request", request)
|
|
1070
|
+
return
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// replace {{{ }}} with {{ }}
|
|
1074
|
+
request = this.kernel.template.flatten(request)
|
|
1075
|
+
|
|
1076
|
+
if (request.params && request.params.path) {
|
|
1077
|
+
request.params.path = this.resolvePath(this.userdir, request.params.path)
|
|
1078
|
+
}
|
|
1065
1079
|
|
|
1066
1080
|
// Kernel Call
|
|
1067
|
-
console.log("REQUEST", request)
|
|
1068
1081
|
let resolved = await this.resolveMethod(request)
|
|
1069
1082
|
try {
|
|
1070
1083
|
resolved.dirname = resolved.dirname
|
package/kernel/environment.js
CHANGED
package/kernel/index.js
CHANGED
package/kernel/shell.js
CHANGED
|
@@ -112,19 +112,22 @@ class Shell {
|
|
|
112
112
|
|
|
113
113
|
this.env.CMAKE_OBJECT_PATH_MAX = 1024
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
// First override this.env with system env
|
|
116
|
+
let system_env = await Environment.get(this.kernel.homedir)
|
|
117
|
+
this.env = Object.assign(this.env, system_env)
|
|
118
|
+
|
|
119
|
+
// if the shell is running from a script file, the params.$parent will include the path to the parent script
|
|
120
|
+
// this means we need to apply app environment as well
|
|
120
121
|
if (params.$parent) {
|
|
121
122
|
let api_path = await Util.api_path(params.$parent.path, this.kernel)
|
|
123
|
+
|
|
124
|
+
// initialize folders
|
|
122
125
|
await Environment.init_folders(api_path)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
|
|
127
|
+
// apply app env to this.env
|
|
128
|
+
let app_env = await Environment.get(api_path)
|
|
129
|
+
this.env = Object.assign(this.env, app_env)
|
|
126
130
|
}
|
|
127
|
-
console.log("THIS.env after", this.env)
|
|
128
131
|
let PATH_KEY;
|
|
129
132
|
if (this.env.Path) {
|
|
130
133
|
PATH_KEY = "Path"
|
package/kernel/shells.js
CHANGED
|
@@ -160,7 +160,6 @@ class Shells {
|
|
|
160
160
|
matches[2] += "g" // if g option is not included, include it (need it for matchAll)
|
|
161
161
|
}
|
|
162
162
|
let re = new RegExp(matches[1], matches[2])
|
|
163
|
-
console.log("stream", stream)
|
|
164
163
|
let line = stream.cleaned.replaceAll(/[\r\n]/g, "")
|
|
165
164
|
//let rendered_event = [...stream.cleaned.matchAll(re)]
|
|
166
165
|
let rendered_event = [...line.matchAll(re)]
|
|
@@ -222,7 +221,6 @@ class Shells {
|
|
|
222
221
|
for(let i=0; i<shells.length; i++) {
|
|
223
222
|
let shell = shells[i]
|
|
224
223
|
if (shell.id === request.id) {
|
|
225
|
-
console.log("FOUND SHELL TO KILL", shell.id)
|
|
226
224
|
shell.kill(message, true)
|
|
227
225
|
}
|
|
228
226
|
}
|
|
@@ -235,7 +233,6 @@ class Shells {
|
|
|
235
233
|
for(let i=0; i<shells.length; i++) {
|
|
236
234
|
let shell = shells[i]
|
|
237
235
|
if (shell.group === request.group) {
|
|
238
|
-
console.log("kill shell ", request.group, i, shell)
|
|
239
236
|
shell.kill(message, true)
|
|
240
237
|
}
|
|
241
238
|
}
|
package/kernel/util.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const dotenv = require('dotenv')
|
|
4
|
+
|
|
5
|
+
const {auto: normalizeEOL} = require("eol");
|
|
6
|
+
const {EOL} = require("os");
|
|
7
|
+
const breakPattern = /\n/g;
|
|
8
|
+
const breakReplacement = "\\n";
|
|
9
|
+
const groupPattern = /\$/g;
|
|
10
|
+
const groupReplacement = "$$$";
|
|
11
|
+
const h = "[^\\S\\r\\n]"; // simulate `\h`
|
|
12
|
+
const returnPattern = /\r/g;
|
|
13
|
+
const returnReplacement = "\\r";
|
|
14
|
+
|
|
4
15
|
const parse_env = async (filename) => {
|
|
5
16
|
try {
|
|
6
17
|
const buf = await fs.promises.readFile(filename)
|
|
@@ -10,6 +21,52 @@ const parse_env = async (filename) => {
|
|
|
10
21
|
return {}
|
|
11
22
|
}
|
|
12
23
|
}
|
|
24
|
+
|
|
25
|
+
const parse_env_detail = async (filename) => {
|
|
26
|
+
// takes env and returns an array
|
|
27
|
+
const config = parse_env(filename)
|
|
28
|
+
/*
|
|
29
|
+
config := {
|
|
30
|
+
key1: val1,
|
|
31
|
+
key2; val2,
|
|
32
|
+
...
|
|
33
|
+
}
|
|
34
|
+
*/
|
|
35
|
+
const keys = Object.keys(config)
|
|
36
|
+
const str = await fs.promises.readFile(filename, "utf8")
|
|
37
|
+
const lines = str.split(EOL)
|
|
38
|
+
|
|
39
|
+
let items = []
|
|
40
|
+
|
|
41
|
+
for(let line of lines) {
|
|
42
|
+
if (line.trim().startsWith("#")) {
|
|
43
|
+
items.push({
|
|
44
|
+
type: "comment",
|
|
45
|
+
val: line
|
|
46
|
+
})
|
|
47
|
+
} else {
|
|
48
|
+
const buf = Buffer.from(line)
|
|
49
|
+
const parsed = dotenv.parse(buf)
|
|
50
|
+
const key = Object.keys(parsed)
|
|
51
|
+
if (key.length > 0) {
|
|
52
|
+
items.push({ key: key[0], val: parsed[key[0]] })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/*
|
|
57
|
+
items := [{
|
|
58
|
+
type: "comment",
|
|
59
|
+
val: "###################################",
|
|
60
|
+
}, {
|
|
61
|
+
type: "comment",
|
|
62
|
+
val: "# this is a comment",
|
|
63
|
+
}, {
|
|
64
|
+
key: "TMPDIR",
|
|
65
|
+
val: "/tmp"
|
|
66
|
+
}]
|
|
67
|
+
*/
|
|
68
|
+
return items
|
|
69
|
+
}
|
|
13
70
|
const api_path = async (fullpath, kernel) => {
|
|
14
71
|
let api_path = `${kernel.homedir}${path.sep}api`
|
|
15
72
|
let rel_path = path.relative(api_path, fullpath)
|
|
@@ -17,6 +74,55 @@ const api_path = async (fullpath, kernel) => {
|
|
|
17
74
|
let current_api_path = `${api_path}${path.sep}${api_name}`
|
|
18
75
|
return current_api_path
|
|
19
76
|
}
|
|
77
|
+
const escapeStringRegexp = (string) => {
|
|
78
|
+
if (typeof string !== 'string') {
|
|
79
|
+
throw new TypeError('Expected a string');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Escape characters with special meaning either inside or outside character sets.
|
|
83
|
+
// Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
|
|
84
|
+
return string
|
|
85
|
+
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
|
|
86
|
+
.replace(/-/g, '\\x2d');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const update_env = async (filepath, changes) => {
|
|
90
|
+
const envString = await fs.promises.readFile(filepath, "utf8")
|
|
91
|
+
console.log({ filepath, envString, changes })
|
|
92
|
+
let hasAppended = false;
|
|
93
|
+
console.log("changes", changes)
|
|
94
|
+
const newval = Object.keys(changes).reduce((result, varname) => {
|
|
95
|
+
const value = changes[varname].toString()
|
|
96
|
+
.replace(breakPattern, breakReplacement)
|
|
97
|
+
.replace(returnPattern, returnReplacement)
|
|
98
|
+
.trim();
|
|
99
|
+
const safeName = escapeStringRegexp(varname);
|
|
100
|
+
const varPattern = new RegExp(`^(${h}*${safeName}${h}*=${h}*)\\S*(${h}*)$`, "gm");
|
|
101
|
+
if (varPattern.test(result)) {
|
|
102
|
+
const safeValue = value.replace(groupPattern, groupReplacement);
|
|
103
|
+
return result.replace(varPattern, `$1${safeValue}$2`);
|
|
104
|
+
} else if (result === "") {
|
|
105
|
+
hasAppended = true;
|
|
106
|
+
return `${varname}=${value}${EOL}`;
|
|
107
|
+
} else if (!result.endsWith(EOL) && !hasAppended) {
|
|
108
|
+
hasAppended = true;
|
|
109
|
+
// Add an extra break between previously defined and newly appended variable
|
|
110
|
+
return `${result}${EOL}${EOL}${varname}=${value}`;
|
|
111
|
+
} else if (!result.endsWith(EOL)) {
|
|
112
|
+
// Add break for appended variable
|
|
113
|
+
return `${result}${EOL}${varname}=${value}`;
|
|
114
|
+
} else if (result.endsWith(EOL) && !hasAppended) {
|
|
115
|
+
hasAppended = true;
|
|
116
|
+
// Add an extra break between previously defined and newly appended variable
|
|
117
|
+
return `${result}${EOL}${varname}=${value}${EOL}`;
|
|
118
|
+
} else /*if (result.endsWith(EOL))*/ {
|
|
119
|
+
// Add break for appended variable
|
|
120
|
+
return `${result}${varname}=${value}${EOL}`;
|
|
121
|
+
}
|
|
122
|
+
}, normalizeEOL(envString));
|
|
123
|
+
console.log("updated ENVIRONMENT", newval)
|
|
124
|
+
await fs.promises.writeFile(filepath, newval)
|
|
125
|
+
};
|
|
20
126
|
module.exports = {
|
|
21
|
-
parse_env, api_path
|
|
127
|
+
parse_env, api_path, update_env, parse_env_detail
|
|
22
128
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinokiod",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.61",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"decompress": "^4.2.1",
|
|
22
22
|
"dotenv": "^16.4.5",
|
|
23
23
|
"ejs": "^3.1.9",
|
|
24
|
+
"eol": "^0.9.1",
|
|
24
25
|
"express": "^4.18.2",
|
|
25
26
|
"express-http-proxy-cp": "^2.0.3",
|
|
26
27
|
"express-session": "^1.17.3",
|
package/server/index.js
CHANGED
|
@@ -36,6 +36,7 @@ const Kernel = require("../kernel")
|
|
|
36
36
|
const packagejson = require("../package.json")
|
|
37
37
|
const Environment = require("../kernel/environment")
|
|
38
38
|
const Cloudflare = require("../kernel/api/cloudflare")
|
|
39
|
+
const Util = require("../kernel/util")
|
|
39
40
|
class Server {
|
|
40
41
|
constructor(config) {
|
|
41
42
|
this.tabs = {}
|
|
@@ -238,9 +239,15 @@ class Server {
|
|
|
238
239
|
await fs.promises.writeFile(current, content)
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
let mode = "run"
|
|
243
|
+
console.log("req.query", req.query)
|
|
244
|
+
if (req.query && req.query.mode) {
|
|
245
|
+
mode = req.query.mode
|
|
246
|
+
}
|
|
241
247
|
|
|
242
248
|
//res.render("browser", {
|
|
243
249
|
res.render("browser-experimental", {
|
|
250
|
+
mode,
|
|
244
251
|
port: this.port,
|
|
245
252
|
mem,
|
|
246
253
|
type,
|
|
@@ -1272,9 +1279,22 @@ class Server {
|
|
|
1272
1279
|
if (config.menu) {
|
|
1273
1280
|
|
|
1274
1281
|
config.menu = [{
|
|
1282
|
+
base: "/",
|
|
1275
1283
|
text: "Configure",
|
|
1276
|
-
href:
|
|
1284
|
+
href: `env/api/${name}/ENVIRONMENT`,
|
|
1277
1285
|
icon: "fa-solid fa-gear"
|
|
1286
|
+
// }, {
|
|
1287
|
+
// base: "/",
|
|
1288
|
+
// text: "Public Share",
|
|
1289
|
+
// action: {
|
|
1290
|
+
// method: "env.set",
|
|
1291
|
+
// params: {
|
|
1292
|
+
// "PINOKIO_SHARE_CLOUDFLARE": true,
|
|
1293
|
+
// "PINOKIO_SHARE_LOCAL": true
|
|
1294
|
+
// }
|
|
1295
|
+
// },
|
|
1296
|
+
// href: `env/api/${name}/ENVIRONMENT`,
|
|
1297
|
+
// icon: "fa-solid fa-gear"
|
|
1278
1298
|
}].concat(config.menu)
|
|
1279
1299
|
|
|
1280
1300
|
for(let i=0; i<config.menu.length; i++) {
|
|
@@ -1285,14 +1305,18 @@ class Server {
|
|
|
1285
1305
|
menuitem.menu = m.menu
|
|
1286
1306
|
}
|
|
1287
1307
|
|
|
1288
|
-
if (menuitem.
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1308
|
+
if (menuitem.base === "/") {
|
|
1309
|
+
config.menu[i].href = menuitem.base + menuitem.href
|
|
1310
|
+
} else {
|
|
1311
|
+
if (menuitem.href && !menuitem.href.startsWith("http")) {
|
|
1312
|
+
|
|
1313
|
+
// href resolution
|
|
1314
|
+
let absolute = path.resolve(__dirname, ...pathComponents, menuitem.href)
|
|
1315
|
+
let seed = path.resolve(__dirname)
|
|
1316
|
+
let p = absolute.replace(seed, "")
|
|
1317
|
+
let link = p.split(/[\/\\]/).filter((x) => { return x }).join("/")
|
|
1318
|
+
config.menu[i].href = "/api/" + name + "/" + link
|
|
1319
|
+
}
|
|
1296
1320
|
}
|
|
1297
1321
|
|
|
1298
1322
|
if (menuitem.href && menuitem.params) {
|
|
@@ -2118,6 +2142,47 @@ class Server {
|
|
|
2118
2142
|
res.json({ error: "type must be 'local' or 'cloudflare'" })
|
|
2119
2143
|
}
|
|
2120
2144
|
})
|
|
2145
|
+
this.app.post("/env", async (req, res) => {
|
|
2146
|
+
console.log("req.body", req.body)
|
|
2147
|
+
let fullpath = path.resolve(this.kernel.homedir, req.body.filepath)
|
|
2148
|
+
let updated = req.body.vals
|
|
2149
|
+
await Util.update_env(fullpath, updated)
|
|
2150
|
+
res.json({})
|
|
2151
|
+
})
|
|
2152
|
+
this.app.get("/env/*", async (req, res) => {
|
|
2153
|
+
let pathComponents = req.params[0].split("/")
|
|
2154
|
+
let filepath = path.resolve(this.kernel.homedir, req.params[0])
|
|
2155
|
+
const items = await Util.parse_env_detail(filepath)
|
|
2156
|
+
res.render("env_editor", {
|
|
2157
|
+
items,
|
|
2158
|
+
theme: this.theme,
|
|
2159
|
+
filepath,
|
|
2160
|
+
agent: this.agent,
|
|
2161
|
+
})
|
|
2162
|
+
})
|
|
2163
|
+
this.app.get("/share/:name", async (req, res) => {
|
|
2164
|
+
let filepath = path.resolve(this.kernel.homedir, "api", req.params.name, "ENVIRONMENT")
|
|
2165
|
+
//let filepath = path.resolve(this.kernel.homedir, req.params[0])
|
|
2166
|
+
console.log("filepath", filepath)
|
|
2167
|
+
const config = await Util.parse_env(filepath)
|
|
2168
|
+
console.log("config", config)
|
|
2169
|
+
const keys = [
|
|
2170
|
+
"PINOKIO_SHARE_CLOUDFLARE",
|
|
2171
|
+
"PINOKIO_SHARE_LOCAL",
|
|
2172
|
+
]
|
|
2173
|
+
for(let key of keys) {
|
|
2174
|
+
if (!config[key]) {
|
|
2175
|
+
config[key] = ""
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
res.render("share_editor", {
|
|
2179
|
+
keys,
|
|
2180
|
+
config,
|
|
2181
|
+
theme: this.theme,
|
|
2182
|
+
filepath,
|
|
2183
|
+
agent: this.agent,
|
|
2184
|
+
})
|
|
2185
|
+
})
|
|
2121
2186
|
this.app.get("/edit/*", async (req, res) => {
|
|
2122
2187
|
let pathComponents = req.params[0].split("/")
|
|
2123
2188
|
let filepath = path.resolve(this.kernel.homedir, req.params[0])
|
|
@@ -2292,8 +2357,13 @@ class Server {
|
|
|
2292
2357
|
this.app.get("/pinokio/launch/:name", async (req, res) => {
|
|
2293
2358
|
this.chrome(req, res, "launch")
|
|
2294
2359
|
})
|
|
2360
|
+
this.app.get("/pinokio/browser/:name/browse", async (req, res) => {
|
|
2361
|
+
console.log("browse mode")
|
|
2362
|
+
this.chrome(req, res, "browse")
|
|
2363
|
+
})
|
|
2295
2364
|
this.app.get("/pinokio/browser/:name", async (req, res) => {
|
|
2296
|
-
|
|
2365
|
+
console.log("run mode")
|
|
2366
|
+
this.chrome(req, res, "run")
|
|
2297
2367
|
})
|
|
2298
2368
|
this.app.post("/pinokio/delete", async (req, res) => {
|
|
2299
2369
|
try {
|
package/server/public/style.css
CHANGED
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
--light-thin: rgba(0,0,0,0.1);
|
|
22
22
|
--dark-thin: rgba(255,255,255,0.03);
|
|
23
23
|
|
|
24
|
+
/*
|
|
24
25
|
--dark-color: rgba(255,255,255,0.8);
|
|
26
|
+
*/
|
|
27
|
+
--dark-color: white;
|
|
25
28
|
--light-color: rgba(0,0,0,0.9);
|
|
26
29
|
|
|
27
30
|
--dark-fade: rgba(255,255,255,0.6);
|
|
@@ -894,13 +897,11 @@ body.dark .readme a {
|
|
|
894
897
|
body.dark .line {
|
|
895
898
|
border-bottom: 1px solid var(--dark-thin);
|
|
896
899
|
}
|
|
897
|
-
body.dark .line.selected {
|
|
900
|
+
body.dark .line.selected, body.dark .line:hover {
|
|
898
901
|
/*
|
|
899
902
|
border-left: 6px solid white;
|
|
900
903
|
*/
|
|
901
904
|
background: rgba(80, 100, 70, 0.1);
|
|
902
|
-
border-top: 1px solid rgba(80, 100, 70, 0.1);
|
|
903
|
-
border-bottom: 1px solid rgba(80, 100, 70, 0.1);
|
|
904
905
|
}
|
|
905
906
|
/*
|
|
906
907
|
body.dark .line:hover {
|
|
@@ -910,13 +911,11 @@ body.dark .line:hover {
|
|
|
910
911
|
border-left: 6px solid black;
|
|
911
912
|
}
|
|
912
913
|
*/
|
|
913
|
-
.line.selected {
|
|
914
|
+
.line.selected, .line:hover {
|
|
914
915
|
/*
|
|
915
916
|
border-left: 6px solid black;
|
|
916
917
|
*/
|
|
917
918
|
background: rgba(70, 200, 250, 0.1);
|
|
918
|
-
border-top: 1px solid rgba(70, 200, 250, 0.3);
|
|
919
|
-
border-bottom: 1px solid rgba(70, 200, 250, 0.3);
|
|
920
919
|
}
|
|
921
920
|
.line {
|
|
922
921
|
text-decoration: none;
|
|
@@ -1554,7 +1553,7 @@ nav.error-message {
|
|
|
1554
1553
|
color: #8fd400;
|
|
1555
1554
|
}
|
|
1556
1555
|
*/
|
|
1557
|
-
.running-apps .spinner {
|
|
1556
|
+
.running-apps .spinner, .not-running-apps .spinner {
|
|
1558
1557
|
width: 50px;
|
|
1559
1558
|
text-align: center;
|
|
1560
1559
|
font-size: 20px;
|
|
@@ -263,6 +263,12 @@ body.dark .frame-link.selected {
|
|
|
263
263
|
padding: 5px;
|
|
264
264
|
*/
|
|
265
265
|
}
|
|
266
|
+
.loader .btn:hover {
|
|
267
|
+
color: royalblue;
|
|
268
|
+
}
|
|
269
|
+
.loader .btn {
|
|
270
|
+
padding: 4px 8px;
|
|
271
|
+
}
|
|
266
272
|
.frame-link .tab {
|
|
267
273
|
flex-grow: 1;
|
|
268
274
|
/*
|
|
@@ -536,6 +542,9 @@ body.dark .loader-container {
|
|
|
536
542
|
<a target="<%=src%>" href="<%=src%>" class='btn header-item frame-link' data-index="0">
|
|
537
543
|
<div class='tab'><i class="fa-regular fa-folder-open"></i> Files</div>
|
|
538
544
|
</a>
|
|
545
|
+
<a target="/share/<%=name%>" href="/share/<%=name%>", class="btn header-item frame-link" data-index="1">
|
|
546
|
+
<div class='tab'><i class="fa-solid fa-wifi"></i> Share</div>
|
|
547
|
+
</a>
|
|
539
548
|
</div>
|
|
540
549
|
<div class='m h menu' data-type='h'>
|
|
541
550
|
<% if (config.menu) { %>
|
|
@@ -713,7 +722,10 @@ const nav = (type) => {
|
|
|
713
722
|
const renderSelection = (e, force) => {
|
|
714
723
|
console.log("# 1")
|
|
715
724
|
// if the menu has a default item, or if the url specifies a selection
|
|
716
|
-
let selection =
|
|
725
|
+
let selection = null;
|
|
726
|
+
<% if (type === "run") { %>
|
|
727
|
+
selection = document.querySelector("[data-default]")
|
|
728
|
+
<% } %>
|
|
717
729
|
if (selection || location.hash && location.hash.length > 0) {
|
|
718
730
|
console.log("# 2")
|
|
719
731
|
|
|
@@ -1248,8 +1260,11 @@ document.querySelector("aside").addEventListener("click", async (e) => {
|
|
|
1248
1260
|
e.stopPropagation()
|
|
1249
1261
|
let link = target.closest("[href]")
|
|
1250
1262
|
let src = new URL(link.href).pathname
|
|
1263
|
+
n.Noty({
|
|
1264
|
+
text: `stopping ${src}`,
|
|
1265
|
+
silent: true
|
|
1266
|
+
})
|
|
1251
1267
|
console.log("src", src)
|
|
1252
|
-
debugger
|
|
1253
1268
|
|
|
1254
1269
|
let socket = new Socket()
|
|
1255
1270
|
socket.run({
|
|
@@ -1262,7 +1277,7 @@ document.querySelector("aside").addEventListener("click", async (e) => {
|
|
|
1262
1277
|
if (packet.type === "result") {
|
|
1263
1278
|
socket.close()
|
|
1264
1279
|
n.Noty({
|
|
1265
|
-
text: `stopped
|
|
1280
|
+
text: `stopped`,
|
|
1266
1281
|
silent: true
|
|
1267
1282
|
})
|
|
1268
1283
|
refresh(true)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<html class='columns'>
|
|
2
|
+
<head>
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
4
|
+
<script src="/hotkeys.min.js"></script>
|
|
5
|
+
<script src="/ace/ace.js"></script>
|
|
6
|
+
<script src="/noty.js"></script>
|
|
7
|
+
<script src="/notyq.js"></script>
|
|
8
|
+
<script src="/xterm.js"></script>
|
|
9
|
+
<script src="/xterm-addon-fit.js"></script>
|
|
10
|
+
<script src="/xterm-addon-web-links.js"></script>
|
|
11
|
+
<script src="/xterm-theme.js"></script>
|
|
12
|
+
<script src="/xterm-addon-search.js"></script>
|
|
13
|
+
<script src="/xterm-addon-search-bar.js"></script>
|
|
14
|
+
<script src="/sweetalert2.js"></script>
|
|
15
|
+
<script src="/Socket.js"></script>
|
|
16
|
+
<script src="/common.js"></script>
|
|
17
|
+
<script src="/he.js"></script>
|
|
18
|
+
<script src="/opener.js"></script>
|
|
19
|
+
<script src="/nav.js"></script>
|
|
20
|
+
<script src="/autoexpand.js"></script>
|
|
21
|
+
<script src="/modalinput.js"></script>
|
|
22
|
+
<script src="/simplemodal.js"></script>
|
|
23
|
+
<script src="/waitmodal.js"></script>
|
|
24
|
+
<script src="/notifyinput.js"></script>
|
|
25
|
+
<script src="/report.js"></script>
|
|
26
|
+
<link href="/xterm.min.css" rel="stylesheet" />
|
|
27
|
+
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
|
28
|
+
<link href="/css/solid.min.css" rel="stylesheet">
|
|
29
|
+
<link href="/css/regular.min.css" rel="stylesheet">
|
|
30
|
+
<link href="/css/brands.min.css" rel="stylesheet">
|
|
31
|
+
<link href="/style.css" rel="stylesheet"/>
|
|
32
|
+
<link href="/noty.css" rel="stylesheet"/>
|
|
33
|
+
<% if (agent === "electron") { %>
|
|
34
|
+
<link href="/electron.css" rel="stylesheet"/>
|
|
35
|
+
<% } %>
|
|
36
|
+
<style>
|
|
37
|
+
body.frozen {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
height: 100%;
|
|
41
|
+
}
|
|
42
|
+
#table > table {
|
|
43
|
+
border-left: 2px solid black;
|
|
44
|
+
padding-left: 5px;
|
|
45
|
+
margin: 10px 0;
|
|
46
|
+
}
|
|
47
|
+
.highlight {
|
|
48
|
+
background: yellow;
|
|
49
|
+
width: 100px;
|
|
50
|
+
}
|
|
51
|
+
th {
|
|
52
|
+
background: rgba(0,0,0,0.1);
|
|
53
|
+
padding: 10px;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
}
|
|
56
|
+
td {
|
|
57
|
+
font-size: 12px;
|
|
58
|
+
padding: 10px;
|
|
59
|
+
}
|
|
60
|
+
td.val {
|
|
61
|
+
background: rgba(0,0,0,0.06);
|
|
62
|
+
}
|
|
63
|
+
td.key {
|
|
64
|
+
background: rgba(0,0,0,0.1);
|
|
65
|
+
}
|
|
66
|
+
.button {
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
background: rgba(0,0,0,0.8);
|
|
69
|
+
color: white;
|
|
70
|
+
padding: 5px 10px;
|
|
71
|
+
border-radius: 4px;
|
|
72
|
+
}
|
|
73
|
+
.button:hover {
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
#editor {
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
max-width: 800px;
|
|
79
|
+
padding: 20px;
|
|
80
|
+
}
|
|
81
|
+
header .runner {
|
|
82
|
+
padding: 10px 0 0;
|
|
83
|
+
}
|
|
84
|
+
.disabled {
|
|
85
|
+
opacity: 0.7;
|
|
86
|
+
}
|
|
87
|
+
body.dark .env-item input[type=text] {
|
|
88
|
+
background: white;
|
|
89
|
+
}
|
|
90
|
+
.env-item input[type=text] {
|
|
91
|
+
width: 100%;
|
|
92
|
+
padding: 10px;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
border: none;
|
|
95
|
+
background: rgba(0,0,100, 0.1);
|
|
96
|
+
}
|
|
97
|
+
body.dark .env-item label {
|
|
98
|
+
color: cornflowerblue;
|
|
99
|
+
}
|
|
100
|
+
.env-item label {
|
|
101
|
+
color: royalblue;
|
|
102
|
+
display: block;
|
|
103
|
+
margin-bottom: 5px;
|
|
104
|
+
font-weight: bold;
|
|
105
|
+
font-size: 18px;
|
|
106
|
+
}
|
|
107
|
+
.env-item {
|
|
108
|
+
padding-top: 10px;
|
|
109
|
+
padding-bottom: 30px;
|
|
110
|
+
}
|
|
111
|
+
body.dark .comment {
|
|
112
|
+
border-left: 2px solid white;
|
|
113
|
+
}
|
|
114
|
+
.comment {
|
|
115
|
+
opacity: 0.8;
|
|
116
|
+
border-left: 2px solid black;
|
|
117
|
+
padding-left: 10px;
|
|
118
|
+
}
|
|
119
|
+
#save {
|
|
120
|
+
background: royalblue;
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
123
|
+
<script>
|
|
124
|
+
document.addEventListener("DOMContentLoaded", async () => {
|
|
125
|
+
const n = new N()
|
|
126
|
+
document.querySelector("#save").addEventListener("click", async (e) => {
|
|
127
|
+
// save
|
|
128
|
+
const vals = {}
|
|
129
|
+
const els = document.querySelectorAll(".env-item")
|
|
130
|
+
for(let el of els) {
|
|
131
|
+
let input = el.querySelector("input[type=text]")
|
|
132
|
+
vals[input.id] = input.value
|
|
133
|
+
}
|
|
134
|
+
console.log("vals", vals)
|
|
135
|
+
let res = await fetch("/env", {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/json"
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
filepath: location.pathname.slice(5),
|
|
142
|
+
vals
|
|
143
|
+
})
|
|
144
|
+
}).then((res) => {
|
|
145
|
+
return res.json()
|
|
146
|
+
})
|
|
147
|
+
console.log("res" ,res)
|
|
148
|
+
n.Noty({
|
|
149
|
+
text: `Updated!`
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
</script>
|
|
154
|
+
</head>
|
|
155
|
+
<body class='columns <%=theme%>'>
|
|
156
|
+
<header class='navheader grabbable'>
|
|
157
|
+
<h1>
|
|
158
|
+
<a class='home' href="/"><i class="fa-solid fa-house"></i></a>
|
|
159
|
+
<%=filepath%>
|
|
160
|
+
</h1>
|
|
161
|
+
<div class='runner'>
|
|
162
|
+
<div id='save' class='btn'>
|
|
163
|
+
<span class='save'><i class="fa-solid fa-check"></i> Save</span>
|
|
164
|
+
</div>
|
|
165
|
+
<button class='btn' id='open-fs' data-filepath="<%=filepath%>"><i class="fa-solid fa-eye"></i> Open in File Explorer</button>
|
|
166
|
+
</div>
|
|
167
|
+
</header>
|
|
168
|
+
<main>
|
|
169
|
+
<div id='editor'>
|
|
170
|
+
<% items.forEach((item) => { %>
|
|
171
|
+
<% if (item.type === "comment") { %>
|
|
172
|
+
<div class='comment'><%=item.val.replaceAll(/^[ #]+/g, "")%></div>
|
|
173
|
+
<% } else { %>
|
|
174
|
+
<div class='env-item'>
|
|
175
|
+
<label for="<%=item.key%>"><%=item.key%></label>
|
|
176
|
+
<input id="<%=item.key%>" type="text" value="<%=item.val%>">
|
|
177
|
+
</div>
|
|
178
|
+
<% } %>
|
|
179
|
+
<% }) %>
|
|
180
|
+
</div>
|
|
181
|
+
</main>
|
|
182
|
+
</body>
|
|
183
|
+
</html>
|
|
@@ -146,9 +146,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
146
146
|
this.socket.close()
|
|
147
147
|
this.socket.run({
|
|
148
148
|
method: "fs.write",
|
|
149
|
-
cwd: "{{kernel.homedir}}",
|
|
149
|
+
//cwd: "{{kernel.homedir}}",
|
|
150
150
|
params: {
|
|
151
|
-
path: filepath
|
|
151
|
+
path: `{{path.resolve(kernel.homedir, "${filepath}")}}`,
|
|
152
152
|
text: str,
|
|
153
153
|
}
|
|
154
154
|
}, (packet) => {
|
package/server/views/help.ejs
CHANGED
package/server/views/index.ejs
CHANGED
|
@@ -112,13 +112,7 @@ a.badge {
|
|
|
112
112
|
}
|
|
113
113
|
body.dark .btn {
|
|
114
114
|
color: white;
|
|
115
|
-
/*
|
|
116
|
-
color: cornflowerblue;
|
|
117
|
-
*/
|
|
118
115
|
border: 1px solid rgba(255,255,255,0.3);
|
|
119
|
-
/*
|
|
120
|
-
border: 1px solid rgba(100, 149, 237, 0.3);
|
|
121
|
-
*/
|
|
122
116
|
}
|
|
123
117
|
body.dark .badge {
|
|
124
118
|
background: rgba(255,255,255,0.1);
|
|
@@ -288,16 +282,19 @@ body.dark .browser-options-row {
|
|
|
288
282
|
<div class='uri'><i class="fa-regular fa-folder"></i><%=item.url%></div>
|
|
289
283
|
<div class='title'><%=item.name%></div>
|
|
290
284
|
<div class='description'><%=item.description%></div>
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
285
|
+
<div>
|
|
286
|
+
<button class='btn browse' data-src="<%=item.browser_url%>/browse">
|
|
287
|
+
<i class="fa-regular fa-folder-open"></i>
|
|
288
|
+
</button>
|
|
289
|
+
<% if (item.running_scripts) { %>
|
|
290
|
+
<% item.running_scripts.forEach((s) => { %>
|
|
291
|
+
<button class='btn shutdown' data-src="<%=s.path%>">
|
|
292
|
+
<i class="fa-solid fa-stop"></i> Stop <%=s.name%>
|
|
293
|
+
<i class='fa-solid fa-spin fa-circle-notch'></i>
|
|
294
|
+
</button>
|
|
295
|
+
<% }) %>
|
|
296
|
+
<% } %>
|
|
297
|
+
</div>
|
|
301
298
|
<% if (item.shortcuts && item.shortcuts.length > 0) { %>
|
|
302
299
|
<div class='btns'>
|
|
303
300
|
<% item.shortcuts.forEach((btn) => { %>
|
|
@@ -336,6 +333,9 @@ body.dark .browser-options-row {
|
|
|
336
333
|
<div class='uri'><i class="fa-regular fa-folder"></i><%=item.url%></div>
|
|
337
334
|
<div class='title'><%=item.name%></div>
|
|
338
335
|
<div class='description'><%=item.description%></div>
|
|
336
|
+
<button class='btn browse' data-src="<%=item.browser_url%>/browse">
|
|
337
|
+
<i class="fa-regular fa-folder-open"></i>
|
|
338
|
+
</button>
|
|
339
339
|
<% if (item.shortcuts && item.shortcuts.length > 0) { %>
|
|
340
340
|
<div class='btns'>
|
|
341
341
|
<% item.shortcuts.forEach((btn) => { %>
|
|
@@ -346,6 +346,9 @@ body.dark .browser-options-row {
|
|
|
346
346
|
</div>
|
|
347
347
|
<% } %>
|
|
348
348
|
</div>
|
|
349
|
+
<div class='spinner'>
|
|
350
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
351
|
+
</div>
|
|
349
352
|
</h3>
|
|
350
353
|
</a>
|
|
351
354
|
<% }) %>
|
|
@@ -729,7 +732,6 @@ document.addEventListener("click", (e) => {
|
|
|
729
732
|
e.stopPropagation()
|
|
730
733
|
let src = target.getAttribute("data-src")
|
|
731
734
|
console.log("src", src)
|
|
732
|
-
debugger
|
|
733
735
|
|
|
734
736
|
socket.run({
|
|
735
737
|
method: "kernel.api.stop",
|
|
@@ -756,6 +758,21 @@ document.addEventListener("click", (e) => {
|
|
|
756
758
|
}
|
|
757
759
|
|
|
758
760
|
|
|
761
|
+
debugger
|
|
762
|
+
if (e.target.classList.contains("browse")) {
|
|
763
|
+
target = e.target
|
|
764
|
+
} else {
|
|
765
|
+
target = e.target.closest(".browse")
|
|
766
|
+
}
|
|
767
|
+
if (target) {
|
|
768
|
+
e.preventDefault()
|
|
769
|
+
e.stopPropagation()
|
|
770
|
+
let src = target.getAttribute("data-src")
|
|
771
|
+
location.href = src
|
|
772
|
+
return
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
|
|
759
776
|
if (document.querySelector(".container")) {
|
|
760
777
|
document.querySelector(".container").addEventListener("click", async (e) => {
|
|
761
778
|
let el = e.target.closest(".menu-btn")
|
|
@@ -145,6 +145,10 @@ body.dark .item select {
|
|
|
145
145
|
padding: 10px 20px;
|
|
146
146
|
text-align: center;
|
|
147
147
|
}
|
|
148
|
+
body.dark .btn {
|
|
149
|
+
color: white;
|
|
150
|
+
border: 1px solid rgba(255,255,255,0.3) !important;
|
|
151
|
+
}
|
|
148
152
|
body.light {
|
|
149
153
|
/*
|
|
150
154
|
background: var(--light-nav-bg);
|
|
@@ -298,7 +302,7 @@ hr {
|
|
|
298
302
|
<br>
|
|
299
303
|
<div>Edit the system ENVIRONMENT file to customize pinokio system wide settings, such as where files are stored, terminal behavior, etc.</div>
|
|
300
304
|
<br>
|
|
301
|
-
<a class='btn' href="/
|
|
305
|
+
<a class='btn' href="/env/ENVIRONMENT">Edit ENVIRONMENT</a>
|
|
302
306
|
</div>
|
|
303
307
|
<hr/>
|
|
304
308
|
<div class='troubleshoot'>
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<html class='columns'>
|
|
2
|
+
<head>
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
4
|
+
<script src="/hotkeys.min.js"></script>
|
|
5
|
+
<script src="/ace/ace.js"></script>
|
|
6
|
+
<script src="/noty.js"></script>
|
|
7
|
+
<script src="/notyq.js"></script>
|
|
8
|
+
<script src="/xterm.js"></script>
|
|
9
|
+
<script src="/xterm-addon-fit.js"></script>
|
|
10
|
+
<script src="/xterm-addon-web-links.js"></script>
|
|
11
|
+
<script src="/xterm-theme.js"></script>
|
|
12
|
+
<script src="/xterm-addon-search.js"></script>
|
|
13
|
+
<script src="/xterm-addon-search-bar.js"></script>
|
|
14
|
+
<script src="/sweetalert2.js"></script>
|
|
15
|
+
<script src="/Socket.js"></script>
|
|
16
|
+
<script src="/common.js"></script>
|
|
17
|
+
<script src="/he.js"></script>
|
|
18
|
+
<script src="/opener.js"></script>
|
|
19
|
+
<script src="/nav.js"></script>
|
|
20
|
+
<script src="/autoexpand.js"></script>
|
|
21
|
+
<script src="/modalinput.js"></script>
|
|
22
|
+
<script src="/simplemodal.js"></script>
|
|
23
|
+
<script src="/waitmodal.js"></script>
|
|
24
|
+
<script src="/notifyinput.js"></script>
|
|
25
|
+
<script src="/report.js"></script>
|
|
26
|
+
<link href="/xterm.min.css" rel="stylesheet" />
|
|
27
|
+
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
|
28
|
+
<link href="/css/solid.min.css" rel="stylesheet">
|
|
29
|
+
<link href="/css/regular.min.css" rel="stylesheet">
|
|
30
|
+
<link href="/css/brands.min.css" rel="stylesheet">
|
|
31
|
+
<link href="/style.css" rel="stylesheet"/>
|
|
32
|
+
<link href="/noty.css" rel="stylesheet"/>
|
|
33
|
+
<% if (agent === "electron") { %>
|
|
34
|
+
<link href="/electron.css" rel="stylesheet"/>
|
|
35
|
+
<% } %>
|
|
36
|
+
<style>
|
|
37
|
+
body.frozen {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
height: 100%;
|
|
41
|
+
}
|
|
42
|
+
#table > table {
|
|
43
|
+
border-left: 2px solid black;
|
|
44
|
+
padding-left: 5px;
|
|
45
|
+
margin: 10px 0;
|
|
46
|
+
}
|
|
47
|
+
.highlight {
|
|
48
|
+
background: yellow;
|
|
49
|
+
width: 100px;
|
|
50
|
+
}
|
|
51
|
+
th {
|
|
52
|
+
background: rgba(0,0,0,0.1);
|
|
53
|
+
padding: 10px;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
}
|
|
56
|
+
td {
|
|
57
|
+
font-size: 12px;
|
|
58
|
+
padding: 10px;
|
|
59
|
+
}
|
|
60
|
+
td.val {
|
|
61
|
+
background: rgba(0,0,0,0.06);
|
|
62
|
+
}
|
|
63
|
+
td.key {
|
|
64
|
+
background: rgba(0,0,0,0.1);
|
|
65
|
+
}
|
|
66
|
+
.button {
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
background: rgba(0,0,0,0.8);
|
|
69
|
+
color: white;
|
|
70
|
+
padding: 5px 10px;
|
|
71
|
+
border-radius: 4px;
|
|
72
|
+
}
|
|
73
|
+
.button:hover {
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
#editor {
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
max-width: 800px;
|
|
79
|
+
padding: 20px;
|
|
80
|
+
}
|
|
81
|
+
header .runner {
|
|
82
|
+
padding: 10px 0 0;
|
|
83
|
+
}
|
|
84
|
+
.disabled {
|
|
85
|
+
opacity: 0.7;
|
|
86
|
+
}
|
|
87
|
+
body.dark .env-item input[type=text] {
|
|
88
|
+
background: white;
|
|
89
|
+
}
|
|
90
|
+
.env-item input[type=text] {
|
|
91
|
+
width: 100%;
|
|
92
|
+
padding: 10px;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
border: none;
|
|
95
|
+
background: rgba(0,0,100, 0.1);
|
|
96
|
+
}
|
|
97
|
+
body.dark .env-item label {
|
|
98
|
+
color: cornflowerblue;
|
|
99
|
+
}
|
|
100
|
+
.env-item a {
|
|
101
|
+
color: royalblue;
|
|
102
|
+
text-decoration: none;
|
|
103
|
+
}
|
|
104
|
+
.env-item div {
|
|
105
|
+
padding: 5px;
|
|
106
|
+
}
|
|
107
|
+
.env-item label {
|
|
108
|
+
color: royalblue;
|
|
109
|
+
display: inline-block;
|
|
110
|
+
margin-bottom: 5px;
|
|
111
|
+
font-weight: bold;
|
|
112
|
+
}
|
|
113
|
+
.env-item {
|
|
114
|
+
padding-top: 10px;
|
|
115
|
+
padding-bottom: 30px;
|
|
116
|
+
}
|
|
117
|
+
body.dark .comment {
|
|
118
|
+
border-left: 2px solid white;
|
|
119
|
+
}
|
|
120
|
+
.comment {
|
|
121
|
+
opacity: 0.8;
|
|
122
|
+
border-left: 2px solid black;
|
|
123
|
+
padding-left: 10px;
|
|
124
|
+
}
|
|
125
|
+
#save {
|
|
126
|
+
background: royalblue;
|
|
127
|
+
}
|
|
128
|
+
hr {
|
|
129
|
+
margin: 20px 0;
|
|
130
|
+
background: rgba(255,255,255,0.1);
|
|
131
|
+
border: none;
|
|
132
|
+
height: 1px;
|
|
133
|
+
}
|
|
134
|
+
</style>
|
|
135
|
+
<script>
|
|
136
|
+
document.addEventListener("DOMContentLoaded", async () => {
|
|
137
|
+
const n = new N()
|
|
138
|
+
document.querySelector("#save").addEventListener("click", async (e) => {
|
|
139
|
+
// save
|
|
140
|
+
const vals = {}
|
|
141
|
+
const els = document.querySelectorAll(".env-item input[type=checkbox]")
|
|
142
|
+
for(let el of els) {
|
|
143
|
+
vals[el.id] = el.checked
|
|
144
|
+
}
|
|
145
|
+
let res = await fetch("/env", {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: {
|
|
148
|
+
"Content-Type": "application/json"
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
filepath: "api/" + location.pathname.slice(7) + "/ENVIRONMENT",
|
|
152
|
+
vals
|
|
153
|
+
})
|
|
154
|
+
}).then((res) => {
|
|
155
|
+
return res.json()
|
|
156
|
+
})
|
|
157
|
+
n.Noty({
|
|
158
|
+
text: `Updated! Restart the app to apply the change.`
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
</script>
|
|
163
|
+
</head>
|
|
164
|
+
<body class='columns <%=theme%>'>
|
|
165
|
+
<main>
|
|
166
|
+
<div id='editor'>
|
|
167
|
+
<h1>Share Apps</h1>
|
|
168
|
+
<div>You can open up your local Pinokio node to the outside world, or any other device on the same network, instantly.</div>
|
|
169
|
+
<hr>
|
|
170
|
+
<div class='env-item'>
|
|
171
|
+
<% if (config.PINOKIO_SHARE_CLOUDFLARE.toString() === "true") { %>
|
|
172
|
+
<input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" checked />
|
|
173
|
+
<% } else { %>
|
|
174
|
+
<input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" />
|
|
175
|
+
<% } %>
|
|
176
|
+
<label for="cloudflare">Share Publicly</label>
|
|
177
|
+
<div>This will set the PINOKIO_SHARE_CLOUDFLARE environment variable to true, and share your local app to the public internet, powered by <a href="https://www.cloudflare.com/products/tunnel/" target="_blank" features="browser">Cloudflare Tunnel</a></div>
|
|
178
|
+
</div>
|
|
179
|
+
<div class='env-item'>
|
|
180
|
+
<% if (config.PINOKIO_SHARE_LOCAL.toString() === "true") { %>
|
|
181
|
+
<input type="checkbox" id="PINOKIO_SHARE_LOCAL" name="local" checked />
|
|
182
|
+
<% } else { %>
|
|
183
|
+
<input type="checkbox" id="PINOKIO_SHARE_LOCAL" name="local" />
|
|
184
|
+
<% } %>
|
|
185
|
+
<label for="local">Share with Local Network Devices</label>
|
|
186
|
+
<div>This will set the PINOKIO_SHARE_LOCAL environment variable to true, and share your local app to any device on the same local network</div>
|
|
187
|
+
</div>
|
|
188
|
+
<div class='runner'>
|
|
189
|
+
<div id='save' class='btn'>
|
|
190
|
+
<span class='save'><i class="fa-solid fa-check"></i> Save</span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</main>
|
|
195
|
+
</body>
|
|
196
|
+
</html>
|