pinokiod 6.0.70 → 6.0.72
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/index.js +5 -4
- package/kernel/api/script/index.js +157 -7
- package/kernel/index.js +40 -3
- package/kernel/info.js +3 -4
- package/kernel/shells.js +96 -7
- package/package.json +1 -1
- package/server/index.js +1 -1
- package/server/public/style.css +0 -22
- package/server/views/app.ejs +456 -70
- package/server/views/shell.ejs +6 -0
- package/server/views/terminal.ejs +6 -0
package/kernel/api/index.js
CHANGED
|
@@ -364,8 +364,6 @@ class Api {
|
|
|
364
364
|
delete this.running[requestPath]
|
|
365
365
|
delete this.kernel.memory.local[requestPath]
|
|
366
366
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
367
|
this.ondata({
|
|
370
368
|
id: req.params.id || requestPath,
|
|
371
369
|
type: "disconnect"
|
|
@@ -924,6 +922,10 @@ class Api {
|
|
|
924
922
|
git: this.parentGitURI(request.path),
|
|
925
923
|
cwd,
|
|
926
924
|
origin: request.origin,
|
|
925
|
+
caller: request.caller,
|
|
926
|
+
action: request.action,
|
|
927
|
+
args,
|
|
928
|
+
client: request.client,
|
|
927
929
|
body: script
|
|
928
930
|
}
|
|
929
931
|
// 7. resolve the rpc
|
|
@@ -1202,7 +1204,7 @@ class Api {
|
|
|
1202
1204
|
index: i,
|
|
1203
1205
|
total,
|
|
1204
1206
|
type: "result",
|
|
1205
|
-
data: result
|
|
1207
|
+
data: result
|
|
1206
1208
|
})
|
|
1207
1209
|
|
|
1208
1210
|
|
|
@@ -1246,7 +1248,6 @@ class Api {
|
|
|
1246
1248
|
return
|
|
1247
1249
|
}
|
|
1248
1250
|
|
|
1249
|
-
|
|
1250
1251
|
if (typeof rpc.next === "undefined" || rpc.next === null) {
|
|
1251
1252
|
|
|
1252
1253
|
|
|
@@ -1,9 +1,146 @@
|
|
|
1
|
-
const path = require('path')
|
|
2
1
|
class Script {
|
|
2
|
+
currentParent(req) {
|
|
3
|
+
if (req && req.parent && typeof req.parent === "object") {
|
|
4
|
+
return req.parent
|
|
5
|
+
}
|
|
6
|
+
return null
|
|
7
|
+
}
|
|
8
|
+
currentPath(req) {
|
|
9
|
+
const parent = this.currentParent(req)
|
|
10
|
+
if (parent && parent.path) {
|
|
11
|
+
return parent.path
|
|
12
|
+
}
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
currentSessionId(req) {
|
|
16
|
+
if (req && typeof req.id === "string" && req.id.trim()) {
|
|
17
|
+
return req.id
|
|
18
|
+
}
|
|
19
|
+
const parent = this.currentParent(req)
|
|
20
|
+
if (parent && typeof parent.id === "string" && parent.id.trim()) {
|
|
21
|
+
return parent.id
|
|
22
|
+
}
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
currentCaller(req) {
|
|
26
|
+
if (req && typeof req.caller === "string" && req.caller.trim()) {
|
|
27
|
+
return req.caller
|
|
28
|
+
}
|
|
29
|
+
const parent = this.currentParent(req)
|
|
30
|
+
if (parent && typeof parent.caller === "string" && parent.caller.trim()) {
|
|
31
|
+
return parent.caller
|
|
32
|
+
}
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
currentClient(req) {
|
|
36
|
+
if (req && req.client) {
|
|
37
|
+
return req.client
|
|
38
|
+
}
|
|
39
|
+
const parent = this.currentParent(req)
|
|
40
|
+
if (parent && parent.client) {
|
|
41
|
+
return parent.client
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
currentOrigin(req) {
|
|
46
|
+
if (req && typeof req.origin === "string" && req.origin.trim()) {
|
|
47
|
+
return req.origin
|
|
48
|
+
}
|
|
49
|
+
const parent = this.currentParent(req)
|
|
50
|
+
if (parent && typeof parent.origin === "string" && parent.origin.trim()) {
|
|
51
|
+
return parent.origin
|
|
52
|
+
}
|
|
53
|
+
return undefined
|
|
54
|
+
}
|
|
55
|
+
buildStartRequest(req, uri, input, target) {
|
|
56
|
+
const nextParams = Object.assign({}, req && req.params ? req.params : {}, {
|
|
57
|
+
uri,
|
|
58
|
+
params: input
|
|
59
|
+
})
|
|
60
|
+
const preserveSession = !!(target && target.self)
|
|
61
|
+
return {
|
|
62
|
+
id: preserveSession ? this.currentSessionId(req) : undefined,
|
|
63
|
+
caller: preserveSession ? this.currentCaller(req) : undefined,
|
|
64
|
+
cwd: req ? req.cwd : undefined,
|
|
65
|
+
client: this.currentClient(req),
|
|
66
|
+
origin: this.currentOrigin(req),
|
|
67
|
+
params: nextParams
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
resolveRestartTarget(req, kernel) {
|
|
71
|
+
const hasParams = req && req.params && typeof req.params === "object"
|
|
72
|
+
const requestedUri = hasParams && typeof req.params.uri === "string" && req.params.uri.trim()
|
|
73
|
+
? req.params.uri
|
|
74
|
+
: null
|
|
75
|
+
const currentPath = this.currentPath(req)
|
|
76
|
+
if (!requestedUri) {
|
|
77
|
+
if (!currentPath) {
|
|
78
|
+
throw new Error("script.restart requires params.uri when called outside a running script")
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
displayUri: currentPath,
|
|
82
|
+
startUri: currentPath,
|
|
83
|
+
stopUri: currentPath,
|
|
84
|
+
self: true,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const stopUri = kernel.api.filePath(requestedUri, req.cwd)
|
|
88
|
+
return {
|
|
89
|
+
displayUri: requestedUri,
|
|
90
|
+
startUri: requestedUri,
|
|
91
|
+
stopUri,
|
|
92
|
+
self: currentPath ? stopUri === currentPath : false,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
resolveRestartInput(req, kernel, target) {
|
|
96
|
+
const hasExplicitParams = !!(req && req.params && typeof req.params === "object" && Object.prototype.hasOwnProperty.call(req.params, "params"))
|
|
97
|
+
if (hasExplicitParams) {
|
|
98
|
+
return req.params.params
|
|
99
|
+
}
|
|
100
|
+
if (target && target.self && req && req.parent && Object.prototype.hasOwnProperty.call(req.parent, "args")) {
|
|
101
|
+
return req.parent.args
|
|
102
|
+
}
|
|
103
|
+
if (kernel && kernel.memory && kernel.memory.args && target) {
|
|
104
|
+
return kernel.memory.args[target.stopUri]
|
|
105
|
+
}
|
|
106
|
+
return undefined
|
|
107
|
+
}
|
|
108
|
+
scheduleStart(req, ondata, kernel) {
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
this.start(req, ondata, kernel).catch((e) => {
|
|
111
|
+
const stack = e && e.stack ? e.stack : String(e)
|
|
112
|
+
ondata({ raw: `\r\nFailed to start ${req.params.uri}\r\n${stack}\r\n` })
|
|
113
|
+
})
|
|
114
|
+
}, 0)
|
|
115
|
+
}
|
|
3
116
|
async start(req, ondata, kernel) {
|
|
4
117
|
let res = await this.run(req, ondata, kernel)
|
|
5
118
|
return res
|
|
6
119
|
}
|
|
120
|
+
async restart(req, ondata, kernel) {
|
|
121
|
+
if (!req.params) {
|
|
122
|
+
req.params = {}
|
|
123
|
+
}
|
|
124
|
+
const target = this.resolveRestartTarget(req, kernel)
|
|
125
|
+
const input = this.resolveRestartInput(req, kernel, target)
|
|
126
|
+
const sessionId = target.self ? this.currentSessionId(req) : undefined
|
|
127
|
+
ondata({
|
|
128
|
+
id: sessionId || target.stopUri,
|
|
129
|
+
}, "restart")
|
|
130
|
+
const stopRequest = sessionId
|
|
131
|
+
? { params: { id: sessionId } }
|
|
132
|
+
: { params: { uri: target.stopUri } }
|
|
133
|
+
await kernel.api.stop(stopRequest)
|
|
134
|
+
ondata({ raw: `\r\nRestarting ${target.displayUri}\r\n` })
|
|
135
|
+
const startRequest = this.buildStartRequest(req, target.startUri, input, target)
|
|
136
|
+
this.scheduleStart(startRequest, ondata, kernel)
|
|
137
|
+
return {
|
|
138
|
+
uri: target.displayUri,
|
|
139
|
+
scheduled: true,
|
|
140
|
+
self: target.self,
|
|
141
|
+
params: input,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
7
144
|
async stop(req, ondata, kernel) {
|
|
8
145
|
/*
|
|
9
146
|
{
|
|
@@ -20,7 +157,7 @@ class Script {
|
|
|
20
157
|
}
|
|
21
158
|
for(let uri of uris) {
|
|
22
159
|
kernel.api.stop({ params: { uri } })
|
|
23
|
-
ondata({ raw: `\r\nStopped ${
|
|
160
|
+
ondata({ raw: `\r\nStopped ${uri}\r\n` })
|
|
24
161
|
}
|
|
25
162
|
}
|
|
26
163
|
async run(req, ondata, kernel) {
|
|
@@ -50,12 +187,25 @@ class Script {
|
|
|
50
187
|
// if not already running, start.
|
|
51
188
|
let uri = await this.download(req, ondata, kernel)
|
|
52
189
|
let res = await new Promise((resolve, reject) => {
|
|
53
|
-
|
|
190
|
+
let request = {
|
|
54
191
|
uri,
|
|
55
192
|
input: req.params.params,
|
|
56
|
-
client: req
|
|
57
|
-
|
|
58
|
-
|
|
193
|
+
client: this.currentClient(req),
|
|
194
|
+
}
|
|
195
|
+
if (req.id) {
|
|
196
|
+
request.id = req.id
|
|
197
|
+
}
|
|
198
|
+
const caller = this.currentCaller(req)
|
|
199
|
+
if (caller) {
|
|
200
|
+
request.caller = caller
|
|
201
|
+
} else if (req.parent && req.parent.path) {
|
|
202
|
+
request.caller = req.parent.path
|
|
203
|
+
}
|
|
204
|
+
const origin = this.currentOrigin(req)
|
|
205
|
+
if (origin) {
|
|
206
|
+
request.origin = origin
|
|
207
|
+
}
|
|
208
|
+
kernel.api.process(request, (r) => {
|
|
59
209
|
resolve(r.input)
|
|
60
210
|
})
|
|
61
211
|
})
|
|
@@ -125,7 +275,7 @@ class Script {
|
|
|
125
275
|
|
|
126
276
|
await kernel.api.init()
|
|
127
277
|
} else {
|
|
128
|
-
uri =
|
|
278
|
+
uri = kernel.api.filePath(req.params.uri, req.cwd)
|
|
129
279
|
}
|
|
130
280
|
return uri
|
|
131
281
|
}
|
package/kernel/index.js
CHANGED
|
@@ -250,10 +250,29 @@ class Kernel {
|
|
|
250
250
|
}
|
|
251
251
|
return o
|
|
252
252
|
}
|
|
253
|
+
scopedMemoryEntry(store, filePath) {
|
|
254
|
+
if (!store || !filePath) {
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
const exact = store[filePath]
|
|
258
|
+
if (exact) {
|
|
259
|
+
return exact
|
|
260
|
+
}
|
|
261
|
+
const wantsExactMatch = this.stripSessionParam(filePath) !== filePath
|
|
262
|
+
if (wantsExactMatch) {
|
|
263
|
+
return null
|
|
264
|
+
}
|
|
265
|
+
for (const [key, value] of Object.entries(store)) {
|
|
266
|
+
if (value && this.stripSessionParam(key) === filePath) {
|
|
267
|
+
return value
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return null
|
|
271
|
+
}
|
|
253
272
|
local(...args) {
|
|
254
273
|
// get local variables at path
|
|
255
274
|
let filePath = this.api.filePath(path.resolve(...args))
|
|
256
|
-
let v = this.memory.local
|
|
275
|
+
let v = this.scopedMemoryEntry(this.memory.local, filePath)
|
|
257
276
|
//let v = this.memory.local[path.resolve(...args)]
|
|
258
277
|
if (v) {
|
|
259
278
|
return v
|
|
@@ -264,7 +283,7 @@ class Kernel {
|
|
|
264
283
|
global(...args) {
|
|
265
284
|
// get local variables at path
|
|
266
285
|
let filePath = this.api.filePath(path.resolve(...args))
|
|
267
|
-
let v = this.memory.global
|
|
286
|
+
let v = this.scopedMemoryEntry(this.memory.global, filePath)
|
|
268
287
|
// let v = this.memory.global[path.resolve(...args)]
|
|
269
288
|
if (v) {
|
|
270
289
|
return v
|
|
@@ -275,9 +294,27 @@ class Kernel {
|
|
|
275
294
|
running(...args) {
|
|
276
295
|
return this.status(path.resolve(...args))
|
|
277
296
|
}
|
|
297
|
+
stripSessionParam(value) {
|
|
298
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
299
|
+
return value
|
|
300
|
+
}
|
|
301
|
+
return value.replace(/([?&])session=[^&]+/g, "").replace(/[?&]$/, "")
|
|
302
|
+
}
|
|
278
303
|
status(uri, cwd) {
|
|
279
304
|
let id = this.api.filePath(uri, cwd)
|
|
280
|
-
|
|
305
|
+
if (this.api.running[id]) {
|
|
306
|
+
return this.api.running[id]
|
|
307
|
+
}
|
|
308
|
+
const wantsExactMatch = this.stripSessionParam(id) !== id
|
|
309
|
+
if (wantsExactMatch) {
|
|
310
|
+
return false
|
|
311
|
+
}
|
|
312
|
+
for (const [key, value] of Object.entries(this.api.running || {})) {
|
|
313
|
+
if (value && this.stripSessionParam(key) === id) {
|
|
314
|
+
return value
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return false
|
|
281
318
|
}
|
|
282
319
|
url (origin, _path, _type) {
|
|
283
320
|
/*
|
package/kernel/info.js
CHANGED
|
@@ -49,8 +49,7 @@ class Info {
|
|
|
49
49
|
running(...args) {
|
|
50
50
|
let cwd = path.dirname(this.caller())
|
|
51
51
|
let resolved_path = path.resolve(cwd, ...args)
|
|
52
|
-
|
|
53
|
-
return running ? running : false
|
|
52
|
+
return this.kernel.status(resolved_path)
|
|
54
53
|
}
|
|
55
54
|
exists(...args) {
|
|
56
55
|
let cwd = path.dirname(this.caller())
|
|
@@ -61,12 +60,12 @@ class Info {
|
|
|
61
60
|
local(...args) {
|
|
62
61
|
let cwd = path.dirname(this.caller())
|
|
63
62
|
let resolved_path = path.resolve(cwd, ...args)
|
|
64
|
-
return this.kernel.memory.local
|
|
63
|
+
return this.kernel.scopedMemoryEntry(this.kernel.memory.local, resolved_path) || {}
|
|
65
64
|
}
|
|
66
65
|
global(...args) {
|
|
67
66
|
let cwd = path.dirname(this.caller())
|
|
68
67
|
let resolved_path = path.resolve(cwd, ...args)
|
|
69
|
-
return this.kernel.memory.global
|
|
68
|
+
return this.kernel.scopedMemoryEntry(this.kernel.memory.global, resolved_path) || {}
|
|
70
69
|
}
|
|
71
70
|
scriptsByApi() {
|
|
72
71
|
if (!this.kernel || !this.kernel.memory || !this.kernel.memory.local) {
|
package/kernel/shells.js
CHANGED
|
@@ -182,6 +182,60 @@ class Shells {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
const parentMeta = params.$parent || null
|
|
185
|
+
const queueTriggerAction = async (action, eventMatch) => {
|
|
186
|
+
if (!action) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
if (!parentMeta || !parentMeta.path) {
|
|
190
|
+
throw new Error(`unable to trigger "${action}" without a parent script`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const runningKey = parentMeta.id || parentMeta.path
|
|
194
|
+
if (runningKey && !this.kernel.api.running[runningKey]) {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let triggerScript = parentMeta.body
|
|
199
|
+
let triggerCwd = parentMeta.cwd
|
|
200
|
+
if (!triggerScript || !Array.isArray(triggerScript[action])) {
|
|
201
|
+
const resolved = await this.kernel.api.resolveScript(parentMeta.path)
|
|
202
|
+
triggerScript = resolved.script
|
|
203
|
+
if (!triggerCwd) {
|
|
204
|
+
triggerCwd = resolved.cwd
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const triggerSteps = (triggerScript && Array.isArray(triggerScript[action])) ? triggerScript[action] : null
|
|
209
|
+
if (!triggerSteps || triggerSteps.length === 0) {
|
|
210
|
+
throw new Error(`missing or invalid attribute: ${action}`)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const request = {
|
|
214
|
+
id: parentMeta.id,
|
|
215
|
+
uri: parentMeta.uri,
|
|
216
|
+
path: parentMeta.path,
|
|
217
|
+
git: parentMeta.git,
|
|
218
|
+
cwd: triggerCwd || parentMeta.cwd,
|
|
219
|
+
origin: parentMeta.origin,
|
|
220
|
+
caller: parentMeta.caller,
|
|
221
|
+
client: parentMeta.client,
|
|
222
|
+
action
|
|
223
|
+
}
|
|
224
|
+
const input = {
|
|
225
|
+
event: eventMatch,
|
|
226
|
+
trigger: action
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.kernel.api.queue(
|
|
230
|
+
request,
|
|
231
|
+
triggerSteps[0],
|
|
232
|
+
input,
|
|
233
|
+
0,
|
|
234
|
+
triggerSteps.length,
|
|
235
|
+
triggerCwd || parentMeta.cwd,
|
|
236
|
+
parentMeta.args
|
|
237
|
+
)
|
|
238
|
+
}
|
|
185
239
|
|
|
186
240
|
const plannedShell = params.shell || (this.kernel.platform === 'win32' ? 'cmd.exe' : 'bash')
|
|
187
241
|
await this.ensureBracketedPasteSupport(plannedShell)
|
|
@@ -193,6 +247,10 @@ class Shells {
|
|
|
193
247
|
|
|
194
248
|
let m
|
|
195
249
|
let matched_index
|
|
250
|
+
const onceHandlers = new Set()
|
|
251
|
+
const handlerLastMatchEnd = new Map()
|
|
252
|
+
let liveEventBuffer = ""
|
|
253
|
+
let liveEventOffset = 0
|
|
196
254
|
|
|
197
255
|
// if error doesn't exist, add default "error:" event
|
|
198
256
|
if (!params.on) {
|
|
@@ -224,6 +282,8 @@ class Shells {
|
|
|
224
282
|
event: <regex>,
|
|
225
283
|
done: true|false,
|
|
226
284
|
kill: true|false,
|
|
285
|
+
trigger: <alternate script action>,
|
|
286
|
+
once: true|false,
|
|
227
287
|
debug: true|false,
|
|
228
288
|
notify: {
|
|
229
289
|
title,
|
|
@@ -236,9 +296,20 @@ class Shells {
|
|
|
236
296
|
}
|
|
237
297
|
*/
|
|
238
298
|
try {
|
|
299
|
+
const rawChunk = typeof stream.raw === "string"
|
|
300
|
+
? stream.raw.replaceAll(/[\r\n]/g, "")
|
|
301
|
+
: ""
|
|
302
|
+
if (rawChunk.length > 0) {
|
|
303
|
+
liveEventBuffer = (liveEventBuffer + rawChunk).slice(-300)
|
|
304
|
+
liveEventOffset += rawChunk.length
|
|
305
|
+
}
|
|
306
|
+
const liveEventBufferStart = Math.max(0, liveEventOffset - liveEventBuffer.length)
|
|
239
307
|
if (params.on && Array.isArray(params.on)) {
|
|
240
308
|
for(let i=0; i<params.on.length; i++) {
|
|
241
309
|
let handler = params.on[i]
|
|
310
|
+
if (handler.once && onceHandlers.has(i)) {
|
|
311
|
+
continue
|
|
312
|
+
}
|
|
242
313
|
// regexify
|
|
243
314
|
//let matches = /^\/([^\/]+)\/([dgimsuy]*)$/.exec(handler.event)
|
|
244
315
|
if (handler.event) {
|
|
@@ -251,6 +322,9 @@ class Shells {
|
|
|
251
322
|
let re = new RegExp(matches[1], matches[2])
|
|
252
323
|
let test = re.exec(sh.monitor)
|
|
253
324
|
if (test && test.length > 0) {
|
|
325
|
+
if (handler.once) {
|
|
326
|
+
onceHandlers.add(i)
|
|
327
|
+
}
|
|
254
328
|
// reset monitor
|
|
255
329
|
sh.monitor = ""
|
|
256
330
|
let params = this.kernel.template.render(handler.notify, { event: test })
|
|
@@ -265,20 +339,35 @@ class Shells {
|
|
|
265
339
|
matches[2] += "g" // if g option is not included, include it (need it for matchAll)
|
|
266
340
|
}
|
|
267
341
|
let re = new RegExp(matches[1], matches[2])
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
let rendered_event = [...
|
|
342
|
+
if (liveEventBuffer.length > 0) {
|
|
343
|
+
const lastHandledEnd = handlerLastMatchEnd.get(i) || 0
|
|
344
|
+
let rendered_event = [...liveEventBuffer.matchAll(re)].filter((match) => {
|
|
345
|
+
const absoluteEnd = liveEventBufferStart + match.index + match[0].length
|
|
346
|
+
return absoluteEnd > lastHandledEnd
|
|
347
|
+
})
|
|
271
348
|
// 3. if the rendered expression is truthy, run the "run" script
|
|
272
349
|
if (rendered_event.length > 0) {
|
|
350
|
+
if (handler.once) {
|
|
351
|
+
onceHandlers.add(i)
|
|
352
|
+
}
|
|
353
|
+
const lastMatch = rendered_event[rendered_event.length - 1]
|
|
354
|
+
handlerLastMatchEnd.set(i, liveEventBufferStart + lastMatch.index + lastMatch[0].length)
|
|
273
355
|
stream.matches = rendered_event
|
|
356
|
+
m = rendered_event[0]
|
|
357
|
+
matched_index = i
|
|
358
|
+
if (typeof handler.trigger === "string" && handler.trigger.trim()) {
|
|
359
|
+
const triggerAction = handler.trigger.trim()
|
|
360
|
+
queueTriggerAction(triggerAction, rendered_event[0]).catch((e) => {
|
|
361
|
+
console.log("Trigger error", e)
|
|
362
|
+
if (ondata) {
|
|
363
|
+
ondata({ raw: (e && e.stack) ? e.stack : String(e) })
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
}
|
|
274
367
|
if (handler.kill) {
|
|
275
|
-
m = rendered_event[0]
|
|
276
|
-
matched_index = i
|
|
277
368
|
sh.kill()
|
|
278
369
|
}
|
|
279
370
|
if (handler.done) {
|
|
280
|
-
m = rendered_event[0]
|
|
281
|
-
matched_index = i
|
|
282
371
|
sh.continue()
|
|
283
372
|
}
|
|
284
373
|
}
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -3714,7 +3714,7 @@ class Server {
|
|
|
3714
3714
|
let relpath = path.relative(this.kernel.homedir, fullpath)
|
|
3715
3715
|
if (relpath.startsWith("api")) {
|
|
3716
3716
|
// api script
|
|
3717
|
-
if (this.kernel.
|
|
3717
|
+
if (this.kernel.status(fullpath)) {
|
|
3718
3718
|
menuitem.running = true
|
|
3719
3719
|
}
|
|
3720
3720
|
} else {
|
package/server/public/style.css
CHANGED
|
@@ -3086,28 +3086,6 @@ aside .qr {
|
|
|
3086
3086
|
.app-icon {
|
|
3087
3087
|
display: block;
|
|
3088
3088
|
}
|
|
3089
|
-
header .runner {
|
|
3090
|
-
gap: 6px;
|
|
3091
|
-
}
|
|
3092
|
-
header .runner .btn {
|
|
3093
|
-
display: flex;
|
|
3094
|
-
flex-direction: column;
|
|
3095
|
-
align-items: center;
|
|
3096
|
-
gap: 2px;
|
|
3097
|
-
padding: 6px 8px;
|
|
3098
|
-
font-size: 10px;
|
|
3099
|
-
line-height: 1.1;
|
|
3100
|
-
text-align: center;
|
|
3101
|
-
}
|
|
3102
|
-
header .runner .btn span {
|
|
3103
|
-
display: flex;
|
|
3104
|
-
flex-direction: column;
|
|
3105
|
-
align-items: center;
|
|
3106
|
-
gap: 2px;
|
|
3107
|
-
}
|
|
3108
|
-
header .runner .btn i {
|
|
3109
|
-
font-size: 14px;
|
|
3110
|
-
}
|
|
3111
3089
|
aside .tab.submenu {
|
|
3112
3090
|
padding: 10px;
|
|
3113
3091
|
}
|
package/server/views/app.ejs
CHANGED
|
@@ -4325,6 +4325,11 @@ body.dark .snapshot-footer-input input {
|
|
|
4325
4325
|
.snapshot-footer-input input::placeholder {
|
|
4326
4326
|
opacity: 0.7;
|
|
4327
4327
|
}
|
|
4328
|
+
.mobile-bottom-nav,
|
|
4329
|
+
.mobile-sheet-actions,
|
|
4330
|
+
.mobile-menu-fallback {
|
|
4331
|
+
display: none;
|
|
4332
|
+
}
|
|
4328
4333
|
header.navheader h1 {
|
|
4329
4334
|
position: relative;
|
|
4330
4335
|
}
|
|
@@ -4348,7 +4353,20 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4348
4353
|
}
|
|
4349
4354
|
@media only screen and (max-width: 768px) {
|
|
4350
4355
|
:root {
|
|
4351
|
-
--mobile-bottom-bar-height:
|
|
4356
|
+
--mobile-bottom-bar-height: calc(33px + env(safe-area-inset-bottom));
|
|
4357
|
+
}
|
|
4358
|
+
header.navheader {
|
|
4359
|
+
position: fixed !important;
|
|
4360
|
+
left: -10000px !important;
|
|
4361
|
+
top: 0 !important;
|
|
4362
|
+
width: auto !important;
|
|
4363
|
+
min-width: 0 !important;
|
|
4364
|
+
max-width: none !important;
|
|
4365
|
+
opacity: 0 !important;
|
|
4366
|
+
pointer-events: none !important;
|
|
4367
|
+
}
|
|
4368
|
+
header.navheader .mode-selector {
|
|
4369
|
+
display: none !important;
|
|
4352
4370
|
}
|
|
4353
4371
|
body:not(.mobile-menu-open) .appcanvas > aside {
|
|
4354
4372
|
display: none;
|
|
@@ -4356,82 +4374,119 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4356
4374
|
body:not(.mobile-menu-open) .appcanvas-resizer {
|
|
4357
4375
|
display: none;
|
|
4358
4376
|
}
|
|
4359
|
-
header.navheader .mode-selector .community-mode-toggle {
|
|
4360
|
-
display: flex;
|
|
4361
|
-
}
|
|
4362
|
-
#menu-mobile {
|
|
4363
|
-
display: flex;
|
|
4364
|
-
}
|
|
4365
4377
|
body.mobile-menu-open #menu-mobile-close {
|
|
4366
4378
|
display: inline-flex;
|
|
4367
4379
|
}
|
|
4368
4380
|
body.mobile-menu-open #layout-toggle {
|
|
4369
4381
|
display: none;
|
|
4370
4382
|
}
|
|
4371
|
-
|
|
4372
|
-
|
|
4383
|
+
.browserview-shell {
|
|
4384
|
+
box-sizing: border-box;
|
|
4373
4385
|
}
|
|
4374
|
-
|
|
4375
|
-
position:
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
bottom: 0;
|
|
4379
|
-
top: auto;
|
|
4380
|
-
transform: none;
|
|
4381
|
-
margin: 0;
|
|
4382
|
-
padding: 6px 10px calc(6px + env(safe-area-inset-bottom)) 10px;
|
|
4383
|
-
border-radius: 0;
|
|
4384
|
-
border-top: 1px solid rgba(0,0,0,0.08);
|
|
4385
|
-
background: rgba(255,255,255,0.95);
|
|
4386
|
-
flex-direction: row;
|
|
4386
|
+
.mobile-bottom-nav {
|
|
4387
|
+
position: relative;
|
|
4388
|
+
display: flex;
|
|
4389
|
+
flex: 0 0 auto;
|
|
4387
4390
|
align-items: center;
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4391
|
+
gap: 3px;
|
|
4392
|
+
width: 100%;
|
|
4393
|
+
min-height: 32px;
|
|
4394
|
+
padding: 0 4px env(safe-area-inset-bottom) 4px;
|
|
4395
|
+
border-top: 1px solid rgba(0,0,0,0.08);
|
|
4396
|
+
background: rgba(255,255,255,0.96);
|
|
4397
|
+
backdrop-filter: blur(20px);
|
|
4398
|
+
z-index: 10000002;
|
|
4399
|
+
box-sizing: border-box;
|
|
4393
4400
|
}
|
|
4394
|
-
body.dark
|
|
4401
|
+
body.dark .mobile-bottom-nav {
|
|
4395
4402
|
background: rgba(15, 17, 21, 0.96);
|
|
4396
4403
|
border-top-color: rgba(255,255,255,0.08);
|
|
4397
|
-
box-shadow: 0 -6px 18px rgba(2, 6, 20, 0.45);
|
|
4398
4404
|
}
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4405
|
+
.mobile-bottom-nav__button {
|
|
4406
|
+
flex: 0 0 28px;
|
|
4407
|
+
min-width: 28px;
|
|
4408
|
+
max-width: 28px;
|
|
4409
|
+
min-height: 28px;
|
|
4410
|
+
display: flex;
|
|
4404
4411
|
align-items: center;
|
|
4405
4412
|
justify-content: center;
|
|
4406
|
-
gap:
|
|
4407
|
-
padding:
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4413
|
+
gap: 0;
|
|
4414
|
+
padding: 0;
|
|
4415
|
+
border-radius: 7px;
|
|
4416
|
+
background: transparent;
|
|
4417
|
+
color: #64748b;
|
|
4418
|
+
text-decoration: none;
|
|
4411
4419
|
}
|
|
4412
|
-
|
|
4413
|
-
|
|
4420
|
+
.mobile-bottom-nav__button i {
|
|
4421
|
+
font-size: 14px;
|
|
4414
4422
|
}
|
|
4415
|
-
|
|
4423
|
+
.mobile-bottom-nav__button.selected {
|
|
4424
|
+
background: rgba(10, 132, 255, 0.12);
|
|
4416
4425
|
color: #0a84ff;
|
|
4417
|
-
background: transparent;
|
|
4418
|
-
font-weight: 600;
|
|
4419
4426
|
}
|
|
4420
|
-
body.dark
|
|
4421
|
-
color:
|
|
4427
|
+
body.dark .mobile-bottom-nav__button {
|
|
4428
|
+
color: rgba(148, 163, 184, 0.9);
|
|
4422
4429
|
}
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4430
|
+
body.dark .mobile-bottom-nav__button.selected {
|
|
4431
|
+
background: rgba(10, 132, 255, 0.18);
|
|
4432
|
+
color: #63a9ff;
|
|
4426
4433
|
}
|
|
4427
|
-
|
|
4434
|
+
.mobile-bottom-nav__logo {
|
|
4435
|
+
width: 25px;
|
|
4436
|
+
height: 25px;
|
|
4437
|
+
object-fit: contain;
|
|
4428
4438
|
display: block;
|
|
4439
|
+
}
|
|
4440
|
+
.mobile-bottom-nav__modes {
|
|
4441
|
+
flex: 1 1 auto;
|
|
4442
|
+
min-width: 0;
|
|
4443
|
+
display: flex;
|
|
4444
|
+
align-items: center;
|
|
4445
|
+
gap: 0;
|
|
4446
|
+
min-height: 26px;
|
|
4447
|
+
padding: 0;
|
|
4448
|
+
border-radius: 8px;
|
|
4449
|
+
border: 1px solid rgba(0,0,0,0.08);
|
|
4450
|
+
background: rgba(15, 23, 42, 0.06);
|
|
4451
|
+
}
|
|
4452
|
+
body.dark .mobile-bottom-nav__modes {
|
|
4453
|
+
border-color: rgba(255,255,255,0.08);
|
|
4454
|
+
background: rgba(255,255,255,0.06);
|
|
4455
|
+
}
|
|
4456
|
+
.mobile-mode-segment {
|
|
4457
|
+
flex: 1 1 0;
|
|
4458
|
+
min-width: 0;
|
|
4459
|
+
display: flex;
|
|
4460
|
+
align-items: center;
|
|
4461
|
+
justify-content: center;
|
|
4462
|
+
min-height: 24px;
|
|
4463
|
+
padding: 0 4px;
|
|
4464
|
+
border-radius: 7px;
|
|
4465
|
+
color: #475569;
|
|
4466
|
+
text-decoration: none;
|
|
4429
4467
|
font-size: 10px;
|
|
4468
|
+
font-weight: 700;
|
|
4430
4469
|
line-height: 1;
|
|
4470
|
+
text-align: center;
|
|
4471
|
+
white-space: nowrap;
|
|
4431
4472
|
}
|
|
4432
|
-
.
|
|
4433
|
-
|
|
4434
|
-
|
|
4473
|
+
.mobile-mode-segment.selected {
|
|
4474
|
+
background: #111827;
|
|
4475
|
+
color: #fff;
|
|
4476
|
+
}
|
|
4477
|
+
body.dark .mobile-mode-segment {
|
|
4478
|
+
color: rgba(226, 232, 240, 0.88);
|
|
4479
|
+
}
|
|
4480
|
+
body.dark .mobile-mode-segment.selected {
|
|
4481
|
+
background: #fff;
|
|
4482
|
+
color: #0f1115;
|
|
4483
|
+
}
|
|
4484
|
+
.appcanvas.community-mode .mobile-bottom-nav .mobile-mode-segment.selected {
|
|
4485
|
+
background: transparent;
|
|
4486
|
+
color: #475569;
|
|
4487
|
+
}
|
|
4488
|
+
body.dark .appcanvas.community-mode .mobile-bottom-nav .mobile-mode-segment.selected {
|
|
4489
|
+
color: rgba(226, 232, 240, 0.88);
|
|
4435
4490
|
}
|
|
4436
4491
|
body.mobile-menu-open .appcanvas > aside {
|
|
4437
4492
|
display: flex;
|
|
@@ -4484,6 +4539,76 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4484
4539
|
gap: 8px;
|
|
4485
4540
|
width: 100%;
|
|
4486
4541
|
}
|
|
4542
|
+
body.mobile-menu-open .appcanvas > aside .mobile-sheet-actions {
|
|
4543
|
+
display: flex;
|
|
4544
|
+
align-items: center;
|
|
4545
|
+
gap: 8px;
|
|
4546
|
+
width: 100%;
|
|
4547
|
+
overflow-x: auto;
|
|
4548
|
+
overflow-y: hidden;
|
|
4549
|
+
padding-bottom: 2px;
|
|
4550
|
+
scrollbar-width: none;
|
|
4551
|
+
}
|
|
4552
|
+
body.mobile-menu-open .appcanvas > aside .mobile-sheet-actions::-webkit-scrollbar,
|
|
4553
|
+
body.mobile-menu-open .mobile-menu-fallback .mobile-sheet-actions::-webkit-scrollbar {
|
|
4554
|
+
display: none;
|
|
4555
|
+
}
|
|
4556
|
+
body.mobile-menu-open .appcanvas > aside .mobile-sheet-action,
|
|
4557
|
+
body.mobile-menu-open .mobile-menu-fallback .mobile-sheet-action {
|
|
4558
|
+
flex: 0 0 auto;
|
|
4559
|
+
width: 40px;
|
|
4560
|
+
height: 40px;
|
|
4561
|
+
min-width: 40px;
|
|
4562
|
+
min-height: 40px;
|
|
4563
|
+
padding: 0;
|
|
4564
|
+
border-radius: 12px;
|
|
4565
|
+
border: 1px solid rgba(0,0,0,0.06);
|
|
4566
|
+
background: #fff;
|
|
4567
|
+
color: #0f172a;
|
|
4568
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
|
4569
|
+
box-sizing: border-box;
|
|
4570
|
+
text-decoration: none;
|
|
4571
|
+
display: inline-flex;
|
|
4572
|
+
align-items: center;
|
|
4573
|
+
justify-content: center;
|
|
4574
|
+
}
|
|
4575
|
+
body.dark.mobile-menu-open .appcanvas > aside .mobile-sheet-action,
|
|
4576
|
+
body.dark.mobile-menu-open .mobile-menu-fallback .mobile-sheet-action {
|
|
4577
|
+
background: rgba(15, 17, 21, 0.82);
|
|
4578
|
+
border-color: rgba(255,255,255,0.08);
|
|
4579
|
+
color: rgba(226, 232, 240, 0.92);
|
|
4580
|
+
box-shadow: 0 1px 2px rgba(2, 6, 20, 0.4);
|
|
4581
|
+
}
|
|
4582
|
+
body.mobile-menu-open .appcanvas > aside .mobile-sheet-action i,
|
|
4583
|
+
body.mobile-menu-open .mobile-menu-fallback .mobile-sheet-action i {
|
|
4584
|
+
font-size: 15px;
|
|
4585
|
+
}
|
|
4586
|
+
body.mobile-menu-open .appcanvas > aside .mobile-sheet-action span,
|
|
4587
|
+
body.mobile-menu-open .mobile-menu-fallback .mobile-sheet-action span {
|
|
4588
|
+
display: none;
|
|
4589
|
+
}
|
|
4590
|
+
body.mobile-menu-open .appcanvas > aside .menu-scroller .mobile-sheet-action,
|
|
4591
|
+
body.mobile-menu-open .mobile-menu-fallback .mobile-sheet-action {
|
|
4592
|
+
width: 40px !important;
|
|
4593
|
+
height: 40px !important;
|
|
4594
|
+
min-width: 40px !important;
|
|
4595
|
+
min-height: 40px !important;
|
|
4596
|
+
padding: 0 !important;
|
|
4597
|
+
border-radius: 12px !important;
|
|
4598
|
+
display: inline-flex;
|
|
4599
|
+
align-items: center;
|
|
4600
|
+
justify-content: center;
|
|
4601
|
+
gap: 0;
|
|
4602
|
+
}
|
|
4603
|
+
body.mobile-menu-open[data-view='files'] .mobile-menu-fallback .mobile-sheet-actions {
|
|
4604
|
+
display: flex;
|
|
4605
|
+
align-items: center;
|
|
4606
|
+
gap: 8px;
|
|
4607
|
+
width: 100%;
|
|
4608
|
+
overflow-x: auto;
|
|
4609
|
+
overflow-y: hidden;
|
|
4610
|
+
padding-bottom: 2px;
|
|
4611
|
+
}
|
|
4487
4612
|
body.mobile-menu-open .appcanvas > aside .menu-scroller .btn:not(.btn2),
|
|
4488
4613
|
body.mobile-menu-open .appcanvas > aside .menu-actions .btn:not(.btn2) {
|
|
4489
4614
|
max-width: none;
|
|
@@ -4518,20 +4643,42 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4518
4643
|
font-size: 15px;
|
|
4519
4644
|
}
|
|
4520
4645
|
body.mobile-menu-open .appcanvas > aside .menu-actions {
|
|
4646
|
+
display: none;
|
|
4647
|
+
}
|
|
4648
|
+
.mobile-menu-fallback {
|
|
4649
|
+
display: none;
|
|
4650
|
+
}
|
|
4651
|
+
body.mobile-menu-open[data-view='files'] .mobile-menu-fallback {
|
|
4652
|
+
display: flex;
|
|
4653
|
+
position: fixed;
|
|
4654
|
+
inset: 0;
|
|
4655
|
+
bottom: var(--mobile-bottom-bar-height);
|
|
4656
|
+
z-index: 10000001;
|
|
4521
4657
|
flex-direction: column;
|
|
4522
|
-
|
|
4523
|
-
border-left: 0;
|
|
4524
|
-
border-top: 1px solid rgba(0,0,0,0.08);
|
|
4525
|
-
margin-top: auto;
|
|
4526
|
-
width: 100%;
|
|
4527
|
-
padding: 0 12px calc(12px + env(safe-area-inset-bottom));
|
|
4528
|
-
background: transparent;
|
|
4529
|
-
box-sizing: border-box;
|
|
4530
|
-
gap: 12px;
|
|
4658
|
+
background: #f2f2f7;
|
|
4531
4659
|
}
|
|
4532
|
-
body.dark.mobile-menu-open .
|
|
4533
|
-
|
|
4534
|
-
|
|
4660
|
+
body.dark.mobile-menu-open[data-view='files'] .mobile-menu-fallback {
|
|
4661
|
+
background: #0f1115;
|
|
4662
|
+
}
|
|
4663
|
+
.mobile-menu-fallback__header {
|
|
4664
|
+
display: flex;
|
|
4665
|
+
align-items: center;
|
|
4666
|
+
gap: 10px;
|
|
4667
|
+
padding: 12px;
|
|
4668
|
+
font-size: 15px;
|
|
4669
|
+
font-weight: 700;
|
|
4670
|
+
}
|
|
4671
|
+
.mobile-menu-fallback__logo {
|
|
4672
|
+
width: 22px;
|
|
4673
|
+
height: 22px;
|
|
4674
|
+
object-fit: contain;
|
|
4675
|
+
display: block;
|
|
4676
|
+
}
|
|
4677
|
+
.mobile-menu-fallback__body {
|
|
4678
|
+
flex: 1 1 auto;
|
|
4679
|
+
overflow-y: auto;
|
|
4680
|
+
padding: 0 12px 16px;
|
|
4681
|
+
box-sizing: border-box;
|
|
4535
4682
|
}
|
|
4536
4683
|
body.mobile-menu-open .appcanvas .container {
|
|
4537
4684
|
display: none;
|
|
@@ -4629,7 +4776,7 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4629
4776
|
})()
|
|
4630
4777
|
</script>
|
|
4631
4778
|
</head>
|
|
4632
|
-
<body class='<%=theme%>' data-platform="<%=platform%>" data-agent="<%=agent%>">
|
|
4779
|
+
<body class='<%=theme%>' data-platform="<%=platform%>" data-agent="<%=agent%>" data-view="<%=type%>">
|
|
4633
4780
|
<header class='navheader grabbable'>
|
|
4634
4781
|
<h1>
|
|
4635
4782
|
<a class='home' href="/home">
|
|
@@ -4700,6 +4847,32 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
4700
4847
|
<% } %>
|
|
4701
4848
|
</div>
|
|
4702
4849
|
<span class="disk-usage tab-metric__value" data-path="/" data-filepath="<%=path%>">--</span>
|
|
4850
|
+
<div class='mobile-sheet-actions' aria-label="Workspace actions">
|
|
4851
|
+
<a class='btn mobile-sheet-action' href="/home" aria-label="Home" title="Home">
|
|
4852
|
+
<i class="fa-solid fa-house"></i>
|
|
4853
|
+
</a>
|
|
4854
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#refresh-page" data-mobile-close-menu="true" aria-label="Refresh" title="Refresh">
|
|
4855
|
+
<i class="fa-solid fa-rotate-right"></i>
|
|
4856
|
+
</button>
|
|
4857
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#back" data-mobile-close-menu="true" aria-label="Back" title="Back">
|
|
4858
|
+
<i class="fa-solid fa-chevron-left"></i>
|
|
4859
|
+
</button>
|
|
4860
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#forward" data-mobile-close-menu="true" aria-label="Forward" title="Forward">
|
|
4861
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
4862
|
+
</button>
|
|
4863
|
+
<a class='btn mobile-sheet-action' href="/columns" aria-label="2 Columns" title="2 Columns">
|
|
4864
|
+
<i class="fa-solid fa-table-columns"></i>
|
|
4865
|
+
</a>
|
|
4866
|
+
<a class='btn mobile-sheet-action' href="/rows" aria-label="2 Rows" title="2 Rows">
|
|
4867
|
+
<i class="fa-solid fa-table-columns fa-rotate-270"></i>
|
|
4868
|
+
</a>
|
|
4869
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#new-window" data-mobile-close-menu="true" aria-label="New Window" title="New Window">
|
|
4870
|
+
<i class="fa-solid fa-plus"></i>
|
|
4871
|
+
</button>
|
|
4872
|
+
<button type='button' class='btn mobile-sheet-action mobile-close-window-action hidden' data-mobile-proxy="#close-window" data-mobile-close-menu="true" aria-label="Close Section" title="Close Section">
|
|
4873
|
+
<i class="fa-solid fa-xmark"></i>
|
|
4874
|
+
</button>
|
|
4875
|
+
</div>
|
|
4703
4876
|
<div class='m n system' data-type="n">
|
|
4704
4877
|
<%if (type==='browse') { %>
|
|
4705
4878
|
<a id='devtab' data-mode="refresh" target="<%=dev_link%>" href="<%=dev_link%>" class="btn frame-link selected" data-index="10">
|
|
@@ -5022,6 +5195,63 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5022
5195
|
</div>
|
|
5023
5196
|
<% } %>
|
|
5024
5197
|
</div>
|
|
5198
|
+
<nav class='mobile-bottom-nav' aria-label="Mobile navigation">
|
|
5199
|
+
<button type='button' class='btn2 mobile-bottom-nav__button mobile-nav-menu' data-mobile-proxy="#menu-mobile" aria-expanded="false" aria-label="Open Pinokio menu">
|
|
5200
|
+
<img class='mobile-bottom-nav__logo' src="/pinokio-black.png" alt="" aria-hidden="true">
|
|
5201
|
+
</button>
|
|
5202
|
+
<div class='mobile-bottom-nav__modes' role="tablist" aria-label="Workspace mode">
|
|
5203
|
+
<a class="mobile-mode-segment <%=type === 'run' ? 'selected' : ''%>" href="<%=run_tab%>" role="tab" data-mobile-mode="run" aria-selected="<%=type === 'run' ? 'true' : 'false'%>">Run</a>
|
|
5204
|
+
<a class="mobile-mode-segment <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>" role="tab" data-mobile-mode="browse" aria-selected="<%=type === 'browse' ? 'true' : 'false'%>">Dev</a>
|
|
5205
|
+
<a class="mobile-mode-segment <%=type === 'files' ? 'selected' : ''%>" href="<%=files_tab%>" role="tab" data-mobile-mode="files" aria-selected="<%=type === 'files' ? 'true' : 'false'%>">Files</a>
|
|
5206
|
+
</div>
|
|
5207
|
+
<% if (registryEnabled) { %>
|
|
5208
|
+
<button type='button' class='btn2 mobile-bottom-nav__button mobile-nav-community' data-mobile-proxy="#community-mode-toggle" data-mobile-close-menu="true" aria-pressed="false" aria-label="Community">
|
|
5209
|
+
<i class="fa-solid fa-globe"></i>
|
|
5210
|
+
</button>
|
|
5211
|
+
<% } %>
|
|
5212
|
+
<% if (type === 'run') { %>
|
|
5213
|
+
<button type='button' class='btn2 mobile-bottom-nav__button mobile-nav-ask-ai' data-mobile-proxy="#ask-ai-tab" data-mobile-close-menu="true" aria-pressed="false" aria-label="Ask AI">
|
|
5214
|
+
<i class="fa-solid fa-robot"></i>
|
|
5215
|
+
</button>
|
|
5216
|
+
<% } %>
|
|
5217
|
+
</nav>
|
|
5218
|
+
</div>
|
|
5219
|
+
<div class='mobile-menu-fallback' aria-hidden="true">
|
|
5220
|
+
<div class='mobile-menu-fallback__header'>
|
|
5221
|
+
<button type='button' class='btn2' data-mobile-proxy="#menu-mobile" aria-label="Close menu">
|
|
5222
|
+
<i class="fa-solid fa-chevron-left"></i>
|
|
5223
|
+
</button>
|
|
5224
|
+
<img class='mobile-menu-fallback__logo' src="/pinokio-black.png" alt="" aria-hidden="true">
|
|
5225
|
+
<div>Pinokio</div>
|
|
5226
|
+
</div>
|
|
5227
|
+
<div class='mobile-menu-fallback__body'>
|
|
5228
|
+
<div class='mobile-sheet-actions' aria-label="Workspace actions">
|
|
5229
|
+
<a class='btn mobile-sheet-action' href="/home" aria-label="Home" title="Home">
|
|
5230
|
+
<i class="fa-solid fa-house"></i>
|
|
5231
|
+
</a>
|
|
5232
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#refresh-page" data-mobile-close-menu="true" aria-label="Refresh" title="Refresh">
|
|
5233
|
+
<i class="fa-solid fa-rotate-right"></i>
|
|
5234
|
+
</button>
|
|
5235
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#back" data-mobile-close-menu="true" aria-label="Back" title="Back">
|
|
5236
|
+
<i class="fa-solid fa-chevron-left"></i>
|
|
5237
|
+
</button>
|
|
5238
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#forward" data-mobile-close-menu="true" aria-label="Forward" title="Forward">
|
|
5239
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
5240
|
+
</button>
|
|
5241
|
+
<a class='btn mobile-sheet-action' href="/columns" aria-label="2 Columns" title="2 Columns">
|
|
5242
|
+
<i class="fa-solid fa-table-columns"></i>
|
|
5243
|
+
</a>
|
|
5244
|
+
<a class='btn mobile-sheet-action' href="/rows" aria-label="2 Rows" title="2 Rows">
|
|
5245
|
+
<i class="fa-solid fa-table-columns fa-rotate-270"></i>
|
|
5246
|
+
</a>
|
|
5247
|
+
<button type='button' class='btn mobile-sheet-action' data-mobile-proxy="#new-window" data-mobile-close-menu="true" aria-label="New Window" title="New Window">
|
|
5248
|
+
<i class="fa-solid fa-plus"></i>
|
|
5249
|
+
</button>
|
|
5250
|
+
<button type='button' class='btn mobile-sheet-action mobile-close-window-action hidden' data-mobile-proxy="#close-window" data-mobile-close-menu="true" aria-label="Close Section" title="Close Section">
|
|
5251
|
+
<i class="fa-solid fa-xmark"></i>
|
|
5252
|
+
</button>
|
|
5253
|
+
</div>
|
|
5254
|
+
</div>
|
|
5025
5255
|
</div>
|
|
5026
5256
|
<script>
|
|
5027
5257
|
let started_script
|
|
@@ -5186,6 +5416,20 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5186
5416
|
}
|
|
5187
5417
|
return `${base}:url`
|
|
5188
5418
|
}
|
|
5419
|
+
const forceDefaultSelectionStorageKey = () => {
|
|
5420
|
+
const base = frameContextKey()
|
|
5421
|
+
if (!base) {
|
|
5422
|
+
const pagePath = (() => {
|
|
5423
|
+
try {
|
|
5424
|
+
return window.location?.pathname || ""
|
|
5425
|
+
} catch (_) {
|
|
5426
|
+
return ""
|
|
5427
|
+
}
|
|
5428
|
+
})()
|
|
5429
|
+
return `__pinokio_force_default_selection__:${pagePath}`
|
|
5430
|
+
}
|
|
5431
|
+
return `${base}:force-default-selection`
|
|
5432
|
+
}
|
|
5189
5433
|
const SELECTION_SELECTOR_ATTRS = [
|
|
5190
5434
|
'target',
|
|
5191
5435
|
'data-target-full',
|
|
@@ -5253,6 +5497,52 @@ header.navheader .mode-selector .community-mode-toggle {
|
|
|
5253
5497
|
storage.setItem(key, JSON.stringify(payload))
|
|
5254
5498
|
} catch (_) {}
|
|
5255
5499
|
}
|
|
5500
|
+
const clearPersistedFrameLinkSelection = () => {
|
|
5501
|
+
const storage = getWindowStorage()
|
|
5502
|
+
if (!storage) {
|
|
5503
|
+
return
|
|
5504
|
+
}
|
|
5505
|
+
try {
|
|
5506
|
+
const key = selectionStorageKey()
|
|
5507
|
+
if (key) {
|
|
5508
|
+
storage.removeItem(key)
|
|
5509
|
+
}
|
|
5510
|
+
} catch (_) {}
|
|
5511
|
+
try {
|
|
5512
|
+
const urlKey = selectionUrlStorageKey()
|
|
5513
|
+
if (urlKey) {
|
|
5514
|
+
storage.removeItem(urlKey)
|
|
5515
|
+
}
|
|
5516
|
+
} catch (_) {}
|
|
5517
|
+
}
|
|
5518
|
+
const markForceDefaultSelection = () => {
|
|
5519
|
+
const storage = getWindowStorage()
|
|
5520
|
+
const key = forceDefaultSelectionStorageKey()
|
|
5521
|
+
if (!storage || !key) {
|
|
5522
|
+
return
|
|
5523
|
+
}
|
|
5524
|
+
try {
|
|
5525
|
+
storage.setItem(key, "1")
|
|
5526
|
+
} catch (_) {}
|
|
5527
|
+
}
|
|
5528
|
+
const consumeForceDefaultSelection = () => {
|
|
5529
|
+
const storage = getWindowStorage()
|
|
5530
|
+
const key = forceDefaultSelectionStorageKey()
|
|
5531
|
+
if (!storage || !key) {
|
|
5532
|
+
return false
|
|
5533
|
+
}
|
|
5534
|
+
try {
|
|
5535
|
+
const value = storage.getItem(key)
|
|
5536
|
+
if (value !== "1") {
|
|
5537
|
+
return false
|
|
5538
|
+
}
|
|
5539
|
+
storage.removeItem(key)
|
|
5540
|
+
return true
|
|
5541
|
+
} catch (_) {
|
|
5542
|
+
return false
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5545
|
+
let forceDefaultSelection = consumeForceDefaultSelection()
|
|
5256
5546
|
const restorePersistedFrameLink = (providedPayload = null) => {
|
|
5257
5547
|
const storage = getWindowStorage()
|
|
5258
5548
|
const key = selectionStorageKey()
|
|
@@ -6053,14 +6343,14 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6053
6343
|
persistedSelectionRaw = null
|
|
6054
6344
|
}
|
|
6055
6345
|
const originalHasPersistedSelection = Boolean(persistedSelectionPayload)
|
|
6056
|
-
const
|
|
6346
|
+
const triggeredByUser = Boolean(eventParam || explicitTarget)
|
|
6347
|
+
const followCurrentDefault = forceDefaultSelection && !triggeredByUser
|
|
6348
|
+
const skipPersistedSelection = ignorePersistedSelection || followCurrentDefault
|
|
6057
6349
|
let hasPersistedSelection = skipPersistedSelection ? false : originalHasPersistedSelection
|
|
6058
6350
|
if (skipPersistedSelection) {
|
|
6059
6351
|
persistedSelectionRaw = null
|
|
6060
6352
|
persistedSelectionPayload = null
|
|
6061
6353
|
}
|
|
6062
|
-
|
|
6063
|
-
const triggeredByUser = Boolean(eventParam || explicitTarget)
|
|
6064
6354
|
let resolvedByGlobalSelector = false
|
|
6065
6355
|
|
|
6066
6356
|
let target = explicitTarget
|
|
@@ -6109,6 +6399,13 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6109
6399
|
}
|
|
6110
6400
|
}
|
|
6111
6401
|
|
|
6402
|
+
if (!target && followCurrentDefault) {
|
|
6403
|
+
const defaultSelection = getDefaultSelection()
|
|
6404
|
+
if (defaultSelection) {
|
|
6405
|
+
target = defaultSelection
|
|
6406
|
+
}
|
|
6407
|
+
}
|
|
6408
|
+
|
|
6112
6409
|
const devTab = document.querySelector('#devtab.frame-link')
|
|
6113
6410
|
if (!triggeredByUser && !resolvedByGlobalSelector && !target && devRouteActive && devTab) {
|
|
6114
6411
|
const defaultCandidate = getDefaultSelection()
|
|
@@ -6197,7 +6494,12 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6197
6494
|
if (skipPersistedSelection && target.hasAttribute('data-default')) {
|
|
6198
6495
|
ignorePersistedSelection = false
|
|
6199
6496
|
}
|
|
6200
|
-
|
|
6497
|
+
if (forceDefaultSelection && triggeredByUser) {
|
|
6498
|
+
forceDefaultSelection = false
|
|
6499
|
+
}
|
|
6500
|
+
if (!followCurrentDefault) {
|
|
6501
|
+
persistFrameLinkSelection(target)
|
|
6502
|
+
}
|
|
6201
6503
|
|
|
6202
6504
|
// save target.href
|
|
6203
6505
|
<% if (type !== "run") { %>
|
|
@@ -6515,22 +6817,62 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6515
6817
|
|
|
6516
6818
|
const mobileMenuQuery = window.matchMedia ? window.matchMedia("(max-width: 768px)") : null
|
|
6517
6819
|
const isMobileMenuOpen = () => document.body.classList.contains("mobile-menu-open")
|
|
6820
|
+
const appcanvas = document.querySelector(".appcanvas")
|
|
6821
|
+
const closeWindowButton = document.querySelector("#close-window")
|
|
6822
|
+
const syncMobileBottomNavState = () => {
|
|
6823
|
+
const mobileMenuOpen = isMobileMenuOpen()
|
|
6824
|
+
const communityModeActive = !!(appcanvas && appcanvas.classList.contains("community-mode"))
|
|
6825
|
+
const askAiOpen = !!(appcanvas && appcanvas.classList.contains("panel-open"))
|
|
6826
|
+
const mobileMenuFallback = document.querySelector(".mobile-menu-fallback")
|
|
6827
|
+
const currentView = document.body.getAttribute("data-view")
|
|
6828
|
+
const closeSectionVisible = !!(closeWindowButton && !closeWindowButton.classList.contains("hidden"))
|
|
6829
|
+
|
|
6830
|
+
document.querySelectorAll(".mobile-nav-menu").forEach((button) => {
|
|
6831
|
+
button.classList.toggle("selected", mobileMenuOpen)
|
|
6832
|
+
button.setAttribute("aria-expanded", mobileMenuOpen ? "true" : "false")
|
|
6833
|
+
})
|
|
6834
|
+
document.querySelectorAll(".mobile-nav-community").forEach((button) => {
|
|
6835
|
+
button.classList.toggle("selected", communityModeActive)
|
|
6836
|
+
button.setAttribute("aria-pressed", communityModeActive ? "true" : "false")
|
|
6837
|
+
})
|
|
6838
|
+
document.querySelectorAll(".mobile-nav-ask-ai").forEach((button) => {
|
|
6839
|
+
button.classList.toggle("selected", askAiOpen)
|
|
6840
|
+
button.setAttribute("aria-pressed", askAiOpen ? "true" : "false")
|
|
6841
|
+
})
|
|
6842
|
+
document.querySelectorAll(".mobile-mode-segment").forEach((segment) => {
|
|
6843
|
+
const segmentMode = segment.getAttribute("data-mobile-mode")
|
|
6844
|
+
const active = !communityModeActive && segmentMode === currentView
|
|
6845
|
+
segment.classList.toggle("selected", active)
|
|
6846
|
+
segment.setAttribute("aria-selected", active ? "true" : "false")
|
|
6847
|
+
})
|
|
6848
|
+
document.querySelectorAll(".mobile-close-window-action").forEach((button) => {
|
|
6849
|
+
button.classList.toggle("hidden", !closeSectionVisible)
|
|
6850
|
+
button.setAttribute("aria-hidden", closeSectionVisible ? "false" : "true")
|
|
6851
|
+
})
|
|
6852
|
+
if (mobileMenuFallback) {
|
|
6853
|
+
const showFallback = mobileMenuOpen && document.body.getAttribute("data-view") === "files"
|
|
6854
|
+
mobileMenuFallback.setAttribute("aria-hidden", showFallback ? "false" : "true")
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6518
6857
|
const setMobileMenuOpen = (next, options = {}) => {
|
|
6519
6858
|
const shouldOpen = !!next
|
|
6520
6859
|
if (!mobileMenuQuery || !mobileMenuQuery.matches) {
|
|
6521
6860
|
document.body.classList.remove("mobile-menu-open")
|
|
6861
|
+
syncMobileBottomNavState()
|
|
6522
6862
|
return
|
|
6523
6863
|
}
|
|
6524
6864
|
document.body.classList.toggle("mobile-menu-open", shouldOpen)
|
|
6525
6865
|
if (shouldOpen && !options.skipRefresh) {
|
|
6526
6866
|
refresh(false, { nodelay: true })
|
|
6527
6867
|
}
|
|
6868
|
+
syncMobileBottomNavState()
|
|
6528
6869
|
}
|
|
6529
6870
|
|
|
6530
6871
|
const handleMobileMenuQueryChange = () => {
|
|
6531
6872
|
if (!mobileMenuQuery || !mobileMenuQuery.matches) {
|
|
6532
6873
|
document.body.classList.remove("mobile-menu-open")
|
|
6533
6874
|
}
|
|
6875
|
+
syncMobileBottomNavState()
|
|
6534
6876
|
}
|
|
6535
6877
|
if (mobileMenuQuery && typeof mobileMenuQuery.addEventListener === "function") {
|
|
6536
6878
|
mobileMenuQuery.addEventListener("change", handleMobileMenuQueryChange)
|
|
@@ -6553,6 +6895,43 @@ const rerenderMenuSection = (container, html) => {
|
|
|
6553
6895
|
setMobileMenuOpen(false, { skipRefresh: true })
|
|
6554
6896
|
})
|
|
6555
6897
|
}
|
|
6898
|
+
document.querySelectorAll("[data-mobile-proxy]").forEach((trigger) => {
|
|
6899
|
+
trigger.addEventListener("click", (event) => {
|
|
6900
|
+
const selector = trigger.getAttribute("data-mobile-proxy")
|
|
6901
|
+
if (!selector) {
|
|
6902
|
+
return
|
|
6903
|
+
}
|
|
6904
|
+
const target = document.querySelector(selector)
|
|
6905
|
+
if (!target) {
|
|
6906
|
+
return
|
|
6907
|
+
}
|
|
6908
|
+
event.preventDefault()
|
|
6909
|
+
if (trigger.getAttribute("data-mobile-close-menu") === "true" && isMobileMenuOpen()) {
|
|
6910
|
+
setMobileMenuOpen(false, { skipRefresh: true })
|
|
6911
|
+
}
|
|
6912
|
+
target.click()
|
|
6913
|
+
setTimeout(syncMobileBottomNavState, 0)
|
|
6914
|
+
})
|
|
6915
|
+
})
|
|
6916
|
+
if (appcanvas && typeof MutationObserver === "function") {
|
|
6917
|
+
const mobileNavObserver = new MutationObserver(() => {
|
|
6918
|
+
syncMobileBottomNavState()
|
|
6919
|
+
})
|
|
6920
|
+
mobileNavObserver.observe(appcanvas, {
|
|
6921
|
+
attributes: true,
|
|
6922
|
+
attributeFilter: ["class"]
|
|
6923
|
+
})
|
|
6924
|
+
}
|
|
6925
|
+
if (closeWindowButton && typeof MutationObserver === "function") {
|
|
6926
|
+
const closeWindowObserver = new MutationObserver(() => {
|
|
6927
|
+
syncMobileBottomNavState()
|
|
6928
|
+
})
|
|
6929
|
+
closeWindowObserver.observe(closeWindowButton, {
|
|
6930
|
+
attributes: true,
|
|
6931
|
+
attributeFilter: ["class"]
|
|
6932
|
+
})
|
|
6933
|
+
}
|
|
6934
|
+
syncMobileBottomNavState()
|
|
6556
6935
|
if (document.querySelector("#menu")) {
|
|
6557
6936
|
document.querySelector("#menu").addEventListener("click", async (e) => {
|
|
6558
6937
|
document.querySelector("aside").classList.toggle("hidden")
|
|
@@ -7631,6 +8010,13 @@ const rerenderMenuSection = (container, html) => {
|
|
|
7631
8010
|
if (event.data.type === 'stream') {
|
|
7632
8011
|
const frameName = resolveFrameName(null, event.source)
|
|
7633
8012
|
updateTabTimestamp(frameName, Date.now())
|
|
8013
|
+
} else if (event.data.type === 'restart') {
|
|
8014
|
+
<% if (type === 'run') { %>
|
|
8015
|
+
clearPersistedFrameLinkSelection()
|
|
8016
|
+
markForceDefaultSelection()
|
|
8017
|
+
window.location.reload()
|
|
8018
|
+
<% } %>
|
|
8019
|
+
return
|
|
7634
8020
|
} else if (event.data.type === 'result') {
|
|
7635
8021
|
refresh()
|
|
7636
8022
|
refresh_du()
|
package/server/views/shell.ejs
CHANGED
|
@@ -856,6 +856,12 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
856
856
|
}
|
|
857
857
|
n.Noty(payload)
|
|
858
858
|
}
|
|
859
|
+
} else if (packet.type === "restart") {
|
|
860
|
+
try {
|
|
861
|
+
if (window.parent && window.parent !== window && typeof window.parent.postMessage === "function") {
|
|
862
|
+
window.parent.postMessage(packet, "*")
|
|
863
|
+
}
|
|
864
|
+
} catch (_) {}
|
|
859
865
|
} else if (packet.type === "result") {
|
|
860
866
|
// this.socket.close()
|
|
861
867
|
} else if (packet.type === "info") {
|
|
@@ -1422,6 +1422,12 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1422
1422
|
}
|
|
1423
1423
|
n.Noty(payload)
|
|
1424
1424
|
}
|
|
1425
|
+
} else if (packet.type === "restart") {
|
|
1426
|
+
try {
|
|
1427
|
+
if (window.parent && window.parent !== window && typeof window.parent.postMessage === "function") {
|
|
1428
|
+
window.parent.postMessage(packet, "*")
|
|
1429
|
+
}
|
|
1430
|
+
} catch (_) {}
|
|
1425
1431
|
} else if (packet.type === "result") {
|
|
1426
1432
|
if (packet.id === "terminal.upload") {
|
|
1427
1433
|
const uploaded = Array.isArray(packet.data && packet.data.files) ? packet.data.files : []
|