pinokiod 3.52.0 → 3.54.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.
- package/kernel/index.js +65 -43
- package/kernel/peer.js +21 -8
- package/kernel/router/index.js +10 -10
- package/kernel/router/localhost_static_router.js +3 -0
- package/kernel/router/peer_static_router.js +1 -0
- package/package.json +1 -1
- package/server/index.js +137 -70
- package/server/public/common.js +0 -9
- package/server/public/style.css +4 -3
- package/server/public/urldropdown.css +95 -3
- package/server/public/urldropdown.js +245 -23
- package/server/views/app.ejs +0 -2
- package/server/views/connect.ejs +3 -0
- package/server/views/container.ejs +2 -1
- package/server/views/index.ejs +3 -0
- package/server/views/init/index.ejs +3 -0
- package/server/views/net.ejs +30 -27
- package/server/views/network.ejs +4 -148
- package/server/views/screenshots.ejs +3 -0
- package/server/views/settings.ejs +3 -0
- package/server/views/tools.ejs +3 -0
- package/3 +0 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
border-top: none;
|
|
13
13
|
border-radius: 0 0 5px 5px;
|
|
14
14
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
15
|
-
max-height:
|
|
15
|
+
max-height: 500px;
|
|
16
16
|
overflow-y: auto;
|
|
17
17
|
z-index: 1000;
|
|
18
18
|
display: none;
|
|
@@ -25,7 +25,7 @@ body.dark .url-dropdown {
|
|
|
25
25
|
box-shadow: 0 2px 8px rgba(255,255,255,0.1);
|
|
26
26
|
}
|
|
27
27
|
.url-dropdown-item {
|
|
28
|
-
padding: 12px
|
|
28
|
+
padding: 8px 12px;
|
|
29
29
|
cursor: pointer;
|
|
30
30
|
border-bottom: 1px solid #eee;
|
|
31
31
|
display: flex;
|
|
@@ -48,6 +48,8 @@ body.dark .url-dropdown-item:hover {
|
|
|
48
48
|
font-weight: bold;
|
|
49
49
|
color: #2d3748;
|
|
50
50
|
font-size: 14px;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
51
53
|
}
|
|
52
54
|
body.dark .url-dropdown-name {
|
|
53
55
|
color: #e2e8f0;
|
|
@@ -133,7 +135,7 @@ body.minimized header h1 .mobile-link-button {
|
|
|
133
135
|
}
|
|
134
136
|
.url-modal-content {
|
|
135
137
|
background: white;
|
|
136
|
-
padding:
|
|
138
|
+
padding: 50px 10px 10px;
|
|
137
139
|
border-radius: 10px;
|
|
138
140
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
139
141
|
width: 90%;
|
|
@@ -172,3 +174,93 @@ body.dark .url-modal-input {
|
|
|
172
174
|
border-color: rgba(255, 255, 255, 0.2);
|
|
173
175
|
color: white;
|
|
174
176
|
}
|
|
177
|
+
|
|
178
|
+
/* Host categorization styles */
|
|
179
|
+
.url-dropdown-host-header {
|
|
180
|
+
background: royalblue;
|
|
181
|
+
font-weight: bold;
|
|
182
|
+
font-size: 18px;
|
|
183
|
+
color: white;
|
|
184
|
+
border-bottom: 1px solid #e2e8f0;
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
gap: 8px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
body.dark .url-dropdown-host-header {
|
|
191
|
+
background: royalblue;
|
|
192
|
+
color: white;
|
|
193
|
+
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.url-dropdown-host-header i {
|
|
197
|
+
width: 18px;
|
|
198
|
+
text-align: center;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.host-badge {
|
|
202
|
+
display: inline-flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
gap: 6px;
|
|
205
|
+
background: #edf2f7;
|
|
206
|
+
padding: 2px 8px;
|
|
207
|
+
border-radius: 12px;
|
|
208
|
+
font-size: 18px;
|
|
209
|
+
color: #4a5568;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
body.dark .host-badge {
|
|
213
|
+
background: rgba(255, 255, 255, 0.1);
|
|
214
|
+
color: #cbd5e0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.host-name {
|
|
218
|
+
font-weight: bold;
|
|
219
|
+
flex-grow: 1;
|
|
220
|
+
text-align: right;
|
|
221
|
+
padding: 5px;
|
|
222
|
+
font-size: 14px;
|
|
223
|
+
color: white;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.host-meta {
|
|
227
|
+
padding: 5px 10px;
|
|
228
|
+
color: white;
|
|
229
|
+
}
|
|
230
|
+
.host-arch {
|
|
231
|
+
color: white;
|
|
232
|
+
font-size: 14px;
|
|
233
|
+
}
|
|
234
|
+
.status-circle {
|
|
235
|
+
width: 8px;
|
|
236
|
+
height: 8px;
|
|
237
|
+
border-radius: 50%;
|
|
238
|
+
display: inline-block;
|
|
239
|
+
margin-right: 8px;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.status-circle.online {
|
|
243
|
+
background-color: yellowgreen;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.status-circle.offline {
|
|
247
|
+
background-color: silver;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.peer-network-button:hover {
|
|
251
|
+
color: white !important;
|
|
252
|
+
}
|
|
253
|
+
.peer-network-button {
|
|
254
|
+
background: royalblue;
|
|
255
|
+
color: white;
|
|
256
|
+
border: none;
|
|
257
|
+
padding: 4px 10px;
|
|
258
|
+
border-radius: 4px;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
margin-right: 10px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
body.dark .host-arch {
|
|
265
|
+
color: #a0aec0;
|
|
266
|
+
}
|
|
@@ -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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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 =
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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 =
|
|
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
|
+
}
|
package/server/views/app.ejs
CHANGED
|
@@ -1833,8 +1833,6 @@ body.minimized #fs-status {
|
|
|
1833
1833
|
<div class='app-info-card'>
|
|
1834
1834
|
<% if (config.icon) { %>
|
|
1835
1835
|
<img src="<%=config.icon%>" onerror="this.src='/pinokio-black.png'"/>
|
|
1836
|
-
<% } else { %>
|
|
1837
|
-
<i class="fa-regular fa-circle-dot"></i>
|
|
1838
1836
|
<% } %>
|
|
1839
1837
|
<div class='app-info-container'>
|
|
1840
1838
|
<div class='app-info-title'><%=config.title%></div>
|
package/server/views/connect.ejs
CHANGED
|
@@ -300,6 +300,7 @@ iframe {
|
|
|
300
300
|
}
|
|
301
301
|
*/
|
|
302
302
|
</style>
|
|
303
|
+
<script src="/window_storage.js"></script>
|
|
303
304
|
<script src="/popper.min.js"></script>
|
|
304
305
|
<script src="/tippy-bundle.umd.min.js"></script>
|
|
305
306
|
<script src="/hotkeys.min.js"></script>
|
|
@@ -363,7 +364,7 @@ iframe {
|
|
|
363
364
|
</button>
|
|
364
365
|
</h1>
|
|
365
366
|
</header>
|
|
366
|
-
<iframe src="<%=src%>"></iframe>
|
|
367
|
+
<iframe class='mainframe' src="<%=src%>"></iframe>
|
|
367
368
|
<script src="/urldropdown.js"></script>
|
|
368
369
|
<script>
|
|
369
370
|
// Initialize URL Dropdown with restore behavior
|
package/server/views/index.ejs
CHANGED
package/server/views/net.ejs
CHANGED
|
@@ -280,7 +280,7 @@ body.dark .open-menu, body.dark .browse {
|
|
|
280
280
|
}
|
|
281
281
|
.section-container {
|
|
282
282
|
display: grid;
|
|
283
|
-
grid-template-columns: repeat(
|
|
283
|
+
grid-template-columns: repeat(2, 1fr); /* Three equal columns */
|
|
284
284
|
/*
|
|
285
285
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
286
286
|
*/
|
|
@@ -445,6 +445,9 @@ body.dark .net .mark {
|
|
|
445
445
|
aside {
|
|
446
446
|
padding: 0 10px;
|
|
447
447
|
}
|
|
448
|
+
aside .tab.submenu {
|
|
449
|
+
padding: 10px;
|
|
450
|
+
}
|
|
448
451
|
aside .tab i {
|
|
449
452
|
width: 100%;
|
|
450
453
|
}
|
|
@@ -608,7 +611,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
608
611
|
<h3>
|
|
609
612
|
<div class='item-icon'>
|
|
610
613
|
<% if (item.icon) { %>
|
|
611
|
-
<
|
|
614
|
+
<div class='placeholder-icon'>
|
|
615
|
+
<img src="<%=item.icon%>" onerror="this.src='/pinokio-black.png'"/>
|
|
616
|
+
</div>
|
|
612
617
|
<% } else { %>
|
|
613
618
|
<div class='placeholder-icon'>
|
|
614
619
|
<i class="fa-solid fa-database"></i>
|
|
@@ -619,31 +624,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
619
624
|
<div class='title'><i class="fa-solid fa-circle"></i><span><%=item.title || item.name%></span></div>
|
|
620
625
|
<div class='grid-3'>
|
|
621
626
|
<div class='section'>
|
|
622
|
-
<%
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
627
|
+
<% item.internal_router.forEach((domain) => { %>
|
|
628
|
+
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
629
|
+
<% }) %>
|
|
630
|
+
<a class='net' target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
|
|
631
|
+
</div>
|
|
632
|
+
<div class='section'>
|
|
633
|
+
<% if (item.external_ip) { %>
|
|
634
|
+
<a class='net' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
|
|
628
635
|
<% } %>
|
|
629
636
|
</div>
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
</div>
|
|
636
|
-
<% } else { %>
|
|
637
|
-
<div class='section'>
|
|
638
|
-
<% if (item.external_ip) { %>
|
|
639
|
-
<a class='net' target="_blank" href="http://<%=item.external_ip%>"><span class='mark'>HTTP</span><span><%=item.external_ip%></span></a>
|
|
640
|
-
<% } %>
|
|
641
|
-
</div>
|
|
642
|
-
<% } %>
|
|
637
|
+
<div class='section'>
|
|
638
|
+
<% item.external_router.forEach((domain) => { %>
|
|
639
|
+
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
640
|
+
<% }) %>
|
|
641
|
+
</div>
|
|
643
642
|
</div>
|
|
644
643
|
</div>
|
|
645
|
-
</
|
|
646
|
-
</
|
|
644
|
+
</h3>
|
|
645
|
+
</div>
|
|
647
646
|
<% }) %>
|
|
648
647
|
<% } else { %>
|
|
649
648
|
<div class='section-container'>
|
|
@@ -653,7 +652,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
653
652
|
<div class='col'>
|
|
654
653
|
<div class='grid-1'>
|
|
655
654
|
<div class='section'>
|
|
656
|
-
<h2>Online</h2><div>Use these URLs to access the online remote servers.</div>
|
|
655
|
+
<h2><i class="fa-solid fa-plug-circle-check"></i> Online</h2><div>Use these URLs to access the online remote servers.</div>
|
|
657
656
|
</div>
|
|
658
657
|
</div>
|
|
659
658
|
</div>
|
|
@@ -664,7 +663,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
664
663
|
<h3>
|
|
665
664
|
<div class='item-icon'>
|
|
666
665
|
<% if (item[`${protocol}_icon`]) { %>
|
|
667
|
-
<img src="<%=item[`${protocol}_icon`]%>"
|
|
666
|
+
<img src="<%=item[`${protocol}_icon`]%>" onerror="this.src='/pinokio-black.png'"/>
|
|
668
667
|
<% } else { %>
|
|
669
668
|
<div class='placeholder-icon'>
|
|
670
669
|
<i class="fa-solid fa-database"></i>
|
|
@@ -697,7 +696,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
697
696
|
<div class='col'>
|
|
698
697
|
<div class='grid-1'>
|
|
699
698
|
<div class='section'>
|
|
700
|
-
<h2>Installed</h2><div>Remotely launch the installed apps.</div>
|
|
699
|
+
<h2><i class="fa-solid fa-folder-tree"></i> Installed</h2><div>Remotely launch the installed apps.</div>
|
|
701
700
|
</div>
|
|
702
701
|
</div>
|
|
703
702
|
</div>
|
|
@@ -708,7 +707,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
708
707
|
<h3>
|
|
709
708
|
<div class='item-icon'>
|
|
710
709
|
<% if (item[`${protocol}_icon`]) { %>
|
|
711
|
-
<img src="<%=item[`${protocol}_icon`]%>"
|
|
710
|
+
<img src="<%=item[`${protocol}_icon`]%>" onerror="this.src='/pinokio-black.png'"/>
|
|
712
711
|
<% } else { %>
|
|
713
712
|
<div class='placeholder-icon'>
|
|
714
713
|
<i class="fa-solid fa-database"></i>
|
|
@@ -734,6 +733,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
734
733
|
</h3>
|
|
735
734
|
<% }) %>
|
|
736
735
|
</div>
|
|
736
|
+
<!--
|
|
737
737
|
<div class='section-col'>
|
|
738
738
|
<div class='section-header'>
|
|
739
739
|
<h3>
|
|
@@ -770,6 +770,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
770
770
|
</h3>
|
|
771
771
|
<% }) %>
|
|
772
772
|
</div>
|
|
773
|
+
-->
|
|
773
774
|
</div>
|
|
774
775
|
<% } %>
|
|
775
776
|
</div>
|
|
@@ -831,6 +832,7 @@ let filteredCount = list.length
|
|
|
831
832
|
let filteredBtnsCount = 0
|
|
832
833
|
|
|
833
834
|
const renderSearch = () => {
|
|
835
|
+
console.log("renderSearch")
|
|
834
836
|
let target = document.querySelector("form input[type=search]")
|
|
835
837
|
let result
|
|
836
838
|
if (target.value.trim().length === 0) {
|
|
@@ -841,6 +843,7 @@ const renderSearch = () => {
|
|
|
841
843
|
result = search(list, target.value)
|
|
842
844
|
}
|
|
843
845
|
let all = document.querySelectorAll(".line.index")
|
|
846
|
+
console.log("all", all)
|
|
844
847
|
for(let el of all) {
|
|
845
848
|
el.classList.add("hidden")
|
|
846
849
|
el.classList.remove("selected")
|