pinokiod 3.265.0 → 3.271.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 +2 -2
- package/kernel/peer.js +186 -19
- package/package.json +1 -1
- package/server/public/container-tab-link.js +0 -2
- package/server/public/style.css +19 -1
- package/server/public/urldropdown.css +5 -0
- package/server/views/agents.ejs +15 -14
- package/server/views/app.ejs +1 -4
- package/server/views/connect.ejs +6 -2
- package/server/views/container.ejs +1 -0
- package/server/views/index.ejs +1 -0
- package/server/views/net.ejs +18 -20
- package/server/views/network.ejs +24 -1
- package/server/views/screenshots.ejs +1 -0
- package/server/views/settings.ejs +1 -0
- package/server/views/tools.ejs +10 -0
package/kernel/index.js
CHANGED
|
@@ -803,6 +803,7 @@ class Kernel {
|
|
|
803
803
|
async init(options) {
|
|
804
804
|
|
|
805
805
|
let home = this.store.get("home") || process.env.PINOKIO_HOME
|
|
806
|
+
this.homedir = home
|
|
806
807
|
|
|
807
808
|
// reset shells if they exist
|
|
808
809
|
if (this.shell) {
|
|
@@ -820,10 +821,9 @@ class Kernel {
|
|
|
820
821
|
this.kv = new KV(this)
|
|
821
822
|
this.cloudflare = new Cloudflare()
|
|
822
823
|
this.peer = new Peer(this)
|
|
824
|
+
await this.peer.initialize(this)
|
|
823
825
|
this.git = new Git(this)
|
|
824
826
|
|
|
825
|
-
this.homedir = home
|
|
826
|
-
|
|
827
827
|
// if (home) {
|
|
828
828
|
// this.homedir = home
|
|
829
829
|
// } else {
|
package/kernel/peer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const dgram = require('dgram');
|
|
2
2
|
const axios = require('axios');
|
|
3
3
|
const os = require('os')
|
|
4
|
+
const systeminformation = require('systeminformation')
|
|
4
5
|
const Environment = require("./environment")
|
|
5
6
|
class PeerDiscovery {
|
|
6
7
|
constructor(kernel, port = 41234, message = 'ping', interval = 1000) {
|
|
@@ -12,9 +13,8 @@ class PeerDiscovery {
|
|
|
12
13
|
this.peers = new Set();
|
|
13
14
|
this.interface_addresses = []
|
|
14
15
|
this.host_candidates = []
|
|
15
|
-
this.host =
|
|
16
|
+
this.host = null
|
|
16
17
|
this.default_port = 42000
|
|
17
|
-
this.peers.add(this.host)
|
|
18
18
|
this.router_info_cache = {}
|
|
19
19
|
// this.start();
|
|
20
20
|
}
|
|
@@ -85,6 +85,13 @@ class PeerDiscovery {
|
|
|
85
85
|
this.active = false
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
// Prepare host/peer state before the rest of the kernel bootstraps
|
|
89
|
+
async initialize(kernel) {
|
|
90
|
+
await this.refreshLocalAddress()
|
|
91
|
+
if (kernel) {
|
|
92
|
+
await this.check(kernel)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
88
95
|
async start(kernel) {
|
|
89
96
|
let env = await Environment.get(kernel.homedir, kernel)
|
|
90
97
|
|
|
@@ -576,23 +583,70 @@ class PeerDiscovery {
|
|
|
576
583
|
_isLocalLAN(ip) {
|
|
577
584
|
return this.isRFC1918(ip)
|
|
578
585
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
+
// Refresh LAN/IP selection; keeps peers set in sync with the active address
|
|
587
|
+
async refreshLocalAddress() {
|
|
588
|
+
try {
|
|
589
|
+
const { host, host_candidates, interface_addresses } = await this._getLocalIPAddress()
|
|
590
|
+
this.interface_addresses = interface_addresses
|
|
591
|
+
this.host_candidates = host_candidates
|
|
592
|
+
if (host && this.host !== host) {
|
|
593
|
+
if (this.host) {
|
|
594
|
+
this.peers.delete(this.host)
|
|
595
|
+
}
|
|
596
|
+
this.host = host
|
|
597
|
+
} else if (!this.host) {
|
|
598
|
+
this.host = host
|
|
599
|
+
}
|
|
600
|
+
if (this.host) {
|
|
601
|
+
this.peers.add(this.host)
|
|
602
|
+
}
|
|
603
|
+
return this.host
|
|
604
|
+
} catch (err) {
|
|
605
|
+
console.error('peer refreshLocalAddress error', err)
|
|
606
|
+
if (!this.host) {
|
|
607
|
+
this.host = null
|
|
608
|
+
}
|
|
609
|
+
return this.host
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
async _getLocalIPAddress() {
|
|
613
|
+
const interface_addresses = await this._collectInterfaceAddresses()
|
|
614
|
+
const shareable = interface_addresses.filter((entry) => entry.shareable)
|
|
615
|
+
const host_candidates = shareable.map((entry) => ({
|
|
616
|
+
address: entry.address,
|
|
617
|
+
netmask: entry.netmask,
|
|
618
|
+
interface: entry.interface,
|
|
619
|
+
scope: entry.scope,
|
|
620
|
+
shareable: entry.shareable,
|
|
621
|
+
type: entry.type || null,
|
|
622
|
+
operstate: entry.operstate || null,
|
|
623
|
+
virtual: entry.virtual || false,
|
|
624
|
+
default: entry.default || false,
|
|
625
|
+
prefixLength: entry.prefixLength,
|
|
626
|
+
mac: entry.mac || null,
|
|
627
|
+
score: this._scoreCandidate(entry)
|
|
628
|
+
}))
|
|
629
|
+
let selectedHost = null
|
|
630
|
+
let bestScore = -Infinity
|
|
631
|
+
host_candidates.forEach((candidate, index) => {
|
|
632
|
+
const score = typeof candidate.score === 'number' ? candidate.score : -Infinity
|
|
633
|
+
if (score > bestScore) {
|
|
634
|
+
bestScore = score
|
|
635
|
+
selectedHost = candidate.address
|
|
636
|
+
} else if (score === bestScore && selectedHost === null) {
|
|
637
|
+
selectedHost = candidate.address
|
|
638
|
+
}
|
|
639
|
+
})
|
|
640
|
+
if (!selectedHost && shareable.length > 0) {
|
|
641
|
+
selectedHost = shareable[0].address
|
|
586
642
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return cgnatCandidate.address
|
|
643
|
+
if (!selectedHost && interface_addresses.length > 0) {
|
|
644
|
+
selectedHost = interface_addresses[0].address
|
|
590
645
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
return publicCandidate.address
|
|
646
|
+
if (!selectedHost) {
|
|
647
|
+
selectedHost = '127.0.0.1'
|
|
594
648
|
}
|
|
595
|
-
return
|
|
649
|
+
return { host: selectedHost, host_candidates, interface_addresses }
|
|
596
650
|
}
|
|
597
651
|
isPrivateOrCGNAT(ip) {
|
|
598
652
|
return this.isRFC1918(ip) || this.isCGNAT(ip)
|
|
@@ -626,7 +680,7 @@ class PeerDiscovery {
|
|
|
626
680
|
}
|
|
627
681
|
return secondOctet >= 16 && secondOctet <= 31
|
|
628
682
|
}
|
|
629
|
-
|
|
683
|
+
_collectInterfaceAddressesSync() {
|
|
630
684
|
const interfaces = os.networkInterfaces()
|
|
631
685
|
const results = []
|
|
632
686
|
const seen = new Set()
|
|
@@ -653,12 +707,51 @@ class PeerDiscovery {
|
|
|
653
707
|
interface: ifaceName,
|
|
654
708
|
internal: Boolean(iface.internal),
|
|
655
709
|
scope: classification.scope,
|
|
656
|
-
shareable: classification.shareable
|
|
710
|
+
shareable: classification.shareable,
|
|
711
|
+
mac: typeof iface.mac === 'string' ? iface.mac : null
|
|
657
712
|
})
|
|
658
713
|
}
|
|
659
714
|
}
|
|
660
715
|
return results
|
|
661
716
|
}
|
|
717
|
+
async _collectInterfaceAddresses() {
|
|
718
|
+
const baseEntries = this._collectInterfaceAddressesSync()
|
|
719
|
+
let metadata = []
|
|
720
|
+
try {
|
|
721
|
+
metadata = await systeminformation.networkInterfaces()
|
|
722
|
+
} catch (err) {
|
|
723
|
+
metadata = []
|
|
724
|
+
}
|
|
725
|
+
const metadataMap = new Map()
|
|
726
|
+
if (Array.isArray(metadata)) {
|
|
727
|
+
metadata.forEach((entry) => {
|
|
728
|
+
if (entry && typeof entry.iface === 'string') {
|
|
729
|
+
metadataMap.set(this._normalizeInterfaceName(entry.iface), entry)
|
|
730
|
+
}
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
return baseEntries.map((entry) => {
|
|
734
|
+
const key = this._normalizeInterfaceName(entry.interface)
|
|
735
|
+
const meta = key ? metadataMap.get(key) : null
|
|
736
|
+
const prefixLength = this._prefixLengthFromNetmask(entry.netmask)
|
|
737
|
+
return {
|
|
738
|
+
...entry,
|
|
739
|
+
prefixLength,
|
|
740
|
+
type: meta && meta.type ? meta.type : null,
|
|
741
|
+
operstate: meta && meta.operstate ? meta.operstate : null,
|
|
742
|
+
speed: typeof meta?.speed === 'number' ? meta.speed : null,
|
|
743
|
+
virtual: Boolean(meta && meta.virtual),
|
|
744
|
+
default: Boolean(meta && meta.default),
|
|
745
|
+
mac: entry.mac || (meta && meta.mac) || null
|
|
746
|
+
}
|
|
747
|
+
})
|
|
748
|
+
}
|
|
749
|
+
_normalizeInterfaceName(name) {
|
|
750
|
+
if (!name || typeof name !== 'string') {
|
|
751
|
+
return ''
|
|
752
|
+
}
|
|
753
|
+
return name.trim().toLowerCase()
|
|
754
|
+
}
|
|
662
755
|
classifyAddress(address, isInternal = false) {
|
|
663
756
|
if (!address || typeof address !== 'string') {
|
|
664
757
|
return { scope: 'unknown', shareable: false }
|
|
@@ -684,6 +777,80 @@ class PeerDiscovery {
|
|
|
684
777
|
}
|
|
685
778
|
return { scope: 'public', shareable: true }
|
|
686
779
|
}
|
|
780
|
+
_prefixLengthFromNetmask(netmask) {
|
|
781
|
+
if (!netmask || typeof netmask !== 'string') {
|
|
782
|
+
return null
|
|
783
|
+
}
|
|
784
|
+
const octets = this._parseIPv4(netmask)
|
|
785
|
+
if (!octets) {
|
|
786
|
+
return null
|
|
787
|
+
}
|
|
788
|
+
let bits = 0
|
|
789
|
+
for (const octet of octets) {
|
|
790
|
+
bits += this._countBits(octet)
|
|
791
|
+
}
|
|
792
|
+
return bits
|
|
793
|
+
}
|
|
794
|
+
_countBits(value) {
|
|
795
|
+
let count = 0
|
|
796
|
+
let v = value & 255
|
|
797
|
+
while (v) {
|
|
798
|
+
count += v & 1
|
|
799
|
+
v >>= 1
|
|
800
|
+
}
|
|
801
|
+
return count
|
|
802
|
+
}
|
|
803
|
+
// Heuristically rank interface candidates so physical LAN adapters win over VPN/tunnels
|
|
804
|
+
_scoreCandidate(entry) {
|
|
805
|
+
if (!entry || !entry.shareable) {
|
|
806
|
+
return -Infinity
|
|
807
|
+
}
|
|
808
|
+
let score = 0
|
|
809
|
+
switch (entry.scope) {
|
|
810
|
+
case 'lan':
|
|
811
|
+
score += 100
|
|
812
|
+
break
|
|
813
|
+
case 'cgnat':
|
|
814
|
+
score += 60
|
|
815
|
+
break
|
|
816
|
+
case 'public':
|
|
817
|
+
score += 40
|
|
818
|
+
break
|
|
819
|
+
default:
|
|
820
|
+
score -= 50
|
|
821
|
+
break
|
|
822
|
+
}
|
|
823
|
+
if (entry.default) {
|
|
824
|
+
score += 20
|
|
825
|
+
}
|
|
826
|
+
const type = entry.type ? entry.type.toLowerCase() : ''
|
|
827
|
+
if (type === 'wired') {
|
|
828
|
+
score += 25
|
|
829
|
+
} else if (type === 'wireless') {
|
|
830
|
+
score += 18
|
|
831
|
+
} else if (type === 'vpn') {
|
|
832
|
+
score -= 40
|
|
833
|
+
} else if (type === 'cellular') {
|
|
834
|
+
score += 5
|
|
835
|
+
}
|
|
836
|
+
if (entry.virtual) {
|
|
837
|
+
score -= 25
|
|
838
|
+
}
|
|
839
|
+
if (entry.operstate && entry.operstate.toLowerCase() === 'up') {
|
|
840
|
+
score += 5
|
|
841
|
+
} else if (entry.operstate) {
|
|
842
|
+
score -= 10
|
|
843
|
+
}
|
|
844
|
+
if (typeof entry.prefixLength === 'number') {
|
|
845
|
+
if (entry.prefixLength <= 24) {
|
|
846
|
+
score += 5
|
|
847
|
+
}
|
|
848
|
+
if (entry.prefixLength >= 30) {
|
|
849
|
+
score -= 20
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return score
|
|
853
|
+
}
|
|
687
854
|
_buildExternalHostEntries(externalPort) {
|
|
688
855
|
if (!externalPort && externalPort !== 0) {
|
|
689
856
|
return []
|
|
@@ -741,7 +908,7 @@ class PeerDiscovery {
|
|
|
741
908
|
return entries
|
|
742
909
|
}
|
|
743
910
|
_broadcastTargets() {
|
|
744
|
-
const addresses = this.
|
|
911
|
+
const addresses = this._collectInterfaceAddressesSync()
|
|
745
912
|
this.interface_addresses = addresses
|
|
746
913
|
const targets = new Set()
|
|
747
914
|
for (const entry of addresses) {
|
package/package.json
CHANGED
|
@@ -100,8 +100,6 @@
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
if (mobileButton) {
|
|
103
|
-
mobileButton.addEventListener('mouseover', () => showPopoverForAnchor(mobileButton, urlInput))
|
|
104
|
-
mobileButton.addEventListener('mouseout', (event) => handleMouseLeave(mobileButton, event))
|
|
105
103
|
mobileButton.addEventListener('focus', () => showPopoverForAnchor(mobileButton, urlInput))
|
|
106
104
|
mobileButton.addEventListener('blur', (event) => handleMouseLeave(mobileButton, event))
|
|
107
105
|
}
|
package/server/public/style.css
CHANGED
|
@@ -1796,8 +1796,9 @@ body.dark .btn {
|
|
|
1796
1796
|
background: rgba(0,0,0,0.8);
|
|
1797
1797
|
}
|
|
1798
1798
|
body.dark .swal2-popup {
|
|
1799
|
-
background:
|
|
1799
|
+
background: #090909;
|
|
1800
1800
|
color: var(--dark-btn-color);
|
|
1801
|
+
border: 5px solid rgba(255,255,255,0.2);
|
|
1801
1802
|
}
|
|
1802
1803
|
.swal2-popup {
|
|
1803
1804
|
border-radius: 0px !important;
|
|
@@ -2586,6 +2587,18 @@ aside .qr {
|
|
|
2586
2587
|
padding: 15px;
|
|
2587
2588
|
}
|
|
2588
2589
|
|
|
2590
|
+
@media only screen and (max-width: 600px) {
|
|
2591
|
+
aside .tab, aside .tab.submenu {
|
|
2592
|
+
flex-direction: column !important;
|
|
2593
|
+
padding: 10px 0 !important;
|
|
2594
|
+
}
|
|
2595
|
+
aside .caption {
|
|
2596
|
+
width: 50px !important;
|
|
2597
|
+
text-align: center !important;
|
|
2598
|
+
word-wrap: break-word !important;
|
|
2599
|
+
display: block !important;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2589
2602
|
|
|
2590
2603
|
@media only screen and (max-width: 768px) {
|
|
2591
2604
|
/* Hide QR block on small screens */
|
|
@@ -2723,6 +2736,11 @@ body[data-agent='electron'] {
|
|
|
2723
2736
|
padding-top: 26px;
|
|
2724
2737
|
}
|
|
2725
2738
|
*/
|
|
2739
|
+
/*
|
|
2740
|
+
body:not([data-agent='electron']) #inspector {
|
|
2741
|
+
display: none !important;
|
|
2742
|
+
}
|
|
2743
|
+
*/
|
|
2726
2744
|
body.dark #dropdown-portal .dropdown-content {
|
|
2727
2745
|
background: rgb(34, 34, 34);
|
|
2728
2746
|
}
|
|
@@ -806,7 +806,9 @@ body.dark .urlbar input[type=url] {
|
|
|
806
806
|
.mobile-link-button {
|
|
807
807
|
background: rgba(0,0,0,0.05);
|
|
808
808
|
padding: 5px;
|
|
809
|
+
/*
|
|
809
810
|
flex-grow: 1;
|
|
811
|
+
*/
|
|
810
812
|
margin: 5px 0;
|
|
811
813
|
border-radius: 10px;
|
|
812
814
|
}
|
|
@@ -827,6 +829,9 @@ body.dark .urlbar input[type=url] {
|
|
|
827
829
|
.mobile-link-button {
|
|
828
830
|
display: block;
|
|
829
831
|
}
|
|
832
|
+
.mobile-flexible {
|
|
833
|
+
flex-grow: 1;
|
|
834
|
+
}
|
|
830
835
|
}
|
|
831
836
|
.mobile-link-button {
|
|
832
837
|
display: none;
|
package/server/views/agents.ejs
CHANGED
|
@@ -41,7 +41,6 @@ body.plugin-page aside .tab.selected {
|
|
|
41
41
|
opacity: 1;
|
|
42
42
|
}
|
|
43
43
|
body.plugin-page aside .tab i {
|
|
44
|
-
width: 20px;
|
|
45
44
|
text-align: center;
|
|
46
45
|
}
|
|
47
46
|
body.plugin-page .plugin-container {
|
|
@@ -52,7 +51,7 @@ body.plugin-page .plugin-container {
|
|
|
52
51
|
body.plugin-page .btn-tab {
|
|
53
52
|
display: flex;
|
|
54
53
|
align-items: center;
|
|
55
|
-
gap:
|
|
54
|
+
gap: 5px;
|
|
56
55
|
}
|
|
57
56
|
body.plugin-page .btn-tab .btn {
|
|
58
57
|
display: flex;
|
|
@@ -339,20 +338,29 @@ body.dark .plugin-option:hover {
|
|
|
339
338
|
}
|
|
340
339
|
}
|
|
341
340
|
@media (max-width: 600px) {
|
|
341
|
+
/*
|
|
342
342
|
body.plugin-page main {
|
|
343
343
|
flex-direction: column;
|
|
344
344
|
}
|
|
345
|
+
*/
|
|
345
346
|
body.plugin-page aside {
|
|
346
347
|
width: auto;
|
|
347
348
|
display: flex;
|
|
348
349
|
flex-wrap: wrap;
|
|
349
|
-
gap:
|
|
350
|
-
padding: 0
|
|
350
|
+
gap: 0px;
|
|
351
|
+
padding: 0 10px;
|
|
352
|
+
flex-direction: column;
|
|
351
353
|
}
|
|
352
|
-
body.plugin-page aside .tab,
|
|
353
354
|
body.plugin-page aside .btn-tab {
|
|
355
|
+
flex-direction: column;
|
|
356
|
+
padding: 10px 0;
|
|
357
|
+
}
|
|
358
|
+
body.plugin-page aside .tab {
|
|
354
359
|
border-left: none;
|
|
355
360
|
opacity: 1;
|
|
361
|
+
flex-direction: column;
|
|
362
|
+
padding: 10px;
|
|
363
|
+
margin: 0;
|
|
356
364
|
}
|
|
357
365
|
body.plugin-page aside .caption {
|
|
358
366
|
display: none;
|
|
@@ -400,7 +408,7 @@ body.dark .plugin-option:hover {
|
|
|
400
408
|
}) %>
|
|
401
409
|
</head>
|
|
402
410
|
<body class='<%=theme%> plugin-page' data-agent="<%=agent%>">
|
|
403
|
-
<header class='grabbable'>
|
|
411
|
+
<header class='navheader grabbable'>
|
|
404
412
|
<h1>
|
|
405
413
|
<a class='home' href="/home"><img class='icon' src="/pinokio-black.png" alt="Pinokio"></a>
|
|
406
414
|
<button class='btn2' id='minimize-header' data-tippy-content="fullscreen" title='fullscreen'>
|
|
@@ -427,7 +435,7 @@ body.dark .plugin-option:hover {
|
|
|
427
435
|
</h1>
|
|
428
436
|
</header>
|
|
429
437
|
<main class='plugin-main'>
|
|
430
|
-
<div class='plugin-container'>
|
|
438
|
+
<div class='plugin-container container'>
|
|
431
439
|
<h1><i class="fa-solid fa-robot"></i>Agents</h1>
|
|
432
440
|
<% const pluginCategories = pluginMenu.reduce((acc, pluginItem, index) => {
|
|
433
441
|
const runs = Array.isArray(pluginItem.run) ? pluginItem.run : []
|
|
@@ -477,13 +485,6 @@ body.dark .plugin-option:hover {
|
|
|
477
485
|
<div class='subtitle'><%=pluginItem.description%></div>
|
|
478
486
|
<% } %>
|
|
479
487
|
</div>
|
|
480
|
-
<div class='flexible'></div>
|
|
481
|
-
<% if (pluginItem.link) { %>
|
|
482
|
-
<button type='button' class='plugin-info' data-link="<%=pluginItem.link.replace(/"/g, '"')%>" aria-label="Open info"><i class="fa-solid fa-circle-info"></i></button>
|
|
483
|
-
<% } %>
|
|
484
|
-
<div class='disclosure-indicator' aria-hidden="true">
|
|
485
|
-
<i class="fa-solid fa-chevron-right"></i>
|
|
486
|
-
</div>
|
|
487
488
|
</div>
|
|
488
489
|
<% }) %>
|
|
489
490
|
</div>
|
package/server/views/app.ejs
CHANGED
|
@@ -3017,10 +3017,7 @@ body.dark .pinokio-fork-dropdown-remote, body.dark .pinokio-publish-dropdown-rem
|
|
|
3017
3017
|
<%if (type==='browse') { %>
|
|
3018
3018
|
<a id='devtab' data-mode="refresh" target="<%=dev_link%>" href="<%=dev_link%>" class="btn frame-link selected" data-index="10">
|
|
3019
3019
|
<div class="tab">
|
|
3020
|
-
|
|
3021
|
-
<img src="<%=config.icon%>" onerror="this.src='/pinokio-black.png'"/>
|
|
3022
|
-
<% } %>
|
|
3023
|
-
<div><%=config.title%></div>
|
|
3020
|
+
<i class="fa-solid fa-bars"></i> Launchers
|
|
3024
3021
|
</div>
|
|
3025
3022
|
</a>
|
|
3026
3023
|
|
package/server/views/connect.ejs
CHANGED
|
@@ -481,10 +481,13 @@ body.dark .open-menu, body.dark .browse {
|
|
|
481
481
|
}
|
|
482
482
|
.grid-3 {
|
|
483
483
|
display: grid;
|
|
484
|
-
grid-template-columns: repeat(
|
|
484
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); /* Responsive columns that wrap */
|
|
485
485
|
gap: 1rem; /* Optional spacing between columns */
|
|
486
486
|
padding: 10px;
|
|
487
487
|
}
|
|
488
|
+
.grid-3 > * {
|
|
489
|
+
min-width: 0;
|
|
490
|
+
}
|
|
488
491
|
.section-header .grid-1, .section-header .grid-2, .section-header .grid-3 {
|
|
489
492
|
background: none !important;
|
|
490
493
|
padding: 0 !important;
|
|
@@ -546,7 +549,6 @@ aside .selected {
|
|
|
546
549
|
*/
|
|
547
550
|
.tab-content {
|
|
548
551
|
width: 100%;
|
|
549
|
-
display: flex;
|
|
550
552
|
flex-direction: column;
|
|
551
553
|
flex-wrap: wrap;
|
|
552
554
|
gap: 15px;
|
|
@@ -729,6 +731,7 @@ body.dark .profile td {
|
|
|
729
731
|
padding:6px 10px;
|
|
730
732
|
white-space: pre-wrap;
|
|
731
733
|
background: rgba(0,0,0,0.04);
|
|
734
|
+
word-break: break-word;
|
|
732
735
|
}
|
|
733
736
|
.app-icon {
|
|
734
737
|
width: 25px;
|
|
@@ -824,6 +827,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
824
827
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
825
828
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
826
829
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
830
|
+
<div class='mobile-flexible'></div>
|
|
827
831
|
<form class='urlbar'>
|
|
828
832
|
<div class='url-input-container'>
|
|
829
833
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
|
@@ -338,6 +338,7 @@ iframe {
|
|
|
338
338
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
339
339
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
340
340
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
341
|
+
<div class='mobile-flexible'></div>
|
|
341
342
|
<form class='urlbar'>
|
|
342
343
|
<div class='url-input-container'>
|
|
343
344
|
<input type='url' placeholder='enter any local url to open in pinokio' value="<%=src%>">
|
package/server/views/index.ejs
CHANGED
|
@@ -437,6 +437,7 @@ body.dark aside .current.selected {
|
|
|
437
437
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
438
438
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
439
439
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
440
|
+
<div class='mobile-flexible'></div>
|
|
440
441
|
<form class='urlbar'>
|
|
441
442
|
<div class='url-input-container'>
|
|
442
443
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
package/server/views/net.ejs
CHANGED
|
@@ -378,10 +378,20 @@ body.dark .open-menu, body.dark .browse {
|
|
|
378
378
|
}
|
|
379
379
|
.grid-3 {
|
|
380
380
|
display: grid;
|
|
381
|
-
grid-template-columns: repeat(
|
|
381
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); /* Responsive columns that wrap */
|
|
382
382
|
gap: 1rem; /* Optional spacing between columns */
|
|
383
383
|
padding: 10px;
|
|
384
384
|
}
|
|
385
|
+
.grid-3 .section > h2 {
|
|
386
|
+
display: flex;
|
|
387
|
+
align-items: center;
|
|
388
|
+
gap: 6px;
|
|
389
|
+
margin: 0 0 8px;
|
|
390
|
+
font-size: 12px;
|
|
391
|
+
}
|
|
392
|
+
.grid-3 > * {
|
|
393
|
+
min-width: 0;
|
|
394
|
+
}
|
|
385
395
|
.section-header .grid-1, .section-header .grid-2, .section-header .grid-3 {
|
|
386
396
|
background: none !important;
|
|
387
397
|
padding: 0 !important;
|
|
@@ -653,6 +663,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
653
663
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
654
664
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
655
665
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
666
|
+
<div class='mobile-flexible'></div>
|
|
656
667
|
<form class='urlbar'>
|
|
657
668
|
<div class='url-input-container'>
|
|
658
669
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
|
@@ -686,25 +697,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
686
697
|
</form>
|
|
687
698
|
<div class='running-apps'>
|
|
688
699
|
<% if (current_host === host) { %>
|
|
689
|
-
<div class='section-header'>
|
|
690
|
-
<h3>
|
|
691
|
-
<div class='blank'></div>
|
|
692
|
-
<div class='col'>
|
|
693
|
-
<div class='grid-3'>
|
|
694
|
-
<div class='section'>
|
|
695
|
-
<h2><i class='fas fa-laptop-code'></i> This machine</h2>
|
|
696
|
-
<div>accessible from this machine</div>
|
|
697
|
-
</div>
|
|
698
|
-
<div class='section'>
|
|
699
|
-
<h2><i class="fa-solid fa-wifi"></i> Local network</h2><div>accessible from any machine on the local network</div>
|
|
700
|
-
</div>
|
|
701
|
-
<div class='section'>
|
|
702
|
-
<h2><i class="fa-solid fa-podcast"></i> Peer</h2><div>accessible from any pinokio peer on the local network</a></div>
|
|
703
|
-
</div>
|
|
704
|
-
</div>
|
|
705
|
-
</div>
|
|
706
|
-
</h3>
|
|
707
|
-
</div>
|
|
708
700
|
<% processes.forEach((item, index) => { %>
|
|
709
701
|
<div class='index line align-top' data-index="<%=index%>" data-name="<%=item.name%>" data-title="<%=item.name%>" data-description="<%=item.internal_router.join(' ')%> <%=item.port%>">
|
|
710
702
|
<h3>
|
|
@@ -723,6 +715,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
723
715
|
<div class='title'><i class="fa-solid fa-circle"></i><span><%=item.title || item.name%></span></div>
|
|
724
716
|
<div class='grid-3'>
|
|
725
717
|
<div class='section'>
|
|
718
|
+
<h2><i class='fas fa-laptop-code'></i> This machine</h2>
|
|
726
719
|
<%
|
|
727
720
|
let default_dns_name = ''
|
|
728
721
|
if (item.internal_router && item.internal_router.length > 0) {
|
|
@@ -755,6 +748,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
755
748
|
<% } %>
|
|
756
749
|
</div>
|
|
757
750
|
<div class='section'>
|
|
751
|
+
<h2><i class="fa-solid fa-wifi"></i> Local network</h2>
|
|
758
752
|
<%
|
|
759
753
|
const directHosts = []
|
|
760
754
|
if (Array.isArray(item.external_hosts)) {
|
|
@@ -801,6 +795,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
801
795
|
<% } %>
|
|
802
796
|
</div>
|
|
803
797
|
<div class='section'>
|
|
798
|
+
<h2><i class="fa-solid fa-podcast"></i> Peer</h2>
|
|
804
799
|
<% item.external_router.forEach((domain) => { %>
|
|
805
800
|
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
806
801
|
<% }) %>
|
|
@@ -822,6 +817,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
822
817
|
<div class='title'><i class="fa-solid fa-circle"></i><span><%=route.name%></span></div>
|
|
823
818
|
<div class='grid-3'>
|
|
824
819
|
<div class='section'>
|
|
820
|
+
<h2><i class='fas fa-laptop-code'></i> This machine</h2>
|
|
825
821
|
<% route.internal_router.forEach((domain) => { %>
|
|
826
822
|
<% if (domain.endsWith(".localhost")) { %>
|
|
827
823
|
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
@@ -831,11 +827,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
831
827
|
<% }) %>
|
|
832
828
|
</div>
|
|
833
829
|
<div class='section'>
|
|
830
|
+
<h2><i class="fa-solid fa-wifi"></i> Local network</h2>
|
|
834
831
|
<% if (route.external_ip) { %>
|
|
835
832
|
<a class='net' target="_blank" href="http://<%=route.external_ip%>">http://<%=route.external_ip%></a>
|
|
836
833
|
<% } %>
|
|
837
834
|
</div>
|
|
838
835
|
<div class='section'>
|
|
836
|
+
<h2><i class="fa-solid fa-podcast"></i> Peer</h2>
|
|
839
837
|
<% route.external_router.forEach((domain) => { %>
|
|
840
838
|
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
841
839
|
<% }) %>
|
package/server/views/network.ejs
CHANGED
|
@@ -444,10 +444,13 @@ body.dark .desc {
|
|
|
444
444
|
}
|
|
445
445
|
.grid-3 {
|
|
446
446
|
display: grid;
|
|
447
|
-
grid-template-columns: repeat(
|
|
447
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); /* Responsive columns that wrap */
|
|
448
448
|
gap: 1rem; /* Optional spacing between columns */
|
|
449
449
|
padding: 10px;
|
|
450
450
|
}
|
|
451
|
+
.grid-3 > * {
|
|
452
|
+
min-width: 0;
|
|
453
|
+
}
|
|
451
454
|
.section-header .grid-1, .section-header .grid-2, .section-header .grid-3 {
|
|
452
455
|
background: none !important;
|
|
453
456
|
padding: 0 !important;
|
|
@@ -1015,6 +1018,25 @@ body.dark .troubleshoot {
|
|
|
1015
1018
|
header .flexible {
|
|
1016
1019
|
min-width: unset;
|
|
1017
1020
|
}
|
|
1021
|
+
.config-body {
|
|
1022
|
+
flex-direction: column;
|
|
1023
|
+
gap: 20px;
|
|
1024
|
+
}
|
|
1025
|
+
.config-row {
|
|
1026
|
+
width: 100%;
|
|
1027
|
+
padding-right: 0;
|
|
1028
|
+
}
|
|
1029
|
+
.network-name {
|
|
1030
|
+
flex-direction: column;
|
|
1031
|
+
align-items: stretch;
|
|
1032
|
+
gap: 10px;
|
|
1033
|
+
}
|
|
1034
|
+
.network-name input[type=text] {
|
|
1035
|
+
width: 100%;
|
|
1036
|
+
}
|
|
1037
|
+
.network-name .btn {
|
|
1038
|
+
width: 100%;
|
|
1039
|
+
}
|
|
1018
1040
|
}
|
|
1019
1041
|
|
|
1020
1042
|
@media only screen and (max-width: 480px) {
|
|
@@ -1060,6 +1082,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1060
1082
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
1061
1083
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
1062
1084
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
1085
|
+
<div class='mobile-flexible'></div>
|
|
1063
1086
|
<form class='urlbar'>
|
|
1064
1087
|
<div class='url-input-container'>
|
|
1065
1088
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
|
@@ -717,6 +717,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
717
717
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
718
718
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
719
719
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
720
|
+
<div class='mobile-flexible'></div>
|
|
720
721
|
<form class='urlbar'>
|
|
721
722
|
<div class='url-input-container'>
|
|
722
723
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
|
@@ -389,6 +389,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
389
389
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
390
390
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
391
391
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
392
|
+
<div class='mobile-flexible'></div>
|
|
392
393
|
<form class='urlbar'>
|
|
393
394
|
<div class='url-input-container'>
|
|
394
395
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|
package/server/views/tools.ejs
CHANGED
|
@@ -971,6 +971,15 @@ aside .selected {
|
|
|
971
971
|
opacity: 1;
|
|
972
972
|
}
|
|
973
973
|
@media only screen and (max-width: 600px) {
|
|
974
|
+
.bundle-header {
|
|
975
|
+
display: block;
|
|
976
|
+
}
|
|
977
|
+
.bundle-status {
|
|
978
|
+
margin-top: 10px;
|
|
979
|
+
}
|
|
980
|
+
.bundles-grid {
|
|
981
|
+
grid-template-columns: minmax(0, 1fr);
|
|
982
|
+
}
|
|
974
983
|
aside {
|
|
975
984
|
width: unset;
|
|
976
985
|
flex-shrink: unset;
|
|
@@ -1107,6 +1116,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1107
1116
|
<button class='btn2' id='screenshot' data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
1108
1117
|
<button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
1109
1118
|
<button class='btn2 mobile-link-button' id='mobile-link-button' data-tippy-content="enter url"><i class="fa-solid fa-link"></i></button>
|
|
1119
|
+
<div class='mobile-flexible'></div>
|
|
1110
1120
|
<form class='urlbar'>
|
|
1111
1121
|
<div class='url-input-container'>
|
|
1112
1122
|
<input type='url' placeholder='enter any local url to open in pinokio'>
|