pinokiod 3.9.27 → 3.9.29

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/server/index.js CHANGED
@@ -102,12 +102,9 @@ class Server {
102
102
  return new Promise(r=>fs.access(s, fs.constants.F_OK, e => r(!e)))
103
103
  }
104
104
  async updateMeta(formData, app_path) {
105
- console.log("updateMeta", formData)
106
-
107
105
  // write title/description to pinokio.json
108
106
  let dirty
109
107
  let meta_path = this.kernel.path("api", app_path, "pinokio.json")
110
- console.log("meta_path", meta_path)
111
108
  let meta = (await this.kernel.loader.load(meta_path)).resolved
112
109
  if (!meta) meta = {}
113
110
  if (formData.title) {
@@ -128,13 +125,11 @@ class Server {
128
125
  //
129
126
  // write icon file
130
127
  let icon_path = this.kernel.path("api", formData.new_path, formData.icon_path)
131
- console.log({ icon_path })
132
128
  await fs.promises.writeFile(icon_path, formData.avatar)
133
129
  meta.icon = formData.icon_path
134
130
  dirty = true
135
131
  }
136
132
  if (dirty) {
137
- console.log("Save", meta_path, meta)
138
133
  await fs.promises.writeFile(meta_path, JSON.stringify(meta, null, 2))
139
134
  }
140
135
  }
@@ -516,6 +511,12 @@ class Server {
516
511
  execUrl: "/api/" + name,
517
512
  // rawpath,
518
513
  }
514
+ if (!this.kernel.proto.config) {
515
+ await this.kernel.proto.init()
516
+ }
517
+ if (!this.kernel.plugin.config) {
518
+ await this.kernel.plugin.init()
519
+ }
519
520
  res.render("app", result)
520
521
  }
521
522
  getVariationUrls(req) {
@@ -1006,7 +1007,6 @@ class Server {
1006
1007
  script_path: (req.base ? full_filepath : null),
1007
1008
  }
1008
1009
 
1009
-
1010
1010
  res.render(template, result)
1011
1011
  }
1012
1012
 
@@ -1262,7 +1262,27 @@ class Server {
1262
1262
 
1263
1263
 
1264
1264
  // not only should include the pattern, but also end with it (otherwise can include similar patterns such as /api/qqqa, /api/qqqaaa, etc.
1265
- if (key.includes(p) && key.endsWith(p)) {
1265
+
1266
+ let is_running
1267
+ let api_path = this.kernel.path("api")
1268
+ if (key.startsWith(api_path)) {
1269
+ // api script
1270
+ if (key.startsWith(p)) {
1271
+ is_running = true
1272
+ }
1273
+ } else {
1274
+ if (key.endsWith(p)) {
1275
+ is_running = true
1276
+ }
1277
+ }
1278
+ // 1. if the script path starts with api path => api script
1279
+ // => check includes and startsWith
1280
+
1281
+ // 2. if the script path starts with anything else => other scripts (prototype, plugin ,etc.)
1282
+ // => check inlcludes and endsWith
1283
+
1284
+ //if (key.includes(p) && key.endsWith(p)) {
1285
+ if (is_running) {
1266
1286
  items[i].running = true
1267
1287
  items[i].index = index
1268
1288
  if (!items[i].running_scripts) {
@@ -1519,6 +1539,49 @@ class Server {
1519
1539
  }
1520
1540
 
1521
1541
 
1542
+ renderMenu2(config, base, keypath) {
1543
+ // when the config has not loaded yet
1544
+ if (!config) {
1545
+ return { menu: [] }
1546
+ }
1547
+
1548
+ if (config.menu) {
1549
+ for(let i=0; i<config.menu.length; i++) {
1550
+ let item = config.menu[i]
1551
+ let new_keypath
1552
+ if (keypath) {
1553
+ new_keypath = keypath.concat(i)
1554
+ } else {
1555
+ new_keypath = [i]
1556
+ }
1557
+ let c = this.renderMenu2(item, base, new_keypath)
1558
+ config.menu[i] = c
1559
+ }
1560
+ }
1561
+ if (config.text) {
1562
+ if (config.hasOwnProperty("icon")) {
1563
+ config.html = `<i class="${config.icon}"></i> ${config.text}`
1564
+ } else if (config.hasOwnProperty("image")) {
1565
+ let imagePath = `${base.path}/${config.image}`
1566
+ config.html = `<img class='menu-item-image' src='${imagePath}' /> ${config.text}`
1567
+ } else {
1568
+ config.html = `${config.text}`
1569
+ }
1570
+ config.btn = config.html
1571
+ }
1572
+ /*
1573
+ if (config.href && !config.href.startsWith("/")) {
1574
+ if (base.href) {
1575
+ config.href = base.href + "/" + config.href
1576
+ }
1577
+ }
1578
+ */
1579
+ if (keypath) {
1580
+ config.href = base.href + "/" + keypath.join("/") + "?path=" + base.cwd
1581
+ }
1582
+ return config
1583
+ }
1584
+
1522
1585
  async renderMenu(uri, name, config, pathComponents, indexPath) {
1523
1586
  if (config.menu) {
1524
1587
 
@@ -1556,7 +1619,7 @@ class Server {
1556
1619
  menuitem.menu = m.menu
1557
1620
  }
1558
1621
 
1559
- if (menuitem.base === "/") {
1622
+ if (menuitem.base && menuitem.base.startsWith("/")) {
1560
1623
  config.menu[i].href = menuitem.base + menuitem.href
1561
1624
  } else {
1562
1625
  if (menuitem.href && !menuitem.href.startsWith("http")) {
@@ -2256,15 +2319,28 @@ class Server {
2256
2319
  }
2257
2320
  }
2258
2321
  async getPlugin(name) {
2259
- let info = new Info(this.kernel)
2260
- info.caller = () => {
2261
- return this.kernel.path("api", name, "pinokio.js")
2322
+ if (this.kernel.plugin.config) {
2323
+ try {
2324
+ let info = new Info(this.kernel)
2325
+ info.caller = () => {
2326
+ return this.kernel.path("api", name, "pinokio.js")
2327
+ }
2328
+ let menu = await this.kernel.plugin.config.menu(this.kernel, info)
2329
+ let plugin = { menu }
2330
+ let uri = this.kernel.path("api")
2331
+ await this.renderMenu(uri, name, plugin, [])
2332
+ return plugin
2333
+ } catch (e) {
2334
+ return {
2335
+ menu: []
2336
+ }
2337
+ }
2338
+ } else {
2339
+ return {
2340
+ menu: []
2341
+ }
2342
+
2262
2343
  }
2263
- let menu = await this.kernel.plugin.config.menu(this.kernel, info)
2264
- let plugin = { menu }
2265
- let uri = this.kernel.path("api")
2266
- await this.renderMenu(uri, name, plugin, [])
2267
- return plugin
2268
2344
  }
2269
2345
 
2270
2346
  async start(options) {
@@ -2511,6 +2587,12 @@ class Server {
2511
2587
  // return
2512
2588
  // }
2513
2589
 
2590
+ if (!this.kernel.proto.config) {
2591
+ await this.kernel.proto.init()
2592
+ }
2593
+ if (!this.kernel.plugin.config) {
2594
+ await this.kernel.plugin.init()
2595
+ }
2514
2596
 
2515
2597
  if (req.query.mode !== "settings" && !home) {
2516
2598
  res.redirect("/?mode=settings")
@@ -2809,7 +2891,6 @@ class Server {
2809
2891
  //Util.openfs(req.body.path, req.body.mode)
2810
2892
  let cwd = req.body.cwd
2811
2893
  let cmd = req.body.run
2812
- console.log({ cmd, cwd })
2813
2894
  Util.run(cmd, cwd, this.kernel)
2814
2895
  res.json({ success: true })
2815
2896
  }))
@@ -3447,22 +3528,34 @@ class Server {
3447
3528
  }))
3448
3529
  this.app.get("/prototype/show/*", ex(async (req, res) => {
3449
3530
  let name = req.params[0].split("/").filter((x) => { return x }).join("/")
3450
- let icon = this.kernel.proto.kv[name].icon
3451
- let title = this.kernel.proto.kv[name].title
3452
- let description = this.kernel.proto.kv[name].description
3453
- let readme = await this.kernel.proto.readme(name)
3454
- let config = await this.kernel.api.meta({ path: req.query.path })
3531
+
3532
+ // print readme
3533
+
3534
+
3535
+
3536
+
3537
+ let paths = req.params[0].split("/")
3538
+ let item
3539
+ let config = this.kernel.proto.config
3540
+ for(let key of paths) {
3541
+ config = config.menu[key]
3542
+ }
3543
+ let run_path = "/run/prototype/system/" + config.href + "?cwd=" + req.query.path
3544
+ console.log({ config, run_path, query: req.query })
3545
+ let readme_path = this.kernel.path("prototype/system", config.readme)
3546
+ console.log("config.readme.split", config.readme.split("/").slice(0, -1))
3547
+ let md = await fs.promises.readFile(readme_path, "utf8")
3548
+ console.log({ readme_path, md })
3549
+ let baseUrl = "/asset/prototype/system/" + (config.readme.split("/").slice(0, -1).join("/")) + "/"
3550
+ console.log("baseUrl", baseUrl)
3551
+ let readme = marked.parse(md, {
3552
+ baseUrl
3553
+ })
3554
+
3455
3555
  res.render("prototype/show", {
3456
- config,
3457
- path: req.query.path,
3458
- icon,
3459
- title,
3460
- description,
3461
- name,
3462
- prototype_path: name,
3556
+ run_path,
3463
3557
  portal: this.portal,
3464
3558
  readme,
3465
- items: this.kernel.proto.items.filter(item => item.type === 'init'),
3466
3559
  logo: this.logo,
3467
3560
  theme: this.theme,
3468
3561
  agent: this.agent,
@@ -3471,11 +3564,11 @@ class Server {
3471
3564
  }))
3472
3565
  this.app.get("/prototype", ex(async (req, res) => {
3473
3566
  // load meta
3474
- let config = await this.kernel.api.meta({ path: req.query.path })
3475
- let items = this.kernel.proto.items
3476
- if (req.query.type) {
3477
- items = this.kernel.proto.items.filter(item => item.type === req.query.type)
3478
- }
3567
+ // let config = await this.kernel.api.meta({ path: req.query.path })
3568
+ // let items = this.kernel.proto.items
3569
+ // if (req.query.type) {
3570
+ // items = this.kernel.proto.items.filter(item => item.type === req.query.type)
3571
+ // }
3479
3572
  let title
3480
3573
  let description
3481
3574
  if (req.query.type === "init") {
@@ -3485,14 +3578,38 @@ class Server {
3485
3578
  title = "Extensions"
3486
3579
  description = "Add extension modules to the current folder"
3487
3580
  }
3581
+
3582
+ let config = structuredClone(this.kernel.proto.config)
3583
+ config = this.renderMenu2(config, {
3584
+ cwd: req.query.path,
3585
+ href: "/prototype/show",
3586
+ path: "/asset/prototype/system"
3587
+ })
3588
+
3589
+ // {
3590
+ // "icon": "fa-solid fa-power-off",
3591
+ // "text": "Run Default",
3592
+ // "href": "/api/facefusion-pinokio.git/run.js?run=true&fullscreen=true&mode=Default",
3593
+ // "params": {
3594
+ // "run": true,
3595
+ // "fullscreen": true,
3596
+ // "mode": "Default"
3597
+ // },
3598
+ // "src": "/api/facefusion-pinokio.git/run.js",
3599
+ // "html": "<i class=\"fa-solid fa-power-off\"></i> Run Default",
3600
+ // "btn": "<i class=\"fa-solid fa-power-off\"></i> Run Default",
3601
+ // "target": "@/api/facefusion-pinokio.git/run.js"
3602
+ // },
3603
+ //
3488
3604
  res.render("prototype/index", {
3489
3605
  title,
3490
3606
  description,
3491
3607
  config,
3492
3608
  path: req.query.path,
3493
3609
  portal: this.portal,
3494
- items,
3610
+ // items,
3495
3611
  logo: this.logo,
3612
+ platform: this.kernel.platform,
3496
3613
  theme: this.theme,
3497
3614
  agent: this.agent,
3498
3615
  kernel: this.kernel,
@@ -4255,10 +4372,8 @@ class Server {
4255
4372
  */
4256
4373
 
4257
4374
  let formData = req.body
4258
- console.log("req.files", req.files)
4259
4375
  for(let key in req.files) {
4260
4376
  let file = req.files[key]
4261
- console.log({ key, file })
4262
4377
  formData[file.fieldname] = file.buffer
4263
4378
  }
4264
4379
  console.log({ formData })
@@ -1,8 +1,6 @@
1
1
  const FSEditor = async ({title, description, old_path, icon, iconpath, redirect, copy, move, edit }) => {
2
- console.log({ icon, iconpath })
3
2
  const compare = async (file1, file2) => {
4
3
  if (file1.size !== file2.size) return false;
5
- console.log({ file1, file2 })
6
4
  const buffer1 = await file1.arrayBuffer();
7
5
  const buffer2 = await file2.arrayBuffer();
8
6
  const view1 = new Uint8Array(buffer1);
@@ -49,11 +47,11 @@ const FSEditor = async ({title, description, old_path, icon, iconpath, redirect,
49
47
  ${folderpath_html}
50
48
  <div class='folder-row ${meta_class}'>
51
49
  <label for='new-folder-title'>Title</label>
52
- <input id="new-folder-title" class="swal2-input" placeholder="Folder Name" value="${title}" />
50
+ <input id="new-folder-title" class="swal2-input" placeholder="Project Name (stored in pinokio.js)" value="${title}" />
53
51
  </div>
54
52
  <div class='folder-row ${meta_class}'>
55
53
  <label for='new-folder-description'>Description</label>
56
- <textarea id="new-folder-description" class="swal2-input" placeholder="Folder Description">${description}</textarea>
54
+ <textarea id="new-folder-description" class="swal2-input" placeholder="Project Description (stored in pinokio.js)">${description}</textarea>
57
55
  </div>
58
56
  </div>
59
57
  </div>`,
@@ -103,8 +101,6 @@ const FSEditor = async ({title, description, old_path, icon, iconpath, redirect,
103
101
  // url: '/pinokio/upload', // optional if same as above
104
102
  process: async (fieldName, file, metadata, load, error, progress, abort) => {
105
103
  const formData = new FormData();
106
-
107
- console.log("upload", { dirty, file })
108
104
  formData.append('avatar', file)
109
105
  if (dirty) {
110
106
  formData.append("icon_dirty", true)
@@ -128,7 +124,6 @@ const FSEditor = async ({title, description, old_path, icon, iconpath, redirect,
128
124
  path = old_path
129
125
  formData.append("new_path", old_path)
130
126
  }
131
- debugger
132
127
  if (mode === "copy") {
133
128
  formData.append("copy", true)
134
129
  } else if (mode === "move") {
@@ -165,8 +160,6 @@ const FSEditor = async ({title, description, old_path, icon, iconpath, redirect,
165
160
  }
166
161
  },
167
162
  onaddfile(error, file) {
168
- console.log({ file, initial_file })
169
- debugger
170
163
  if (initial_file) {
171
164
  compare(initial_file.file, file.file).then((same) => {
172
165
  if (same) {
@@ -10,6 +10,7 @@ const ModalInput = async (params, uri) => {
10
10
  title,
11
11
  description,
12
12
  placeholder,
13
+ required,
13
14
  default
14
15
  }]
15
16
  }
@@ -37,20 +38,34 @@ const ModalInput = async (params, uri) => {
37
38
  input = `<select data-id="${field.key}">${items}</select>`
38
39
  }
39
40
  } else if (type === 'checkbox') {
40
- input = `<div class='checkbox-row'><input data-type="checkbox" type="checkbox" data-id="${field.key}" value="${field.key}" />${field.text}</div>`
41
+ input = `<div class='checkbox-row'>
42
+ <input data-type="checkbox" type="checkbox" data-id="${field.key}" value="${field.key}" />
43
+ <div class='flexible'>
44
+ <h5>${field.title}</h5>
45
+ <div>${field.description ? field.description : ''}</div>
46
+ </div>
47
+ </div>`
41
48
  } else if (type === 'file') {
42
49
  input = `<div class='dropzone' data-type='file' data-id='${field.key}'></div>`
43
50
  //input = `<input type='file' data-id="${field.key}" />`
44
51
  } else {
45
52
  input = `<input ${autofocus} type='${type}' data-id="${field.key}" class="swal2-input" placeholder="${field.placeholder ? field.placeholder : ''}">`
46
53
  }
47
- return [
48
- "<div class='field'>",
49
- `<label class='title'>${field.title ? field.title : ''}</label>`,
50
- input,
51
- `<label class='description'>${field.description ? field.description : ''}</label>`,
52
- "</div>"
53
- ].join("\n")
54
+ if (type === 'checkbox') {
55
+ return [
56
+ "<div class='field'>",
57
+ input,
58
+ "</div>"
59
+ ].join("\n")
60
+ } else {
61
+ return [
62
+ "<div class='field'>",
63
+ `<label class='title'>${field.title ? field.title : ''}</label>`,
64
+ input,
65
+ `<label class='description'>${field.description ? field.description : ''}</label>`,
66
+ "</div>"
67
+ ].join("\n")
68
+ }
54
69
  }).join("\n"),
55
70
  //focusConfirm: false,
56
71
  confirmButtonText: 'Done',
@@ -86,9 +101,13 @@ const ModalInput = async (params, uri) => {
86
101
  for(let field of form) {
87
102
  let input = Swal.getPopup().querySelector("[data-id='" + field.key + "']")
88
103
  let type = input.getAttribute("data-type")
89
- debugger
90
104
  if (type !== 'file' && type !== 'checkbox') {
91
105
  response[field.key] = input.value
106
+ debugger
107
+ if (field.required && input.value.length === 0) {
108
+ alert(`${field.title || field.key} value must exist`)
109
+ return false
110
+ }
92
111
  }
93
112
  if (type === 'checkbox') {
94
113
  response[field.key] = input.checked
@@ -38,7 +38,6 @@ document.addEventListener("click", async (e) => {
38
38
  }
39
39
  }
40
40
  if (el) {
41
- debugger
42
41
  e.preventDefault()
43
42
  e.stopPropagation()
44
43
  let run = el.getAttribute("data-run")
@@ -98,6 +98,31 @@ body.dark .dynamic.selected {
98
98
  background: rgba(0,0,0,0.05);
99
99
  */
100
100
  }
101
+
102
+
103
+ .loader-container {
104
+ background: rgba(0,0,0,0.2) !important;
105
+ }
106
+ body.dark .loader-container {
107
+ background: rgba(0,0,0,0.3) !important;
108
+ }
109
+ .loader-dialog {
110
+ padding: 100px;
111
+ }
112
+ body.dark .loader-popup {
113
+ background: rgba(255,255,255,0.07) !important;
114
+ color: whitesmoke;
115
+ }
116
+ .swal2-popup.loader-popup {
117
+ font-size: 12px;
118
+ letter-spacing: 1px;
119
+ background: rgba(0,0,0,0.6);
120
+ width: 200px !important;
121
+ border-radius: 50px !important;
122
+ color: white;
123
+ }
124
+
125
+
101
126
  .type-tab {
102
127
  display: flex;
103
128
  align-items: center;
@@ -153,10 +178,10 @@ body.dark .type-tabs {
153
178
  .navheader {
154
179
  backdrop-filter: blur(16px);
155
180
 
156
- /*
157
181
  padding: 15px 10px 5px;
158
- */
182
+ /*
159
183
  padding: 15px;
184
+ */
160
185
  }
161
186
  .navheader .btn2 {
162
187
  text-transform: lowercase;
@@ -1469,7 +1494,8 @@ body.dark .swal2-modal input {
1469
1494
  .swal2-modal input[type=checkbox] {
1470
1495
  width: 14px;
1471
1496
  height: 14px;
1472
- background: white;
1497
+ background: whitesmoke;
1498
+ margin: 0;
1473
1499
  margin-right: 5px;
1474
1500
  }
1475
1501
  .swal2-modal input[type=checkbox]:checked {
@@ -1477,7 +1503,12 @@ body.dark .swal2-modal input {
1477
1503
  }
1478
1504
  .swal2-modal .checkbox-row {
1479
1505
  display: flex;
1480
- align-items: center;
1506
+ align-items: flex-start;
1507
+ text-align: left;
1508
+ }
1509
+ .swal2-modal .checkbox-row h5 {
1510
+ margin: 0;
1511
+ font-size: 14px;
1481
1512
  }
1482
1513
  .swal2-container {
1483
1514
  font-size: 14px !important;
@@ -1790,7 +1821,8 @@ body.dark .not-running-apps, body.dark .running-apps {
1790
1821
  }
1791
1822
  */
1792
1823
  .running-apps .line img {
1793
- border: 3px solid #82bc09;
1824
+ border-left: 10px solid #82bc09;
1825
+ padding-left: 10px;
1794
1826
  /*
1795
1827
  border: 3px solid #8fd400;
1796
1828
  */
@@ -1798,6 +1830,13 @@ body.dark .not-running-apps, body.dark .running-apps {
1798
1830
  border-radius: 5px;
1799
1831
  */
1800
1832
  }
1833
+ body.dark .not-running-apps .line img {
1834
+ border-left: 10px solid rgba(255,255,255,0.1);
1835
+ }
1836
+ .not-running-apps .line img {
1837
+ border-left: 10px solid rgba(0,0,0,0.04);
1838
+ padding-left: 10px;
1839
+ }
1801
1840
  /*
1802
1841
  .running-apps .line.align-top h3 .col .title {
1803
1842
  color: #8fd400;