pinokiod 2.15.42 → 2.15.44

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/shells.js CHANGED
@@ -155,6 +155,7 @@ class Shells {
155
155
 
156
156
  // 2. first find the `break: false` handlers and replace the patterns with blank => so they won't be matched in the next step
157
157
  //let line = response.replaceAll(/[\r\n]/g, "")
158
+ console.log("checking breakpoints", breakPoints)
158
159
  for(let handler of breakPoints) {
159
160
  if (handler.event) {
160
161
  let matches = /^\/(.+)\/([dgimsuy]*)$/gs.exec(handler.event)
@@ -170,6 +171,8 @@ class Shells {
170
171
  }
171
172
  }
172
173
 
174
+ console.log("Line before", line)
175
+
173
176
  // 3. Now with all the `break: false` (ignored patterns) gone, look for the `break: true` patterns
174
177
  for(let handler of breakPoints) {
175
178
  if (handler.event) {
@@ -179,12 +182,16 @@ class Shells {
179
182
  }
180
183
  let re = new RegExp(matches[1], matches[2])
181
184
 
185
+ console.log("re", re)
186
+
182
187
  // only the "break: true" ones
183
188
  if (handler.break) {
189
+ console.log("checking break handler", handler)
184
190
 
185
191
  let match;
186
192
  // Keep executing the regex on the text until no more matches are found
187
193
  while ((match = re.exec(line)) !== null) {
194
+ console.log("match found", match)
188
195
  errors.add(match[0])
189
196
  // errors.add(match.slice(1));
190
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "2.15.42",
3
+ "version": "2.15.44",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -602,8 +602,6 @@ class Server {
602
602
  let json
603
603
  let js
604
604
 
605
- console.log("filepath", filepath)
606
-
607
605
  if (filepath.endsWith(".json")) {
608
606
  try {
609
607
  json = (await this.kernel.loader.load(filepath)).resolved
@@ -1305,9 +1303,7 @@ class Server {
1305
1303
  }
1306
1304
 
1307
1305
  // custom theme
1308
- console.log(this.kernel.path('web'))
1309
1306
  let exists = await fse.pathExists(this.kernel.path("web"))
1310
- console.log("Exists", exists)
1311
1307
  if (exists) {
1312
1308
  let config_exists = await fse.pathExists(this.kernel.path("web/config.json"))
1313
1309
  if (config_exists) {
@@ -1321,35 +1317,68 @@ class Server {
1321
1317
  }
1322
1318
  }
1323
1319
 
1324
- res.render("index", {
1325
- home_url: `http://localhost:${this.port}`,
1326
- proxy: home_proxy,
1327
- cloudflare_pub: this.cloudflare_pub,
1328
- qr,
1329
- qr_cloudflare,
1330
- error: error,
1331
- logo: this.logo,
1332
- // memory: mem,
1333
- theme: this.theme,
1334
- pinokioPath,
1335
- config,
1336
- display,
1337
- agent: this.agent,
1338
- // folder,
1339
- paths,
1340
- uri,
1341
- gitRemote,
1342
- userdir: this.kernel.api.userdir,
1343
- ishome: meta,
1344
- running,
1345
- notRunning,
1346
- readme,
1347
- filepath,
1348
- mode: null,
1349
- kernel: this.kernel,
1350
- //mode: (req.query && req.query.mode ? req.query.mode : null),
1351
- items
1352
- })
1320
+ if (meta) {
1321
+ res.render("index", {
1322
+ home_url: `http://localhost:${this.port}`,
1323
+ proxy: home_proxy,
1324
+ cloudflare_pub: this.cloudflare_pub,
1325
+ qr,
1326
+ qr_cloudflare,
1327
+ error: error,
1328
+ logo: this.logo,
1329
+ // memory: mem,
1330
+ theme: this.theme,
1331
+ pinokioPath,
1332
+ config,
1333
+ display,
1334
+ agent: this.agent,
1335
+ // folder,
1336
+ paths,
1337
+ uri,
1338
+ gitRemote,
1339
+ userdir: this.kernel.api.userdir,
1340
+ ishome: meta,
1341
+ running,
1342
+ notRunning,
1343
+ readme,
1344
+ filepath,
1345
+ mode: null,
1346
+ kernel: this.kernel,
1347
+ //mode: (req.query && req.query.mode ? req.query.mode : null),
1348
+ items
1349
+ })
1350
+ } else {
1351
+ res.render("file_explorer", {
1352
+ home_url: `http://localhost:${this.port}`,
1353
+ proxy: home_proxy,
1354
+ cloudflare_pub: this.cloudflare_pub,
1355
+ qr,
1356
+ qr_cloudflare,
1357
+ error: error,
1358
+ logo: this.logo,
1359
+ // memory: mem,
1360
+ theme: this.theme,
1361
+ pinokioPath,
1362
+ config,
1363
+ display,
1364
+ agent: this.agent,
1365
+ // folder,
1366
+ paths,
1367
+ uri,
1368
+ gitRemote,
1369
+ userdir: this.kernel.api.userdir,
1370
+ ishome: meta,
1371
+ running,
1372
+ notRunning,
1373
+ readme,
1374
+ filepath,
1375
+ mode: null,
1376
+ kernel: this.kernel,
1377
+ //mode: (req.query && req.query.mode ? req.query.mode : null),
1378
+ items
1379
+ })
1380
+ }
1381
+
1353
1382
  }
1354
1383
  }
1355
1384
  async renderShortcuts(uri, name, config, pathComponents) {
@@ -2665,7 +2694,6 @@ class Server {
2665
2694
  }
2666
2695
  }))
2667
2696
  this.app.get("/api/*", ex(async (req, res) => {
2668
- console.log("req.params", req.params)
2669
2697
  let pathComponents = req.params[0].split("/")
2670
2698
  if (req.query && 'fs' in req.query) {
2671
2699
  // open in file system
@@ -2689,7 +2717,6 @@ class Server {
2689
2717
  try {
2690
2718
  await this.render(req, res, pathComponents)
2691
2719
  } catch (e) {
2692
- console.log("ERROR", e)
2693
2720
  res.status(404).send(e.message)
2694
2721
  }
2695
2722
  }
@@ -0,0 +1,1083 @@
1
+ <html>
2
+ <head>
3
+ <title>Pinokio</title>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
6
+ <link href="/xterm.min.css" rel="stylesheet" />
7
+ <link href="/css/fontawesome.min.css" rel="stylesheet">
8
+ <link href="/css/solid.min.css" rel="stylesheet">
9
+ <link href="/css/regular.min.css" rel="stylesheet">
10
+ <link href="/css/brands.min.css" rel="stylesheet">
11
+ <link href="/markdown.css" rel="stylesheet"/>
12
+ <link href="/noty.css" rel="stylesheet"/>
13
+ <link href="/style.css" rel="stylesheet"/>
14
+ <% if (agent === "electron") { %>
15
+ <link href="/electron.css" rel="stylesheet"/>
16
+ <% } %>
17
+ <style>
18
+ .line2 {
19
+ display: flex;
20
+ align-items: center;
21
+ cursor: pointer;
22
+ background: rgba(0,0,100,0.04);
23
+ }
24
+ .line2 a {
25
+ text-decoration: none;
26
+ color: black;
27
+ }
28
+ .status {
29
+ padding: 10px;
30
+ margin: 10px;
31
+ border-radius: 10px;
32
+ }
33
+ .status.offline {
34
+ background: silver;
35
+ }
36
+ .status.online {
37
+ background: yellowgreen;
38
+ }
39
+ .switch {
40
+ padding: 10px;
41
+ margin: 10px 0;
42
+ }
43
+ .switch[data-online=true] {
44
+ color: yellowgreen;
45
+ }
46
+ .button {
47
+ padding: 10px;
48
+ }
49
+ .on, .off {
50
+ display: flex;
51
+ align-items: center;
52
+ }
53
+ header .runner {
54
+ padding: 10px 0 0;
55
+ }
56
+ .proxy {
57
+ padding: 0 10px;
58
+ text-align: right;
59
+ }
60
+ .proxy a {
61
+ text-decoration: none;
62
+ display: inline-block;
63
+ padding: 5px 10px;
64
+ border-radius: 2px;
65
+ background: black;
66
+ color: white;
67
+ }
68
+ .browser-options-btns {
69
+ display: flex;
70
+ padding-top: 5px;
71
+ }
72
+ .browser-options-row {
73
+ /*
74
+ border-top: 1px solid rgba(0,0,0,0.1);
75
+ border-bottom: 1px solid rgba(0,0,0,0.1);
76
+ */
77
+ display: flex;
78
+ padding: 10px;
79
+ align-items: flex-start;
80
+ }
81
+ .browser-options-row h3 {
82
+ margin: 0;
83
+ font-size: 14px;
84
+ margin-bottom: 3px;
85
+ }
86
+ .browser-options-row .btn.disabled {
87
+ opacity: 0.7;
88
+ }
89
+ .browser-options-row .btn {
90
+ font-weight: bold;
91
+ width: 100px;
92
+ text-align: center;
93
+ display: block;
94
+ margin-right: 10px;
95
+ flex-shrink: 0;
96
+ padding: 5px;
97
+ }
98
+ .badge {
99
+ font-size: 12px;
100
+ padding: 5px 10px;
101
+ background: rgba(0,0,100,0.1);
102
+ border-radius: 5px;
103
+ display: inline-block;
104
+ margin-top: 5px;
105
+ }
106
+ a.badge {
107
+ color: royalblue;
108
+ text-decoration: none;
109
+ cursor: pointer;
110
+ }
111
+ .badge a {
112
+ color: royalblue;
113
+ text-decoration: none;
114
+ }
115
+ body.dark .btn {
116
+ color: white;
117
+ border: 1px solid rgba(255,255,255,0.3);
118
+ }
119
+ body.dark .badge {
120
+ background: rgba(255,255,255,0.1);
121
+ }
122
+ /*
123
+ body.dark .browser-options-row {
124
+ border-top: 1px solid rgba(255,255,255,0.05);
125
+ border-bottom: 1px solid rgba(255,255,255,0.05);
126
+ }
127
+ */
128
+ @media only screen and (max-width: 480px) {
129
+ .btn2 {
130
+ padding: 5px;
131
+ font-size: 11px;
132
+ }
133
+ .nav-btns {
134
+ flex-grow: 1;
135
+ justify-content: center;
136
+ padding: 0;
137
+ }
138
+ }
139
+ </style>
140
+ <script src="/hotkeys.min.js"></script>
141
+ <script src="/sweetalert2.js"></script>
142
+ <script src="/noty.js"></script>
143
+ <script src="/notyq.js"></script>
144
+ <script src="/xterm.js"></script>
145
+ <script src="/xterm-addon-fit.js"></script>
146
+ <script src="/xterm-addon-web-links.js"></script>
147
+ <script src="/xterm-theme.js"></script>
148
+ <script src="/Socket.js"></script>
149
+ <script src="/install.js"></script>
150
+ <script src="/common.js"></script>
151
+ <script src="/opener.js"></script>
152
+ <script src="/nav.js"></script>
153
+ <script src="/fuse.js"></script>
154
+ <script src="/report.js"></script>
155
+ </head>
156
+ <body class='<%=theme%>'>
157
+ <% if (error) { %>
158
+ <nav class='error-message'>
159
+ <div><%=error%></div>
160
+ <div class='flexible'></div>
161
+ <a class='btn' target="_blank" href="https://program.pinokio.computer/#/?id=install">Download</a>
162
+ </nav>
163
+ <% } %>
164
+ <!--
165
+ <header class='grabbable'>
166
+ -->
167
+ <header class='navheader grabbable'>
168
+ <h1>
169
+ <% if (ishome) { %>
170
+ <a class='home' href="/"><i class="fa-solid fa-house"></i></a>
171
+ <button class='btn2' id='back'><i class="fa-solid fa-chevron-left"></i></button>
172
+ <button class='btn2' id='forward'><i class="fa-solid fa-chevron-right"></i></button>
173
+ <% } %>
174
+ <% paths.forEach((path) => { %>
175
+ <% if (path.action) { %>
176
+ <a class='path nav-button' id="<%=path.id%>" onclick="<%=path.action%>"><%-path.name%></a>
177
+ <% } else if (path.path) { %>
178
+ <a class='path' href="<%=path.path%>"><%-path.name%></a>
179
+ <% } %>
180
+ <% }) %>
181
+ <form class='search'>
182
+ <% if (display.includes("form")) { %>
183
+ <input type='search' class="flexible" placeholder='Filter downloaded apps'>
184
+ <% } else { %>
185
+ <% } %>
186
+ </form>
187
+ <% if (ishome) { %>
188
+ <div class='nav-btns'>
189
+ <a class='btn2' id='explore'><div><i class="fa-solid fa-globe"></i></div><div>Discover</div></a>
190
+ <a class='btn2' href="https://portal.pinokio.computer/" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
191
+ <% if (agent === "electron") { %>
192
+ <a class='btn2' data-filepath="<%=filepath%>"><div><i class="fa-regular fa-folder-open"></i></div><div>Files</div></a>
193
+ <% } %>
194
+ <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Debug</div></button>
195
+ <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
196
+ <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
197
+ <button id='new-window' title='open a new window' class='btn2'><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
198
+ </div>
199
+ <% } %>
200
+ </h1>
201
+ <% if (!ishome) { %>
202
+ <div class='runner'>
203
+ <button class='btn' id='open-fs' data-filepath="<%=filepath%>"><i class="fa-solid fa-eye"></i> View Folder</button>
204
+ </div>
205
+ <% } %>
206
+ <% if (display.includes("dependencies")) { %>
207
+ <div class='dependencies'>
208
+ <h2>Prerequisites</h2>
209
+ <div class='item'>Download and install the following dependencies first</div>
210
+ <div class='requirements'>
211
+ <% config.dependencies.forEach((dep) => { %>
212
+ <% if (dep.downloaded) { %>
213
+ <div class='item'><i class="fa-solid fa-check"></i> <a class='downloaded' href="<%=dep.downloaded%>" target="_blank"><%=dep.uri%></a><div class='flexible'></div></div>
214
+ <% } else { %>
215
+ <div class='item'><a class='btn' href="/?mode=download&uri=<%=dep.uri%>" target="_blank"><i class="fa-solid fa-download"></i> Download</a> <%=dep.uri%><div class='flexible'></div></div>
216
+ <% } %>
217
+ <% }) %>
218
+ </div>
219
+ </div>
220
+ <% } %>
221
+ </header>
222
+ <main>
223
+ <div id='terminal' class='hidden'></div>
224
+ <div class='container'>
225
+ <% if (ishome) { %>
226
+ <% if (running.length === 0 && notRunning.length === 0) { %>
227
+ <% } else { %>
228
+ <div class='browser-options'>
229
+ <% if (agent === "electron") { %>
230
+ <div class='browser-options-row'>
231
+ <% if (agent === "electron") { %>
232
+ <a href="/" target="_blank" class='btn' features="browser"><div><i class="fa-solid fa-square-up-right"></i> Open</div></a>
233
+ <% } else { %>
234
+ <a href="/" target="_blank" class='btn'><div><i class="fa-solid fa-square-up-right"></i> Open</div></a>
235
+ <% } %>
236
+ <div class='flexible'>
237
+ <h3><i class="fa-solid fa-bolt"></i> Instant Launch</h3>
238
+ <div>(Experimental) Directly open pinokio apps from your favorite browser address bar at localhost. If an app you're trying to use is already running, it will instantly load. If it's not, the app will first launch and then load the website.</div>
239
+ </div>
240
+ </div>
241
+ <% } %>
242
+ <div class='browser-options-row'>
243
+ <a href="/proxy" class='btn'><div><i class="fa-solid fa-gauge"></i> Configure</div></a>
244
+ <div class='flexible'>
245
+ <h3><i class="fa-solid fa-wifi"></i> Local Sharing</h3>
246
+ <div>Serve Pinokio, installed apps, and even external apps (example: Ollama) to any device on the same network.</div>
247
+ </div>
248
+ </div>
249
+ <!--
250
+ <div class='browser-options-row'>
251
+ <a href="/proxy" class='flexible'>
252
+ <h3><i class="fa-solid fa-wifi"></i> Local Share</h3>
253
+ <div>Access Pinokio, Ollama, and other 3rd party apps from any device on the same network. By default it's off.</div>
254
+ </a>
255
+ <% if (false) { %>
256
+ <% if (proxy) { %>
257
+ <a id='wifi-stop' data-proxy="<%=proxy.proxy%>" class='btn'><div><i class="fa-solid fa-stop"></i> Stop</div></a>
258
+ <div class='flexible'>
259
+ <h3><i class="fa-solid fa-wifi"></i> Local Share</h3>
260
+ <div>Access Pinokio from any device on the same network. By default it's off.</div>
261
+ <span class='badge'><i class="fa-solid fa-circle-notch fa-spin"></i> Running at <a href="<%=proxy.proxy%>" target="_blank" features="browser"><%=proxy.proxy%></a></span>
262
+ <a id='wifi-qr' class='badge'><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
263
+ </div>
264
+ <% } else { %>
265
+ <a id='wifi' class='btn'><div><i class="fa-solid fa-play"></i> Start</div></a>
266
+ <a id='wifi-starting' class='btn disabled hidden'><div><i class="fa-solid fa-circle-notch fa-spin"></i> Starting</div></a>
267
+ <div class='flexible'>
268
+ <h3><i class="fa-solid fa-wifi"></i> Local Share</h3>
269
+ <div>Access Pinokio from any device on the same network. By default it's off.</div>
270
+ </div>
271
+ <% } %>
272
+ <% } %>
273
+ </div>
274
+ -->
275
+ <div class='browser-options-row hidden'>
276
+ <% if (cloudflare_pub) { %>
277
+ <a id='cloudflare-stop' class='btn'><div><i class="fa-solid fa-stop"></i> Stop</div></a>
278
+ <div class='flexible'>
279
+ <h3>Public Share</h3>
280
+ <div>Open up your Pinokio server to the open internet (anyone with the URL can access)</div>
281
+ <span class='badge'><i class="fa-solid fa-circle-notch fa-spin"></i> Running at <a href="<%=cloudflare_pub%>" target="_blank" features="browser"><%=cloudflare_pub%></a></span>
282
+ <a id='cloudflare-qr' class='badge'><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
283
+ </div>
284
+ <% } else { %>
285
+ <a id='cloudflare' class='btn'><div><i class="fa-solid fa-play"></i> Start</div></a>
286
+ <a id='cloudflare-starting' class='btn disabled hidden'><div><i class="fa-solid fa-circle-notch fa-spin"></i> Starting</div></a>
287
+ <div class='flexible'>
288
+ <h3>Public Share</h3>
289
+ <div>Open up your Pinokio server to the open internet (anyone with the URL can access)</div>
290
+ </div>
291
+ <% } %>
292
+ </div>
293
+ </div>
294
+ <% } %>
295
+ <!--
296
+ <div class='type-tabs'>
297
+ <% if (mode === "task") { %>
298
+ <a class='type-tab' href="/"><img src='/pinokio-<%=theme=='dark' ? 'white' : 'black'%>.png'> Apps</a>
299
+ <a class='type-tab selected' href="/?mode=task"> <img src='/<%=theme=='dark' ? 'term-dark.png' : 'term-white.png'%>'> Tasks</a>
300
+ <% } else { %>
301
+ <a class='type-tab selected' href="/"><img src='/pinokio-<%=theme=='dark' ? 'white' : 'black'%>.png'> Apps</a>
302
+ <a class='type-tab' href="/?mode=task"> <img src='/<%=theme=='dark' ? 'term-dark.png' : 'term-white.png'%>'> Tasks</a>
303
+ <% } %>
304
+ </div>
305
+ -->
306
+ <% if (running.length > 0) { %>
307
+ <div class='running-apps'>
308
+ <div class='header-label'>Running</div>
309
+ <% running.forEach((item) => { %>
310
+ <a href="<%=item.browser_url%>" data-description="<%=item.description%>" data-index="<%=item.index%>" data-name="<%=item.name%>" data-uri="<%=item.uri%>" class='line align-top'>
311
+ <h3>
312
+ <% if (item.icon) { %>
313
+ <img src="<%=item.icon%>">
314
+ <% } else { %>
315
+ <% if (theme === 'dark') { %>
316
+ <img src="/pinokio-white.png">
317
+ <% } else { %>
318
+ <img src="/pinokio-black.png">
319
+ <% } %>
320
+ <!--
321
+ <div class='avatar'>
322
+ <i class="fa-regular fa-circle-dot"></i>
323
+ </div>
324
+ -->
325
+ <% } %>
326
+ <div class='col'>
327
+ <div class='uri'><i class="fa-regular fa-folder"></i><%=item.url%></div>
328
+ <div class='title'><%=item.name%></div>
329
+ <div class='description'><%=item.description%></div>
330
+ <div>
331
+ <button class='btn browse' data-src="<%=item.browser_url%>/browse">
332
+ <i class='fa-regular fa-folder-open'></i>
333
+ </button>
334
+ <button class='btn del' data-src="<%=item.url%>">
335
+ <i class="fa-solid fa-trash-can"></i>
336
+ </button>
337
+ <% if (item.running_scripts) { %>
338
+ <% item.running_scripts.forEach((s) => { %>
339
+ <button class='btn shutdown' data-src="<%=s.path%>">
340
+ <i class="fa-solid fa-stop"></i> Stop <%=s.name%>&nbsp;
341
+ <i class='fa-solid fa-spin fa-circle-notch'></i>
342
+ </button>
343
+ <% }) %>
344
+ <% } %>
345
+ </div>
346
+ <% if (item.shortcuts && item.shortcuts.length > 0) { %>
347
+ <div class='btns'>
348
+ <% item.shortcuts.forEach((btn) => { %>
349
+ <% if (btn.btn) { %>
350
+ <button data-action="<%=btn.action ? JSON.stringify(btn.action) : ''%>" class='btn menu-btn'><%-btn.btn%></button>
351
+ <% } %>
352
+ <% }) %>
353
+ </div>
354
+ <% } %>
355
+ </div>
356
+ <div class='spinner'>
357
+ <i class="fa-solid fa-chevron-right"></i>
358
+ <!--
359
+ <i class="fa-solid fa-circle-notch fa-spin"></i>
360
+ -->
361
+ </div>
362
+ </h3>
363
+ </a>
364
+ <% }) %>
365
+ </div>
366
+ <% } %>
367
+ <% if (notRunning.length > 0) { %>
368
+ <div class='not-running-apps'>
369
+ <div class='header-label'>Not Running</div>
370
+ <% notRunning.forEach((item) => { %>
371
+ <a href="<%=item.browser_url%>" data-description="<%=item.description%>" data-index="<%=item.index%>" data-name="<%=item.name%>" data-uri="<%=item.uri%>" class='line align-top'>
372
+ <h3>
373
+ <% if (item.icon) { %>
374
+ <img src="<%=item.icon%>">
375
+ <% } else { %>
376
+ <!--
377
+ <div class='avatar'>
378
+ <i class="fa-regular fa-circle-dot"></i>
379
+ </div>
380
+ -->
381
+ <% if (theme === 'dark') { %>
382
+ <img src="/pinokio-white.png">
383
+ <% } else { %>
384
+ <img src="/pinokio-black.png">
385
+ <% } %>
386
+ <% } %>
387
+ <div class='col'>
388
+ <div class='uri'><i class="fa-regular fa-folder"></i><%=item.url%></div>
389
+ <div class='title'><%=item.name%></div>
390
+ <div class='description'><%=item.description%></div>
391
+ <button class='btn browse' data-src="<%=item.browser_url%>/browse">
392
+ <i class='fa-regular fa-folder-open'></i>
393
+ </button>
394
+ <button class='btn del' data-src="<%=item.url%>">
395
+ <i class="fa-solid fa-trash-can"></i>
396
+ </button>
397
+ <% if (item.shortcuts && item.shortcuts.length > 0) { %>
398
+ <div class='btns'>
399
+ <% item.shortcuts.forEach((btn) => { %>
400
+ <% if (btn.btn) { %>
401
+ <button data-action="<%=btn.action ? JSON.stringify(btn.action) : ''%>" class='btn menu-btn'><%-btn.btn%></button>
402
+ <% } %>
403
+ <% }) %>
404
+ </div>
405
+ <% } %>
406
+ </div>
407
+ <div class='spinner'>
408
+ <i class="fa-solid fa-chevron-right"></i>
409
+ </div>
410
+ </h3>
411
+ </a>
412
+ <% }) %>
413
+ </div>
414
+ <% } %>
415
+ <% if (running.length === 0 && notRunning.length === 0) { %>
416
+ <div class='placeholder'>
417
+ <h1>Welcome.</h1>
418
+ <br>
419
+ <div>Get started by installing some Pinokio scripts.</div>
420
+ <a href="/?mode=explore" class='btn'><i class="fa-solid fa-globe"></i> Visit Discover Page</a>
421
+ </div>
422
+ <% } %>
423
+ <% } else { %>
424
+ <% items.forEach((item, index) => { %>
425
+ <a data-description="<%=item.description%>" data-index="<%=index%>" data-name="<%=item.name%>" data-uri="<%=item.uri%>" class='line thick' href="<%=item.url%>">
426
+ <h3>
427
+ <% if (item.icon) { %>
428
+ <i class="<%=item.icon%>"></i>
429
+ <% } else { %>
430
+ <i class="fa-regular fa-folder"></i>
431
+ <% } %>
432
+ <%=item.name%>
433
+ </h3>
434
+ <div class='description'><%=item.description%></div>
435
+ </a>
436
+ <% }) %>
437
+ <% } %>
438
+ <% if (readme) { %>
439
+ <div class='readme markdown-body'>
440
+ <div class='content'><%-readme%></div>
441
+ </div>
442
+ <% } %>
443
+ <% if (display.includes("onboarding")) { %>
444
+ <!--
445
+ <div class='container'>
446
+ <div class='content'>
447
+ <h2>Quickstart</h2>
448
+ <div>Install llama</div>
449
+ <br>
450
+ <button class='btn' id='llama'>Install</button>
451
+ </div>
452
+ </div>
453
+ -->
454
+ <% } %>
455
+ </div>
456
+ </main>
457
+ <script>
458
+ let paths = location.pathname.split("/").filter(x=>x)
459
+ var term
460
+ const n = new N()
461
+ let socket = new Socket()
462
+ const pull = async () => {
463
+ if (!term) term = createTerm()
464
+ socket.close()
465
+ await new Promise((resolve, reject) => {
466
+ let cwd
467
+ let chunks = location.pathname.split("/").filter((x) => { return x })
468
+ if (chunks.length > 0) {
469
+ //cwd = chunks.slice(1).join("/")
470
+ cwd = "~/" + chunks.join("/")
471
+ } else {
472
+ cwd = "."
473
+ }
474
+ socket.run({
475
+ method: "shell.run",
476
+ params: {
477
+ message: `git pull`,
478
+ // path: "<%=uri%>",//cwd
479
+ path: cwd
480
+ }
481
+ }, (packet) => {
482
+ if (packet.type === 'stream') {
483
+ term.write(packet.data.raw)
484
+ } else if (packet.type === "result") {
485
+ resolve()
486
+ } else if (packet.type === "error") {
487
+ n.Noty({
488
+ text: packet.data
489
+ })
490
+ }
491
+ })
492
+ })
493
+ <% if (config && config.update) { %>
494
+ n.Noty({
495
+ text: "Success! An update script exists. Click to visit and run the update script!",
496
+ type: "success",
497
+ callbacks: {
498
+ onClose: () => {
499
+ location.href = "<%=config.update%>"
500
+ }
501
+ }
502
+ })
503
+ <% } else { %>
504
+ n.Noty({
505
+ text: "Success! Click to refresh.",
506
+ type: "success",
507
+ callbacks: {
508
+ onClose: () => {
509
+ location.href = location.href
510
+ }
511
+ }
512
+ })
513
+ <% } %>
514
+ }
515
+ <% if (display.includes("onboarding")) { %>
516
+ //document.querySelector("#llama").addEventListener("click", async (e) => {
517
+ // if (!term) term = createTerm()
518
+ // await install("llama", "https://github.com/malfunctionize/lla", term, socket)
519
+ //})
520
+ <% } %>
521
+ <% if (display.includes("form")) { %>
522
+ let list = []
523
+ document.querySelectorAll(".line").forEach((el, index) => {
524
+ list.push({
525
+ index: parseInt(el.getAttribute("data-index")),
526
+ name: el.getAttribute("data-name"),
527
+ description: el.getAttribute("data-description"),
528
+ uri: el.getAttribute("data-uri")
529
+ })
530
+ })
531
+ const search = (items, value) => {
532
+ let filtered = []
533
+ for(let i=0; i<items.length; i++) {
534
+ let item = items[i]
535
+ let re = new RegExp(value, "i")
536
+ if (re.test(item.name)) {
537
+ filtered.push({ item })
538
+ } else if (re.test(item.description)) {
539
+ filtered.push({ item })
540
+ } else if (re.test(item.uri)) {
541
+ filtered.push({ item })
542
+ }
543
+ }
544
+ return filtered
545
+ }
546
+ //const fuse = new Fuse(list, {
547
+ // threshold: 0.3,
548
+ // keys: [{
549
+ // name: "name",
550
+ // weight: 5,
551
+ // }, {
552
+ // name: "description",
553
+ // weight: 1,
554
+ // }],
555
+ // includeScore: true,
556
+ // includeMatches: true
557
+ //})
558
+
559
+
560
+ let selectedIndex = -1
561
+ let selectedBtnIndex = 0
562
+ let filteredCount = list.length
563
+ let filteredBtnsCount = 0
564
+
565
+ const renderSearch = () => {
566
+ let target = document.querySelector("form input[type=search]")
567
+ let result
568
+ if (target.value.trim().length === 0) {
569
+ result = list.map((i, index) => {
570
+ return { item: i }
571
+ })
572
+ } else {
573
+ result = search(list, target.value)
574
+ }
575
+ let all = document.querySelectorAll(".line")
576
+ for(let el of all) {
577
+ el.classList.add("hidden")
578
+ el.classList.remove("selected")
579
+ }
580
+
581
+ filteredCount = result.length
582
+ for(let i=0; i<result.length; i++) {
583
+ let selector = result[i]
584
+ //let el = document.querySelector(".line[data-name='" + selector.item.name + "']")
585
+ let el = document.querySelector(".line[data-index='" + selector.item.index + "']")
586
+ el.classList.remove("hidden")
587
+ if (i === selectedIndex) {
588
+ el.classList.add("selected")
589
+ let btns = el.querySelectorAll(".btns a")
590
+ filteredBtnsCount = btns.length
591
+ btns.forEach((_el, j) => {
592
+ if (selectedBtnIndex === j) {
593
+ _el.classList.add("selected")
594
+ } else {
595
+ _el.classList.remove("selected")
596
+ }
597
+ })
598
+ } else {
599
+ el.classList.remove("selected")
600
+ let btns = el.querySelectorAll(".btns a")
601
+ btns.forEach((_el, j) => {
602
+ _el.classList.remove("selected")
603
+ })
604
+ }
605
+ }
606
+
607
+ }
608
+
609
+ renderSearch()
610
+
611
+ document.querySelector("form input[type=search]").addEventListener("input", (e) => {
612
+ selectedIndex = 0
613
+ selectedBtnIndex = 0
614
+ renderSearch()
615
+ /*
616
+ if (e.target.value.trim().length === 0) {
617
+ let all = document.querySelectorAll(".line")
618
+ for(let el of all) {
619
+ el.classList.remove("hidden")
620
+ }
621
+ } else {
622
+ let all = document.querySelectorAll(".line")
623
+ for(let el of all) {
624
+ el.classList.add("hidden")
625
+ }
626
+
627
+ //let result = fuse.search(e.target.value)
628
+ let result = search(list, e.target.value)
629
+ // result.sort((x, y) => {
630
+ // return y.score - x.score
631
+ // })
632
+ let i = 0
633
+ //for(let selector of result) {
634
+ for(let i=0; i<result.length; i++) {
635
+ let selector = result[i]
636
+ let el = document.querySelector(".line[data-name='" + selector.item.name + "']")
637
+ el.classList.remove("hidden")
638
+ if (i === 0) {
639
+ el.classList.add("selected")
640
+ }
641
+ }
642
+ }
643
+ */
644
+ })
645
+ document.querySelector("form input[type=search]").focus()
646
+ <% } %>
647
+
648
+
649
+ // if (el) {
650
+ // let url = el.getAttribute("data-url")
651
+ // if (url) {
652
+ // let target = el.getAttribute("data-target")
653
+ // if (target && target.length > 0) {
654
+ // let agent = el.getAttribute("data-agent")
655
+ // if (agent && agent.length > 0) {
656
+ // window.open(url, target, agent)
657
+ // } else {
658
+ // window.open(url, target)
659
+ // }
660
+ // } else {
661
+ // location.href = url
662
+ // }
663
+ // }
664
+ // }
665
+ //document.querySelector(".runner").addEventListener("click", async (e) => {
666
+ // let el = e.target.closest(".btn")
667
+ // if (!el) {
668
+ // if (el.classList.contains("btn")) {
669
+ // el = e.target
670
+ // }
671
+ // }
672
+ //
673
+ // if (el) {
674
+ // let url = el.getAttribute("data-url")
675
+ // if (url) {
676
+ // let target = el.getAttribute("data-target")
677
+ // if (target && target.length > 0) {
678
+ // let agent = el.getAttribute("data-agent")
679
+ // if (agent && agent.length > 0) {
680
+ // window.open(url, target, agent)
681
+ // } else {
682
+ // window.open(url, target)
683
+ // }
684
+ // } else {
685
+ // location.href = url
686
+ // }
687
+ // }
688
+ // }
689
+ //})
690
+ <% if (display.includes("install")) { %>
691
+ document.querySelector("#install").addEventListener("click", async (e) => {
692
+ location.href = e.target.getAttribute("data-url")
693
+ })
694
+ <% } %>
695
+ <% if (display.includes("explore")) { %>
696
+ document.querySelector("#explore").addEventListener("click", async (e) => {
697
+ e.preventDefault()
698
+ e.stopPropagation()
699
+ location.href = "/?mode=explore"
700
+ })
701
+ <% } %>
702
+ //Reporter()
703
+ /*
704
+ <% if (display.includes("dependencies")) { %>
705
+ document.querySelector(".requirements").addEventListener("click", (e) => {
706
+ e.preventDefault()
707
+ e.stopPropagation()
708
+ let el = e.target.closest(".opener")
709
+ if (!el) {
710
+ if (e.target.classList.contains("opener")) {
711
+ el = e.target
712
+ }
713
+ }
714
+ if (el) {
715
+ window.open(el.getAttribute("data-href"), "_blank", "app")
716
+ }
717
+ })
718
+ <% } %>
719
+ */
720
+ let params = new URLSearchParams(location.search)
721
+ let selected = params.get("selected")
722
+ if (selected) {
723
+ document.querySelector("form input[type=search]").value = selected
724
+ document.querySelector("form input[type=search]").dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
725
+ }
726
+ if (document.querySelector("form input[type=search]")) {
727
+ document.querySelector("form input[type=search]").addEventListener("input", (e) => {
728
+ let p
729
+ if (e.target.value && e.target.value.trim().length > 0) {
730
+ p = location.pathname + "?selected=" + e.target.value
731
+ } else {
732
+ p = location.pathname
733
+ }
734
+ window.history.pushState({ path: p }, '', p);
735
+ })
736
+ }
737
+ <% if (ishome) { %>
738
+ document.addEventListener("keydown", (e) => {
739
+ e = e || window.event;
740
+ if (e.key === "ArrowUp") {
741
+ console.log("up arrow pressed");
742
+ selectedIndex = Math.max(selectedIndex-1, 0)
743
+ selectedBtnIndex = 0
744
+ renderSearch()
745
+ let selected = document.querySelector(".line.selected")
746
+ selected.scrollIntoView(false)
747
+ //
748
+ } else if (e.key === "ArrowDown") {
749
+ console.log("before selectedIndex", selectedIndex)
750
+ selectedIndex = Math.min(filteredCount-1, selectedIndex+1)
751
+ selectedBtnIndex = 0
752
+ console.log("after selectedIndex", selectedIndex)
753
+ renderSearch()
754
+ console.log("down arrow pressed");
755
+ let selected = document.querySelector(".line.selected")
756
+ selected.scrollIntoView(false)
757
+ } else if (e.key === "ArrowLeft") {
758
+ selectedBtnIndex = Math.max(selectedBtnIndex-1, 0)
759
+ console.log("left arrow pressed");
760
+ console.log("selectedBtnIndex", selectedBtnIndex)
761
+ renderSearch()
762
+ } else if (e.key === "ArrowRight") {
763
+ console.log("right arrow pressed");
764
+ selectedBtnIndex = Math.min(filteredBtnsCount-1, selectedBtnIndex+1)
765
+ console.log("selectedBtnIndex", selectedBtnIndex)
766
+ renderSearch()
767
+ } else if (e.key === "Enter") {
768
+ //let selected = document.querySelector(".line.selected:not(.hidden) .btns a.selected")
769
+ //let selected = document.querySelector(".line.selected:not(.hidden) .title a")
770
+ let selected = document.querySelector(".line.selected:not(.hidden)")
771
+ console.log("selected", selected)
772
+ if (selected) {
773
+ e.preventDefault()
774
+ e.stopPropagation()
775
+ selected.click()
776
+ }
777
+ }
778
+ });
779
+ document.addEventListener("click", async (e) => {
780
+
781
+ let target
782
+
783
+ if (e.target.classList.contains("shutdown")) {
784
+ target = e.target
785
+ } else {
786
+ target = e.target.closest(".shutdown")
787
+ }
788
+ if (target) {
789
+ e.preventDefault()
790
+ e.stopPropagation()
791
+ let src = target.getAttribute("data-src")
792
+ console.log("src", src)
793
+
794
+ socket.run({
795
+ method: "kernel.api.stop",
796
+ params: {
797
+ uri: "~/" + src
798
+ }
799
+ }, (packet) => {
800
+ console.log("packet", packet)
801
+ if (packet.type === "result") {
802
+ socket.close()
803
+ target.querySelector("i.fa-stop").className = "fa-solid fa-check"
804
+ target.querySelector("i.fa-spin").classList.add("hidden")
805
+ n.Noty({
806
+ text: `stopped ${src}`,
807
+ silent: true
808
+ })
809
+ setTimeout(() => {
810
+ location.href = location.href
811
+ }, 1000)
812
+ }
813
+ })
814
+
815
+ return
816
+ }
817
+
818
+
819
+ if (e.target.classList.contains("browse")) {
820
+ target = e.target
821
+ } else {
822
+ target = e.target.closest(".browse")
823
+ }
824
+ if (target) {
825
+ e.preventDefault()
826
+ e.stopPropagation()
827
+ let src = target.getAttribute("data-src")
828
+ location.href = src
829
+ return
830
+ }
831
+
832
+
833
+ // delete
834
+ if (e.target.classList.contains("del")) {
835
+ target = e.target
836
+ } else {
837
+ target = e.target.closest(".del")
838
+ }
839
+ if (target) {
840
+ e.preventDefault()
841
+ e.stopPropagation()
842
+ let confirmed = confirm("Are you sure you want to delete the folder? All files in the folder will be gone.")
843
+ if (confirmed) {
844
+ let itemEl = target.closest(".line[data-uri]")
845
+ let name = itemEl.getAttribute("data-uri")
846
+ console.log("delete", name)
847
+ Swal.fire({
848
+ html: '<i class="fa-solid fa-circle-notch fa-spin"></i> Deleting',
849
+ customClass: {
850
+ container: "loader-container",
851
+ popup: "loader-popup",
852
+ htmlContainer: "loader-dialog",
853
+ footer: "hidden",
854
+ actions: "hidden"
855
+ }
856
+ });
857
+ let res = await fetch("/pinokio/delete", {
858
+ method: "post",
859
+ headers: {
860
+ "Content-Type": "application/json"
861
+ },
862
+ body: JSON.stringify({ name })
863
+ }).then((res) => {
864
+ return res.json()
865
+ })
866
+ console.log("res", res)
867
+ Swal.close()
868
+ if (res) {
869
+ if (res.success) {
870
+ location.href = "/"
871
+ } else if (res.error) {
872
+ alert(res.error)
873
+ }
874
+ }
875
+ } else {
876
+ }
877
+ return
878
+ }
879
+
880
+
881
+
882
+ if (document.querySelector(".container")) {
883
+ document.querySelector(".container").addEventListener("click", async (e) => {
884
+ let el = e.target.closest(".menu-btn")
885
+ if (!el) {
886
+ if (el.classList.contains("menu-btn")) {
887
+ el = e.target
888
+ }
889
+ }
890
+
891
+ console.log("EL", el)
892
+
893
+ if (el) {
894
+ e.preventDefault()
895
+ e.stopPropagation()
896
+ let actionStr = el.getAttribute("data-action")
897
+ if (actionStr) {
898
+ let action = JSON.parse(actionStr)
899
+ console.log("action", action)
900
+ if (action.method === "stop" && action.uri) {
901
+ socket.run({
902
+ method: "kernel.api.stop",
903
+ params: {
904
+ uri: action.uri//"~" + location.pathname
905
+ }
906
+ }, (packet) => {
907
+ console.log("packet", packet)
908
+ if (packet.type === "result") {
909
+ socket.close()
910
+ location.href = location.href
911
+ }
912
+ })
913
+ return
914
+ }
915
+ }
916
+
917
+ }
918
+ })
919
+ }
920
+
921
+
922
+
923
+
924
+ /*
925
+ target = (e.target.classList.contains(".btn") ? e.target : e.target.closest(".btn"))
926
+ if (target) {
927
+ target.classList.add("clicked")
928
+ document.querySelectorAll(".line .btns .btn").forEach((e) => {
929
+ e.classList.remove("selected")
930
+ })
931
+ target.classList.add("selected")
932
+ target.closest(".line").classList.add("selected")
933
+ return
934
+ }
935
+ */
936
+
937
+
938
+ })
939
+ <% if (proxy) { %>
940
+ if (document.querySelector("#wifi-qr")) {
941
+ document.querySelector("#wifi-qr").addEventListener("click", async (e) => {
942
+ await Swal.fire({
943
+ title: "Local Share",
944
+ customClass: {
945
+ popup: "min-popup",
946
+ title: "min-title"
947
+ },
948
+ showConfirmButton: false,
949
+ html: `<div class='min-modal'>
950
+ <div>Access Pinokio from</div>
951
+ <div>any local network device</div>
952
+ <div><img src="<%=qr%>"></div>
953
+ <a href="<%=proxy.proxy%>" target="_blank"><%=proxy.proxy%></a>
954
+ </div>`
955
+ })
956
+ })
957
+ }
958
+ if (document.querySelector("#wifi-stop")) {
959
+ document.querySelector("#wifi-stop").addEventListener("click", async (e) => {
960
+ let res = await fetch("/unpublish", {
961
+ method: "post",
962
+ headers: {
963
+ "Content-Type": "application/json"
964
+ },
965
+ body: JSON.stringify({
966
+ type: "local"
967
+ })
968
+ }).then((res) => {
969
+ return res.json()
970
+ })
971
+ console.log(res)
972
+ location.href = location.href
973
+ })
974
+ }
975
+ <% } %>
976
+ <% if (cloudflare_pub) { %>
977
+ if (document.querySelector("#cloudflare-qr")) {
978
+ document.querySelector("#cloudflare-qr").addEventListener("click", async (e) => {
979
+ await Swal.fire({
980
+ title: "Public Share",
981
+ customClass: {
982
+ popup: "min-popup",
983
+ title: "min-title"
984
+ },
985
+ showConfirmButton: false,
986
+ html: `<div class='min-modal'>
987
+ <div>Access Pinokio from anywhere</div>
988
+ <div>(powered by cloudflare tunnel)</div>
989
+ <div><img src="<%=qr_cloudflare%>"></div>
990
+ <a href="<%=cloudflare_pub%>" target="_blank"><%=cloudflare_pub%></a>
991
+ </div>`
992
+ })
993
+ })
994
+ }
995
+ if (document.querySelector("#cloudflare-stop")) {
996
+ document.querySelector("#cloudflare-stop").addEventListener("click", async (e) => {
997
+ let res = await fetch("/unpublish", {
998
+ method: "post",
999
+ headers: {
1000
+ "Content-Type": "application/json"
1001
+ },
1002
+ body: JSON.stringify({
1003
+ type: "cloudflare"
1004
+ })
1005
+ }).then((res) => {
1006
+ return res.json()
1007
+ })
1008
+ console.log(res)
1009
+ location.href = location.href
1010
+ })
1011
+ }
1012
+ <% } %>
1013
+ if (document.querySelector("#wifi")) {
1014
+ document.querySelector("#wifi").addEventListener("click", async (e) => {
1015
+ document.querySelector("#wifi").classList.add("hidden")
1016
+ document.querySelector("#wifi-starting").classList.remove("hidden")
1017
+ let res = await fetch("/publish", {
1018
+ method: "post",
1019
+ headers: {
1020
+ "Content-Type": "application/json"
1021
+ },
1022
+ body: JSON.stringify({
1023
+ type: "local"
1024
+ })
1025
+ }).then((res) => {
1026
+ return res.json()
1027
+ })
1028
+ console.log(res)
1029
+ location.href = location.href
1030
+ })
1031
+ }
1032
+ if (document.querySelector("#cloudflare")) {
1033
+ document.querySelector("#cloudflare").addEventListener("click", async (e) => {
1034
+ document.querySelector("#cloudflare").classList.add("hidden")
1035
+ document.querySelector("#cloudflare-starting").classList.remove("hidden")
1036
+ let res = await fetch("/publish", {
1037
+ method: "post",
1038
+ headers: {
1039
+ "Content-Type": "application/json"
1040
+ },
1041
+ body: JSON.stringify({
1042
+ type: "cloudflare"
1043
+ })
1044
+ }).then((res) => {
1045
+ return res.json()
1046
+ })
1047
+ console.log(res)
1048
+ location.href = location.href
1049
+ })
1050
+ }
1051
+ <% } %>
1052
+ if (document.querySelector("#back")) {
1053
+ document.querySelector("#back").addEventListener("click", (e) => {
1054
+ history.back()
1055
+ })
1056
+ }
1057
+ if (document.querySelector("#forward")) {
1058
+ document.querySelector("#forward").addEventListener("click", (e) => {
1059
+ history.forward()
1060
+ })
1061
+ }
1062
+ if (document.querySelector("#genlog")) {
1063
+ document.querySelector("#genlog").addEventListener("click", (e) => {
1064
+ e.preventDefault()
1065
+ e.stopPropagation()
1066
+ e.target.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i>'
1067
+ fetch("/pinokio/log", {
1068
+ method: "post",
1069
+ }).then((res) => {
1070
+ console.log("RES", res)
1071
+ let btn = document.querySelector("#genlog")
1072
+ let btn2 = document.querySelector("#downloadlogs")
1073
+ btn2.classList.remove("hidden")
1074
+ btn.classList.add("hidden")
1075
+ btn.innerHTML = '<i class="fa-solid fa-circle-check"></i> Generated!'
1076
+ //btn.classList.add("hidden")
1077
+ })
1078
+ })
1079
+ }
1080
+
1081
+ </script>
1082
+ </body>
1083
+ </html>