pinokiod 3.9.0 → 3.9.3

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.
Files changed (119) hide show
  1. package/:w +1247 -0
  2. package/kernel/api/filepicker/index.js +59 -0
  3. package/kernel/api/fs/index.js +6 -4
  4. package/kernel/api/index.js +123 -37
  5. package/kernel/api/input/index.js +2 -0
  6. package/kernel/api/web/index.js +6 -0
  7. package/kernel/bin/caddy.js +127 -0
  8. package/kernel/bin/cloudflared.js +1 -0
  9. package/kernel/bin/conda.js +10 -2
  10. package/kernel/bin/cuda.js +0 -16
  11. package/kernel/bin/ffmpeg.js +4 -6
  12. package/kernel/bin/git.js +55 -29
  13. package/kernel/bin/gxx.js +8 -6
  14. package/kernel/bin/huggingface.js +4 -5
  15. package/kernel/bin/index.js +347 -88
  16. package/kernel/bin/node.js +13 -6
  17. package/kernel/bin/py.js +1 -1
  18. package/kernel/bin/setup.js +146 -0
  19. package/kernel/bin/uv.js +4 -5
  20. package/kernel/bin/zip.js +7 -8
  21. package/kernel/connect/index.js +25 -0
  22. package/kernel/connect/providers/github/README.md +11 -0
  23. package/kernel/connect/providers/x/README.md +96 -0
  24. package/kernel/connect/providers/x/index.js +152 -0
  25. package/kernel/environment.js +50 -1
  26. package/kernel/index.js +152 -32
  27. package/kernel/info.js +4 -2
  28. package/kernel/kv.js +38 -0
  29. package/kernel/loader.js +13 -3
  30. package/kernel/peer.js +161 -0
  31. package/kernel/procs.js +116 -0
  32. package/kernel/prototype.js +62 -0
  33. package/kernel/router/common.js +42 -0
  34. package/kernel/router/connector.js +81 -0
  35. package/kernel/router/index.js +235 -0
  36. package/kernel/router/localhost_home_router.js +84 -0
  37. package/kernel/router/localhost_port_router.js +15 -0
  38. package/kernel/router/localhost_variable_router.js +37 -0
  39. package/kernel/router/peer_home_router.js +15 -0
  40. package/kernel/router/peer_peer_router.js +26 -0
  41. package/kernel/router/peer_port_router.js +45 -0
  42. package/kernel/router/peer_variable_router.js +46 -0
  43. package/kernel/router/processor.js +31 -0
  44. package/kernel/shell.js +141 -60
  45. package/kernel/shells.js +32 -17
  46. package/kernel/store.js +58 -0
  47. package/kernel/sysinfo.js +0 -1
  48. package/kernel/util.js +143 -36
  49. package/package.json +4 -1
  50. package/pipe/index.js +0 -2
  51. package/script/index.js +12 -0
  52. package/server/index.js +1473 -553
  53. package/server/public/Socket.js +5 -1
  54. package/server/public/SourceCodePro-Regular.ttf +0 -0
  55. package/server/public/SpaceMono-Bold.ttf +0 -0
  56. package/server/public/SpaceMono-BoldItalic.ttf +0 -0
  57. package/server/public/SpaceMono-Italic.ttf +0 -0
  58. package/server/public/SpaceMono-Regular.ttf +0 -0
  59. package/server/public/commander.js +40 -0
  60. package/server/public/common.js +60 -0
  61. package/server/public/dropzone-min.js +2 -0
  62. package/server/public/dropzone.css +1 -0
  63. package/server/public/filepond-plugin-file-validate-type.min.js +9 -0
  64. package/server/public/filepond-plugin-image-crop.min.js +9 -0
  65. package/server/public/filepond-plugin-image-edit.min.css +8 -0
  66. package/server/public/filepond-plugin-image-edit.min.js +9 -0
  67. package/server/public/filepond-plugin-image-exif-orientation.min.js +9 -0
  68. package/server/public/filepond-plugin-image-preview.min.css +8 -0
  69. package/server/public/filepond-plugin-image-preview.min.js +9 -0
  70. package/server/public/filepond-plugin-image-resize.min.js +9 -0
  71. package/server/public/filepond-plugin-image-transform.min.js +9 -0
  72. package/server/public/filepond.min.css +8 -0
  73. package/server/public/filepond.min.js +9 -0
  74. package/server/public/fscreator.js +174 -0
  75. package/server/public/fseditor.js +212 -0
  76. package/server/public/github.png +0 -0
  77. package/server/public/highlight-js.js +81 -0
  78. package/server/public/highlight.css +9 -0
  79. package/server/public/highlight.js +1244 -0
  80. package/server/public/install.js +17 -20
  81. package/server/public/modalinput.js +105 -9
  82. package/server/public/opener.js +3 -1
  83. package/server/public/pinokio-black.png +0 -0
  84. package/server/public/pinokio-black2.png +0 -0
  85. package/server/public/redoc.standalone.js +1832 -0
  86. package/server/public/style.css +215 -17
  87. package/server/public/swagger-ui-bundle.js +2 -0
  88. package/server/public/swagger-ui.css +3 -0
  89. package/server/public/x.js +63 -0
  90. package/server/socket.js +136 -39
  91. package/server/views/404.ejs +0 -1
  92. package/server/views/app.ejs +368 -98
  93. package/server/views/bootstrap.ejs +22 -19
  94. package/server/views/connect/x.ejs +324 -0
  95. package/server/views/connect.ejs +197 -0
  96. package/server/views/download.ejs +7 -1
  97. package/server/views/editor.ejs +68 -9
  98. package/server/views/explore.ejs +13 -10
  99. package/server/views/file_explorer.ejs +10 -37
  100. package/server/views/github.ejs +218 -0
  101. package/server/views/help.ejs +4 -3
  102. package/server/views/index.ejs +275 -121
  103. package/server/views/install.ejs +56 -29
  104. package/server/views/keys.ejs +276 -0
  105. package/server/views/network.ejs +889 -0
  106. package/server/views/partials/dynamic.ejs +9 -0
  107. package/server/views/partials/menu.ejs +7 -1
  108. package/server/views/prototype/index.ejs +311 -0
  109. package/server/views/prototype/show.ejs +383 -0
  110. package/server/views/redoc.ejs +18 -0
  111. package/server/views/required_env_editor.ejs +11 -2
  112. package/server/views/settings.ejs +38 -10
  113. package/server/views/setup.ejs +254 -0
  114. package/server/views/setup_home.ejs +192 -0
  115. package/server/views/shell.ejs +781 -0
  116. package/server/views/swaggerui.ejs +19 -0
  117. package/server/views/terminal.ejs +174 -42
  118. package/LICENSE +0 -7
  119. package/server/views/proxy.ejs +0 -405
@@ -0,0 +1,383 @@
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="/filepond.min.css" rel="stylesheet" />
14
+ <link href="/filepond-plugin-image-preview.min.css" rel="stylesheet" />
15
+ <link href="/filepond-plugin-image-edit.min.css" rel="stylesheet" />
16
+ <link href="/style.css" rel="stylesheet"/>
17
+ <% if (agent === "electron") { %>
18
+ <link href="/electron.css" rel="stylesheet"/>
19
+ <% } %>
20
+ <style>
21
+ main {
22
+ max-width: 800px;
23
+ margin: 0 auto;
24
+ }
25
+ .doc {
26
+ padding: 20px;
27
+ margin-top: 20px;
28
+ border-radius: 10px;
29
+ border: 1px solid rgba(0,0,0,0.1);
30
+ }
31
+ #readme {
32
+ border-top: 1px solid rgba(0,0,0,0.1);
33
+ padding: 20px;
34
+ margin-top: 20px;
35
+ }
36
+ .line2 {
37
+ display: flex;
38
+ align-items: center;
39
+ cursor: pointer;
40
+ background: rgba(0,0,100,0.04);
41
+ }
42
+ .line2 a {
43
+ text-decoration: none;
44
+ color: black;
45
+ }
46
+ .status {
47
+ padding: 10px;
48
+ margin: 10px;
49
+ border-radius: 10px;
50
+ }
51
+ .status.offline {
52
+ background: silver;
53
+ }
54
+ .status.online {
55
+ background: yellowgreen;
56
+ }
57
+ .switch {
58
+ padding: 10px;
59
+ margin: 10px 0;
60
+ }
61
+ .switch[data-online=true] {
62
+ color: yellowgreen;
63
+ }
64
+ .button {
65
+ padding: 10px;
66
+ }
67
+ .on, .off {
68
+ display: flex;
69
+ align-items: center;
70
+ }
71
+ header .runner {
72
+ padding: 10px 0 0;
73
+ }
74
+ .proxy {
75
+ padding: 0 10px;
76
+ text-align: right;
77
+ }
78
+ .proxy a {
79
+ text-decoration: none;
80
+ display: inline-block;
81
+ padding: 5px 10px;
82
+ border-radius: 2px;
83
+ background: black;
84
+ color: white;
85
+ }
86
+ .browser-options-btns {
87
+ display: flex;
88
+ padding-top: 5px;
89
+ }
90
+ .browser-options-row {
91
+ /*
92
+ border-top: 1px solid rgba(0,0,0,0.1);
93
+ border-bottom: 1px solid rgba(0,0,0,0.1);
94
+ */
95
+ display: flex;
96
+ padding: 10px;
97
+ align-items: flex-start;
98
+ }
99
+ .browser-options-row h3 {
100
+ margin: 0;
101
+ font-size: 14px;
102
+ margin-bottom: 3px;
103
+ }
104
+ .browser-options-row .btn.disabled {
105
+ opacity: 0.7;
106
+ }
107
+ .browser-options-row .btn {
108
+ font-weight: bold;
109
+ width: 100px;
110
+ text-align: center;
111
+ display: block;
112
+ margin-right: 10px;
113
+ flex-shrink: 0;
114
+ padding: 5px;
115
+ }
116
+ .badge {
117
+ font-size: 12px;
118
+ padding: 5px 10px;
119
+ background: rgba(0,0,100,0.1);
120
+ border-radius: 5px;
121
+ display: inline-block;
122
+ margin-top: 5px;
123
+ }
124
+ a.badge {
125
+ color: royalblue;
126
+ text-decoration: none;
127
+ cursor: pointer;
128
+ }
129
+ .badge a {
130
+ color: royalblue;
131
+ text-decoration: none;
132
+ }
133
+ body.dark .btn {
134
+ color: white;
135
+ border: 1px solid rgba(255,255,255,0.3);
136
+ }
137
+ body.dark .badge {
138
+ background: rgba(255,255,255,0.1);
139
+ }
140
+ .folder-templates {
141
+ background: rgba(0,0,100, 0.02);
142
+ }
143
+ .folder-templates .folder {
144
+ text-decoration: none;
145
+ color: rgba(0,0,0,0.9);
146
+ font-size: 12px;
147
+ padding: 10px;
148
+ display: flex;
149
+ align-items: center;
150
+ }
151
+ .folder-templates .folder .title {
152
+ font-weight: bold;
153
+ font-size: 15px;
154
+ text-transform: capitalize;
155
+ }
156
+ .folder-templates .folder img {
157
+ width: 30px;
158
+ height: 30px;
159
+ margin-right: 10px;
160
+ border-radius: 10px;
161
+ }
162
+ .prototype-header {
163
+ padding: 20px;
164
+ border-radius: 10px;
165
+ background: rgba(0,0,0,0.04);
166
+ }
167
+ .prototype-header input, .prototype-header textarea {
168
+ width: 100%;
169
+ padding: 10px;
170
+ box-sizing: border-box;
171
+ border: 1px solid rgba(0,0,0,0.2);
172
+ }
173
+ .prototype-header .footer {
174
+ text-align: right;
175
+ }
176
+ /*
177
+ body.dark .browser-options-row {
178
+ border-top: 1px solid rgba(255,255,255,0.05);
179
+ border-bottom: 1px solid rgba(255,255,255,0.05);
180
+ }
181
+ */
182
+ .meta {
183
+ display: flex;
184
+ align-items: flex-start;
185
+ }
186
+ .meta-content {
187
+ padding: 10px;
188
+ }
189
+ .meta-content h2 {
190
+ margin: 0;
191
+ font-size: 30px;
192
+ }
193
+ .meta-path {
194
+ padding: 10px 0;
195
+ opacity: 0.7;
196
+ }
197
+ .meta img {
198
+ width: 100px;
199
+ }
200
+ @media only screen and (max-width: 480px) {
201
+ .btn2 {
202
+ padding: 5px;
203
+ font-size: 11px;
204
+ }
205
+ .nav-btns {
206
+ flex-grow: 1;
207
+ justify-content: center;
208
+ padding: 0;
209
+ }
210
+ }
211
+ </style>
212
+ <script src="/hotkeys.min.js"></script>
213
+ <script src="/sweetalert2.js"></script>
214
+ <script src="/noty.js"></script>
215
+ <script src="/notyq.js"></script>
216
+ <script src="/xterm.js"></script>
217
+ <script src="/xterm-addon-fit.js"></script>
218
+ <script src="/xterm-addon-web-links.js"></script>
219
+ <script src="/xterm-theme.js"></script>
220
+ <script src="/Socket.js"></script>
221
+ <script src="/install.js"></script>
222
+ <script src="/common.js"></script>
223
+ <script src="/opener.js"></script>
224
+ <script src="/nav.js"></script>
225
+ <script src="/fuse.js"></script>
226
+ <script src="/report.js"></script>
227
+ <script src="/filepond-plugin-file-validate-type.min.js"></script>
228
+ <script src="/filepond-plugin-image-exif-orientation.min.js"></script>
229
+ <script src="/filepond-plugin-image-preview.min.js"></script>
230
+ <script src="/filepond-plugin-image-edit.min.js"></script>
231
+ <script src="/filepond-plugin-image-crop.min.js"></script>
232
+ <script src="/filepond-plugin-image-resize.min.js"></script>
233
+ <script src="/filepond-plugin-image-transform.min.js"></script>
234
+ <script src="/filepond.min.js"></script>
235
+ <script src="/commander.js"></script>
236
+ </head>
237
+ <body class='<%=theme%>'>
238
+ <header class='navheader grabbable'>
239
+ <h1>
240
+ <a class='home' href="/"><i class="fa-solid fa-house"></i></a>
241
+ <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
242
+ <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
243
+ <div class='flexible'></div>
244
+ <div class='nav-btns'>
245
+ <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
246
+ <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
247
+ <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
248
+ <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>
249
+ <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
250
+ <% if (agent === "electron") { %>
251
+ <a href="/" target="_blank" class='btn2' features="browser"><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
252
+ <% } else { %>
253
+ <a href="/" target="_blank" class='btn2'><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
254
+ <% } %>
255
+ <button id='new-window' title='open a new window' class='btn2'><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
256
+ </div>
257
+ </h1>
258
+ </header>
259
+ <main>
260
+ <div class='prototype-header'>
261
+ <div class='filepond-wrapper'>
262
+ <div class='avatar-field'>
263
+ <input id='new-folder-image' type="file" class="filepond" name="avatar" accept="image/png, image/jpeg, image/gif" />
264
+ </div>
265
+ <div class='folder-rows'>
266
+ <div class='folder-row'>
267
+ <label for='new-folder-path'>Folder Path</label>
268
+ <div class='field-row'>
269
+ <input id="new-folder-path" placeholder="Folder Path" autofocus />
270
+ </div>
271
+ </div>
272
+ <div class='folder-row'>
273
+ <label for='new-folder-title'>Title (optional)</label>
274
+ <input id="new-folder-title" placeholder="Enter the display name" />
275
+ </div>
276
+ <div class='folder-row'>
277
+ <label for='new-folder-description'>Description (optional)</label>
278
+ <textarea id="new-folder-description" placeholder="Enter the project description"></textarea>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ <div class='footer'>
283
+ <button class='btn' id='create'>Create</button>
284
+ </div>
285
+ </div>
286
+ <div class='doc'>
287
+ <div class='meta'>
288
+ <img src="<%=icon%>">
289
+ <div class='meta-content'>
290
+ <h2><%=title%></h2>
291
+ <div class='meta-description'><%=description%></div>
292
+ </div>
293
+ </div>
294
+ <div id='readme'><%-readme%></div>
295
+ </div>
296
+ </main>
297
+ <script>
298
+ let textareas = document.querySelectorAll("textarea")
299
+ for(let textarea of textareas) {
300
+ textarea.style.height = 'auto';
301
+ textarea.style.height = textarea.scrollHeight + 'px';
302
+ textarea.addEventListener('input', () => {
303
+ textarea.style.height = textarea.scrollHeight + 'px';
304
+ });
305
+ }
306
+ const input = document.querySelector("#new-folder-path")
307
+ input.focus()
308
+ FilePond.registerPlugin(
309
+ FilePondPluginFileValidateType,
310
+ FilePondPluginImageExifOrientation,
311
+ FilePondPluginImagePreview,
312
+ FilePondPluginImageCrop,
313
+ FilePondPluginImageResize,
314
+ FilePondPluginImageTransform,
315
+ FilePondPluginImageEdit
316
+ );
317
+ let path
318
+ let title
319
+ let description
320
+ let pond = FilePond.create(
321
+ document.querySelector('#new-folder-image'),
322
+ {
323
+ credits: false,
324
+ allowReplace: true,
325
+ allowRevert: true,
326
+ allowRemove: true,
327
+
328
+ allowImageCrop: true,
329
+ allowImageEdit: true,
330
+ allowImageTransform: true,
331
+
332
+
333
+ labelIdle: `Drag & Drop your picture or <span class="filepond--label-action">Browse</span>`,
334
+ // imagePreviewHeight: 170,
335
+ imageCropAspectRatio: '1:1',
336
+ // imageResizeTargetWidth: 200,
337
+ // imageResizeTargetHeight: 200,
338
+ imageTransformOutputMimeType: 'image/png',
339
+ // stylePanelLayout: 'compact circle',
340
+ instantUpload: false,
341
+ server: {
342
+ process: {
343
+ url: '/new',
344
+ onerror: (response) => {
345
+ let err = JSON.parse(response)
346
+ alert(err.error)
347
+ },
348
+ ondata: (formData) => {
349
+ // set folder path
350
+ let folder_path_el = document.querySelector('#new-folder-path')
351
+ let folder_title_el = document.querySelector('#new-folder-title')
352
+ let folder_description_el = document.querySelector('#new-folder-description')
353
+ path = folder_path_el.value
354
+ title = folder_title_el.value || path
355
+ description = folder_description_el.value || ""
356
+ formData.append("path", path)
357
+ formData.append("title", title)
358
+ formData.append("description", description)
359
+ debugger
360
+ return formData;
361
+ }
362
+ }
363
+ },
364
+ onprocessfile(error, file) {
365
+ console.log({ file })
366
+ if (!error) {
367
+ location.href = `/prototype/run/<%=prototype_path%>?api_path=${path}`
368
+ }
369
+ },
370
+ files: [
371
+ {
372
+ type: 'local',
373
+ source: '/pinokio-black.png',
374
+ }
375
+ ]
376
+ }
377
+ );
378
+ document.querySelector("#create").addEventListener("click", async (e) => {
379
+ pond.processFile()
380
+ })
381
+ </script>
382
+ </body>
383
+ </html>
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API Docs</title>
5
+ <link rel="stylesheet" type="text/css" href="/swagger-ui.css" />
6
+ </head>
7
+ <body>
8
+ <% if (spec) { %>
9
+ <div id='container'></div>
10
+ <script src="/redoc.standalone.js"></script>
11
+ <script>
12
+ Redoc.init(<%-spec.replace(/'/g, "&#39;")%>, {}, document.querySelector("#container"))
13
+ </script>
14
+ <% } else { %>
15
+ <h1>url required</h1>
16
+ <% } %>
17
+ </body>
18
+ </html>
@@ -223,10 +223,18 @@ document.addEventListener("DOMContentLoaded", async () => {
223
223
  document.querySelector("#save").addEventListener("click", async (e) => {
224
224
  // save
225
225
  const vals = {}
226
+ const hosts = {}
226
227
  const els = document.querySelectorAll(".env-item")
227
228
  for(let el of els) {
228
229
  let input = el.querySelector("input[type=text]")
229
230
  vals[input.id] = input.value
231
+ let host = input.getAttribute("data-host")
232
+ if (host) {
233
+ hosts[input.id] = {
234
+ index: input.getAttribute("data-index"),
235
+ value: host
236
+ }
237
+ }
230
238
  }
231
239
  console.log("vals", vals)
232
240
  let res = await fetch("/env", {
@@ -236,7 +244,8 @@ document.addEventListener("DOMContentLoaded", async () => {
236
244
  },
237
245
  body: JSON.stringify({
238
246
  filepath: "<%=filepath%>",
239
- vals
247
+ vals,
248
+ hosts
240
249
  })
241
250
  }).then((res) => {
242
251
  return res.json()
@@ -263,7 +272,7 @@ document.addEventListener("DOMContentLoaded", async () => {
263
272
  <% if (item.description) { %>
264
273
  <div class='env-description'><%=item.description%></div>
265
274
  <% } %>
266
- <input id="<%=item.env%>" type="text" value="<%=item.val%>">
275
+ <input id="<%=item.env%>" data-index="<%=item.index%>" data-host="<%=item.host%>" type="text" value="<%=item.val%>">
267
276
  </div>
268
277
  <% }) %>
269
278
  <div id='save' class='btn'>
@@ -200,6 +200,11 @@ hr {
200
200
  display: inline-block;
201
201
  cursor: pointer;
202
202
  }
203
+ .keys pre {
204
+ padding: 20px;
205
+ margin-top: 20px;
206
+ background: rgba(0,0,200,0.1) !important;
207
+ }
203
208
  </style>
204
209
  <script src="/hotkeys.min.js"></script>
205
210
  <script src="/sweetalert2.js"></script>
@@ -226,8 +231,12 @@ hr {
226
231
  <header class='navheader grabbable'>
227
232
  <h1>
228
233
  <a class='home' href="/"><i class="fa-solid fa-house"></i></a>
229
- <button class='btn2' id='back'><i class="fa-solid fa-chevron-left"></i></button>
230
- <button class='btn2' id='forward'><i class="fa-solid fa-chevron-right"></i></button>
234
+ <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
235
+ <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
236
+ <a class='btn2 create-new' id='create-new-folder'><div><i class='fa-solid fa-plus'></i></div><div>New</div></a>
237
+ <a class='btn2' id='explore' href="/?mode=explore"><div><i class="fa-solid fa-globe"></i></div><div>Discover</div></a>
238
+ <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
239
+ <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
231
240
  <% paths.forEach((path) => { %>
232
241
  <% if (path.action) { %>
233
242
  <a class='path nav-button' id="<%=path.id%>" onclick="<%=path.action%>"><%-path.name%></a>
@@ -237,11 +246,18 @@ hr {
237
246
  <% }) %>
238
247
  <div class='flexible'></div>
239
248
  <div class='nav-btns'>
240
- <a class='btn2' id='explore' href="/?mode=explore"><div><i class="fa-solid fa-globe"></i></div><div>Discover</div></a>
241
249
  <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
242
- <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Debug</div></button>
250
+ <% if (agent === "electron") { %>
251
+ <a class='btn2' data-filepath="<%=filepath%>"><div><i class="fa-regular fa-folder-open"></i></div><div>Files</div></a>
252
+ <% } %>
253
+ <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
243
254
  <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>
244
255
  <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
256
+ <% if (agent === "electron") { %>
257
+ <a href="/" target="_blank" class='btn2' features="browser"><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
258
+ <% } else { %>
259
+ <a href="/" target="_blank" class='btn2'><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
260
+ <% } %>
245
261
  <button id='new-window' title='open a new window' class='btn2'><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
246
262
  </div>
247
263
  <!--
@@ -369,6 +385,24 @@ hr {
369
385
  </div>
370
386
  </div>
371
387
  <hr/>
388
+ <div class='keys'>
389
+ <h1>Keys</h1>
390
+ <br>
391
+ <br>
392
+ <div>Keys are array values stored for unique host names, stored at <code>~/pinokio/key.json</code>, and can be set through <code>pre</code> attributes in pinokio scripts. Here's an example:</div>
393
+ <pre>{
394
+ "openai.com": [
395
+ "sk-proj-2najBvCGFx18belpjf13T3BlbkFJIAR5KXK031n41WzAsPEn"
396
+ ],
397
+ "x.com": [
398
+ "12345678-FAKEaccesstoken1234567890abcdef",
399
+ "FAKEaccesstokensecret0987654321abcdef"
400
+ ]
401
+ }</pre>
402
+ <br>
403
+ <a class='btn' href="/keys"><i class="fa-solid fa-key"></i> Edit Keys</a>
404
+ </div>
405
+ <hr/>
372
406
  <div class='advanced'>
373
407
  <h1>Advanced Settings</h1>
374
408
  <br>
@@ -497,12 +531,6 @@ document.addEventListener("DOMContentLoaded", async () => {
497
531
  el.classList.remove("hidden")
498
532
  })
499
533
  })
500
- document.querySelector("#back").addEventListener("click", (e) => {
501
- history.back()
502
- })
503
- document.querySelector("#forward").addEventListener("click", (e) => {
504
- history.forward()
505
- })
506
534
  if (document.querySelector("#genlog")) {
507
535
  document.querySelector("#genlog").addEventListener("click", (e) => {
508
536
  e.preventDefault()