pinokiod 3.51.0 → 3.53.0

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 (117) hide show
  1. package/kernel/gitconfig_template +3 -1
  2. package/kernel/index.js +65 -43
  3. package/kernel/peer.js +21 -8
  4. package/kernel/router/index.js +10 -10
  5. package/kernel/router/localhost_static_router.js +3 -0
  6. package/kernel/router/peer_static_router.js +1 -0
  7. package/kernel/scripts/git/create +1 -2
  8. package/kernel/scripts/git/push +1 -1
  9. package/package.json +6 -2
  10. package/server/index.js +178 -87
  11. package/server/public/common.js +0 -13
  12. package/server/public/serve/directory.html +106 -0
  13. package/server/public/serve/icons/application_xp.png +0 -0
  14. package/server/public/serve/icons/application_xp_terminal.png +0 -0
  15. package/server/public/serve/icons/box.png +0 -0
  16. package/server/public/serve/icons/cd.png +0 -0
  17. package/server/public/serve/icons/controller.png +0 -0
  18. package/server/public/serve/icons/drive.png +0 -0
  19. package/server/public/serve/icons/film.png +0 -0
  20. package/server/public/serve/icons/folder.png +0 -0
  21. package/server/public/serve/icons/font.png +0 -0
  22. package/server/public/serve/icons/image.png +0 -0
  23. package/server/public/serve/icons/map.png +0 -0
  24. package/server/public/serve/icons/page.png +0 -0
  25. package/server/public/serve/icons/page_add.png +0 -0
  26. package/server/public/serve/icons/page_attach.png +0 -0
  27. package/server/public/serve/icons/page_code.png +0 -0
  28. package/server/public/serve/icons/page_copy.png +0 -0
  29. package/server/public/serve/icons/page_delete.png +0 -0
  30. package/server/public/serve/icons/page_edit.png +0 -0
  31. package/server/public/serve/icons/page_error.png +0 -0
  32. package/server/public/serve/icons/page_excel.png +0 -0
  33. package/server/public/serve/icons/page_find.png +0 -0
  34. package/server/public/serve/icons/page_gear.png +0 -0
  35. package/server/public/serve/icons/page_go.png +0 -0
  36. package/server/public/serve/icons/page_green.png +0 -0
  37. package/server/public/serve/icons/page_key.png +0 -0
  38. package/server/public/serve/icons/page_lightning.png +0 -0
  39. package/server/public/serve/icons/page_link.png +0 -0
  40. package/server/public/serve/icons/page_paintbrush.png +0 -0
  41. package/server/public/serve/icons/page_paste.png +0 -0
  42. package/server/public/serve/icons/page_red.png +0 -0
  43. package/server/public/serve/icons/page_refresh.png +0 -0
  44. package/server/public/serve/icons/page_save.png +0 -0
  45. package/server/public/serve/icons/page_white.png +0 -0
  46. package/server/public/serve/icons/page_white_acrobat.png +0 -0
  47. package/server/public/serve/icons/page_white_actionscript.png +0 -0
  48. package/server/public/serve/icons/page_white_add.png +0 -0
  49. package/server/public/serve/icons/page_white_c.png +0 -0
  50. package/server/public/serve/icons/page_white_camera.png +0 -0
  51. package/server/public/serve/icons/page_white_cd.png +0 -0
  52. package/server/public/serve/icons/page_white_code.png +0 -0
  53. package/server/public/serve/icons/page_white_code_red.png +0 -0
  54. package/server/public/serve/icons/page_white_coldfusion.png +0 -0
  55. package/server/public/serve/icons/page_white_compressed.png +0 -0
  56. package/server/public/serve/icons/page_white_copy.png +0 -0
  57. package/server/public/serve/icons/page_white_cplusplus.png +0 -0
  58. package/server/public/serve/icons/page_white_csharp.png +0 -0
  59. package/server/public/serve/icons/page_white_cup.png +0 -0
  60. package/server/public/serve/icons/page_white_database.png +0 -0
  61. package/server/public/serve/icons/page_white_delete.png +0 -0
  62. package/server/public/serve/icons/page_white_dvd.png +0 -0
  63. package/server/public/serve/icons/page_white_edit.png +0 -0
  64. package/server/public/serve/icons/page_white_error.png +0 -0
  65. package/server/public/serve/icons/page_white_excel.png +0 -0
  66. package/server/public/serve/icons/page_white_find.png +0 -0
  67. package/server/public/serve/icons/page_white_flash.png +0 -0
  68. package/server/public/serve/icons/page_white_freehand.png +0 -0
  69. package/server/public/serve/icons/page_white_gear.png +0 -0
  70. package/server/public/serve/icons/page_white_get.png +0 -0
  71. package/server/public/serve/icons/page_white_go.png +0 -0
  72. package/server/public/serve/icons/page_white_h.png +0 -0
  73. package/server/public/serve/icons/page_white_horizontal.png +0 -0
  74. package/server/public/serve/icons/page_white_key.png +0 -0
  75. package/server/public/serve/icons/page_white_lightning.png +0 -0
  76. package/server/public/serve/icons/page_white_link.png +0 -0
  77. package/server/public/serve/icons/page_white_magnify.png +0 -0
  78. package/server/public/serve/icons/page_white_medal.png +0 -0
  79. package/server/public/serve/icons/page_white_office.png +0 -0
  80. package/server/public/serve/icons/page_white_paint.png +0 -0
  81. package/server/public/serve/icons/page_white_paintbrush.png +0 -0
  82. package/server/public/serve/icons/page_white_paste.png +0 -0
  83. package/server/public/serve/icons/page_white_php.png +0 -0
  84. package/server/public/serve/icons/page_white_picture.png +0 -0
  85. package/server/public/serve/icons/page_white_powerpoint.png +0 -0
  86. package/server/public/serve/icons/page_white_put.png +0 -0
  87. package/server/public/serve/icons/page_white_ruby.png +0 -0
  88. package/server/public/serve/icons/page_white_stack.png +0 -0
  89. package/server/public/serve/icons/page_white_star.png +0 -0
  90. package/server/public/serve/icons/page_white_swoosh.png +0 -0
  91. package/server/public/serve/icons/page_white_text.png +0 -0
  92. package/server/public/serve/icons/page_white_text_width.png +0 -0
  93. package/server/public/serve/icons/page_white_tux.png +0 -0
  94. package/server/public/serve/icons/page_white_vector.png +0 -0
  95. package/server/public/serve/icons/page_white_visualstudio.png +0 -0
  96. package/server/public/serve/icons/page_white_width.png +0 -0
  97. package/server/public/serve/icons/page_white_word.png +0 -0
  98. package/server/public/serve/icons/page_white_world.png +0 -0
  99. package/server/public/serve/icons/page_white_wrench.png +0 -0
  100. package/server/public/serve/icons/page_white_zip.png +0 -0
  101. package/server/public/serve/icons/page_word.png +0 -0
  102. package/server/public/serve/icons/page_world.png +0 -0
  103. package/server/public/serve/style.css +308 -0
  104. package/server/public/style.css +13 -5
  105. package/server/public/urldropdown.css +95 -3
  106. package/server/public/urldropdown.js +245 -23
  107. package/server/serveIndex.js +691 -0
  108. package/server/views/app.ejs +305 -89
  109. package/server/views/connect.ejs +3 -0
  110. package/server/views/container.ejs +2 -1
  111. package/server/views/index.ejs +3 -0
  112. package/server/views/init/index.ejs +3 -0
  113. package/server/views/net.ejs +30 -27
  114. package/server/views/network.ejs +23 -70
  115. package/server/views/screenshots.ejs +3 -0
  116. package/server/views/settings.ejs +3 -0
  117. package/server/views/tools.ejs +3 -0
@@ -55,6 +55,31 @@ function initUrlDropdown(config = {}) {
55
55
  }
56
56
  });
57
57
 
58
+ if (document.querySelector(".urlbar")) {
59
+ document.querySelector(".urlbar").addEventListener("submit", (e) => {
60
+ e.preventDefault()
61
+ e.stopPropagation()
62
+ let el = e.target.querySelector("input[type=url]")
63
+ let val = el.value
64
+ let type = el.getAttribute("data-host-type")
65
+ if (type === "local") {
66
+ let redirect_uri = "/container?url=" + val
67
+ location.href = redirect_uri
68
+ } else {
69
+ let u = new URL(val)
70
+ if (String(u.port) === "42000") {
71
+ // pinokio app => open the url itself
72
+ window.open(val, "_blank")
73
+ } else {
74
+ // other servers => open in pinokio redirect frame
75
+ let redirect_uri = "/container?url=" + val
76
+ location.href = redirect_uri
77
+ }
78
+ }
79
+ })
80
+ }
81
+
82
+
58
83
  function initializeInputValue() {
59
84
  if (options.clearBehavior === 'empty') {
60
85
  urlInput.value = '';
@@ -140,6 +165,111 @@ function initUrlDropdown(config = {}) {
140
165
  dropdown.style.display = 'none';
141
166
  }
142
167
 
168
+ function createHostBadge(host) {
169
+ if (!host || !host.platform) return '';
170
+
171
+ // Get platform icon
172
+ let platformIcon = '';
173
+ switch (host.platform) {
174
+ case 'darwin':
175
+ platformIcon = 'fa-brands fa-apple';
176
+ break;
177
+ case 'win32':
178
+ platformIcon = 'fa-brands fa-windows';
179
+ break;
180
+ case 'linux':
181
+ platformIcon = 'fa-brands fa-linux';
182
+ break;
183
+ default:
184
+ platformIcon = 'fa-solid fa-desktop';
185
+ break;
186
+ }
187
+
188
+ // Create badge HTML
189
+ return `
190
+ <span class="host-badge">
191
+ <i class="${platformIcon}"></i>
192
+ <span class="host-name">${escapeHtml(host.name)}</span>
193
+ </span>
194
+ `;
195
+ }
196
+
197
+ function groupProcessesByHost(processes) {
198
+ const grouped = {};
199
+
200
+ processes.forEach(process => {
201
+ // Create a normalized host key based only on name for grouping
202
+ const hostKey = process.host ? process.host.name : 'Unknown';
203
+
204
+ if (!grouped[hostKey]) {
205
+ grouped[hostKey] = {
206
+ host: process.host || { name: 'Unknown', platform: 'unknown', arch: 'unknown' },
207
+ processes: [],
208
+ isLocal: false
209
+ };
210
+ }
211
+
212
+ // Mark as local if any process from this host is local
213
+ if (process.host.local === true) {
214
+ grouped[hostKey].isLocal = true;
215
+ }
216
+
217
+ grouped[hostKey].processes.push(process);
218
+ });
219
+
220
+ // Sort host keys: local host first, then alphabetically
221
+ return Object.keys(grouped)
222
+ .sort((a, b) => {
223
+ const aIsLocal = grouped[a].isLocal;
224
+ const bIsLocal = grouped[b].isLocal;
225
+
226
+ // Local host always comes first
227
+ if (aIsLocal && !bIsLocal) return 1;
228
+ if (!aIsLocal && bIsLocal) return -1;
229
+
230
+ // Both local or both remote - sort alphabetically
231
+ return a.localeCompare(b);
232
+ })
233
+ .reduce((sortedGrouped, hostKey) => {
234
+ sortedGrouped[hostKey] = grouped[hostKey];
235
+ return sortedGrouped;
236
+ }, {});
237
+ }
238
+
239
+ function createHostHeader(host, isLocal = false) {
240
+ if (!host) return '';
241
+
242
+ // Get platform icon
243
+ let platformIcon = '';
244
+ switch (host.platform) {
245
+ case 'darwin':
246
+ platformIcon = 'fa-brands fa-apple';
247
+ break;
248
+ case 'win32':
249
+ platformIcon = 'fa-brands fa-windows';
250
+ break;
251
+ case 'linux':
252
+ platformIcon = 'fa-brands fa-linux';
253
+ break;
254
+ default:
255
+ platformIcon = 'fa-solid fa-desktop';
256
+ break;
257
+ }
258
+
259
+ console.log({ isLocal, host })
260
+ const hostName = isLocal ? `${host.name} (This Machine)` : `${host.name} (Peer)`;
261
+
262
+ return `
263
+ <div class="url-dropdown-host-header">
264
+ <span class='host-meta'>
265
+ <i class="${platformIcon}"></i>
266
+ <span class="host-arch">${escapeHtml(host.arch)}</span>
267
+ </span>
268
+ <span class="host-name">${escapeHtml(hostName)}</span>
269
+ </div>
270
+ `;
271
+ }
272
+
143
273
  function populateDropdown(processes) {
144
274
  if (processes.length === 0) {
145
275
  const query = urlInput.value.toLowerCase().trim();
@@ -150,28 +280,76 @@ function initUrlDropdown(config = {}) {
150
280
  return;
151
281
  }
152
282
 
153
- const items = processes.map(process => {
154
- const url = `http://${process.ip}`;
155
- return `
156
- <div class="url-dropdown-item" data-url="${url}">
157
- <div class="url-dropdown-name">${escapeHtml(process.name)}</div>
158
- <div class="url-dropdown-url">${escapeHtml(url)}</div>
159
- </div>
160
- `;
161
- }).join('');
283
+ // Group processes by host
284
+ const groupedProcesses = groupProcessesByHost(processes);
285
+
286
+ let html = '';
287
+ Object.keys(groupedProcesses).forEach(hostKey => {
288
+ const hostData = groupedProcesses[hostKey];
289
+ const hostInfo = hostData.host;
290
+ const processes = hostData.processes;
291
+ const isLocal = hostData.isLocal;
292
+
293
+ // Add host header
294
+ html += createHostHeader(hostInfo, isLocal);
295
+
296
+ // Add processes for this host
297
+ processes.forEach(process => {
298
+ const onlineIndicator = process.online ?
299
+ '<div class="status-circle online"></div>' :
300
+ '<div class="status-circle offline"></div>';
301
+
302
+ if (process.ip === null || process.ip === undefined) {
303
+ // Non-selectable item with "turn on peer network" button
304
+ const networkUrl = `http://${process.host.ip}:42000/network`;
305
+ html += `
306
+ <div class="url-dropdown-item non-selectable">
307
+ <div class="url-dropdown-name">
308
+ ${onlineIndicator}
309
+ <button class="peer-network-button" data-network-url="${networkUrl}"><i class="fa-solid fa-toggle-on"></i> Turn on peer network</button>
310
+ ${escapeHtml(process.name)}
311
+ </div>
312
+ </div>
313
+ `;
314
+ } else {
315
+ // Normal selectable item
316
+ const url = `http://${process.ip}`;
317
+ html += `
318
+ <div class="url-dropdown-item" data-url="${url}" data-host-type="${process.host.local ? "local" : "remote"}">
319
+ <div class="url-dropdown-name">
320
+ ${onlineIndicator}
321
+ ${escapeHtml(process.name)}
322
+ </div>
323
+ <div class="url-dropdown-url">${escapeHtml(url)}</div>
324
+ </div>
325
+ `;
326
+ }
327
+ });
328
+ });
162
329
 
163
- dropdown.innerHTML = items;
330
+ dropdown.innerHTML = html;
164
331
 
165
332
  // Add click handlers to dropdown items
166
- dropdown.querySelectorAll('.url-dropdown-item').forEach(item => {
333
+ dropdown.querySelectorAll('.url-dropdown-item:not(.non-selectable)').forEach(item => {
167
334
  item.addEventListener('click', function() {
168
335
  const url = this.getAttribute('data-url');
336
+ const type = this.getAttribute('data-host-type');
169
337
  urlInput.value = url;
338
+ urlInput.setAttribute("data-host-type", type)
170
339
  hideDropdown();
171
340
  // Submit the form
172
341
  urlInput.closest('form').dispatchEvent(new Event('submit'));
173
342
  });
174
343
  });
344
+
345
+ // Add click handlers to peer network buttons
346
+ dropdown.querySelectorAll('.peer-network-button').forEach(button => {
347
+ button.addEventListener('click', function(e) {
348
+ e.stopPropagation();
349
+ const networkUrl = this.getAttribute('data-network-url');
350
+ window.open(networkUrl, '_blank');
351
+ });
352
+ });
175
353
  }
176
354
 
177
355
  // Utility function to escape HTML
@@ -330,19 +508,54 @@ function initUrlDropdown(config = {}) {
330
508
  return;
331
509
  }
332
510
 
333
- const items = processes.map(process => {
334
- const url = `http://${process.ip}`;
335
- return `
336
- <div class="url-dropdown-item" data-url="${url}">
337
- <div class="url-dropdown-name">${escapeHtml(process.name)}</div>
338
- <div class="url-dropdown-url">${escapeHtml(url)}</div>
339
- </div>
340
- `;
341
- }).join('');
511
+ // Group processes by host
512
+ const groupedProcesses = groupProcessesByHost(processes);
513
+
514
+ let html = '';
515
+ Object.keys(groupedProcesses).forEach(hostKey => {
516
+ const hostData = groupedProcesses[hostKey];
517
+ const hostInfo = hostData.host;
518
+ const processes = hostData.processes;
519
+ const isLocal = hostData.isLocal;
520
+
521
+ // Add host header
522
+ html += createHostHeader(hostInfo, isLocal);
523
+
524
+ // Add processes for this host
525
+ processes.forEach(process => {
526
+ const onlineIndicator = process.online ?
527
+ '<div class="status-circle online"></div>' :
528
+ '<div class="status-circle offline"></div>';
529
+
530
+ if (process.ip === null || process.ip === undefined) {
531
+ // Non-selectable item with "turn on peer network" button
532
+ const networkUrl = `http://${process.host.ip}:42000/network`;
533
+ html += `
534
+ <div class="url-dropdown-item non-selectable">
535
+ <div class="url-dropdown-name">
536
+ ${onlineIndicator}
537
+ ${escapeHtml(process.name)}
538
+ </div>
539
+ <button class="peer-network-button" data-network-url="${networkUrl}"><i class="fa-solid fa-toggle-on"></i> Turn on peer network</button>
540
+ </div>
541
+ `;
542
+ } else {
543
+ // Normal selectable item
544
+ const url = `http://${process.ip}`;
545
+ html += `
546
+ <div class="url-dropdown-item" data-url="${url}" data-host-type="${process.host.local ? "local" : "remote"}">
547
+ ${onlineIndicator}
548
+ <div class="url-dropdown-name">${escapeHtml(process.name)}</div>
549
+ <div class="url-dropdown-url">${escapeHtml(url)}</div>
550
+ </div>
551
+ `;
552
+ }
553
+ });
554
+ });
342
555
 
343
- modalDropdown.innerHTML = items;
556
+ modalDropdown.innerHTML = html;
344
557
 
345
- modalDropdown.querySelectorAll('.url-dropdown-item').forEach(item => {
558
+ modalDropdown.querySelectorAll('.url-dropdown-item:not(.non-selectable)').forEach(item => {
346
559
  item.addEventListener('click', function() {
347
560
  const url = this.getAttribute('data-url');
348
561
  modalInput.value = url;
@@ -351,6 +564,15 @@ function initUrlDropdown(config = {}) {
351
564
  closeMobileModal();
352
565
  });
353
566
  });
567
+
568
+ // Add click handlers to peer network buttons in modal
569
+ modalDropdown.querySelectorAll('.peer-network-button').forEach(button => {
570
+ button.addEventListener('click', function(e) {
571
+ e.stopPropagation();
572
+ const networkUrl = this.getAttribute('data-network-url');
573
+ window.open(networkUrl, '_blank');
574
+ });
575
+ });
354
576
  }
355
577
 
356
578
  // Set up mobile button click handler
@@ -393,4 +615,4 @@ if (document.readyState === 'loading') {
393
615
  });
394
616
  } else {
395
617
  // DOM is already loaded, templates can initialize immediately
396
- }
618
+ }