pinokiod 3.250.0 → 3.252.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.
@@ -162,16 +162,26 @@ body.dark .context-menu-wrapper {
162
162
  color: white;
163
163
  }
164
164
  .context-menu-wrapper {
165
- background: rgba(0,0,0,0.06) !important;
165
+ background: whitesmoke !important;
166
+ border-radius: 10px;
167
+ overflow: hidden;
168
+ }
169
+ .menu-btns {
170
+ position: relative;
166
171
  }
167
172
  .context-menu {
168
- /*
169
- max-height: 10000px;
170
- overflow: hidden;
171
- transition: max-height 1s ease-in-out;
172
- */
173
- display: flex;
174
- margin: 0 5px 0 0;
173
+ position: absolute;
174
+ right: auto;
175
+ margin: 0;
176
+ padding: 0;
177
+ z-index: 10;
178
+ border: none;
179
+ outline: none;
180
+ background: transparent;
181
+ }
182
+ .context-menu:popover-open {
183
+ border: none;
184
+ outline: none;
175
185
  }
176
186
  .context-menu .btn:hover {
177
187
  color: cornflowerblue !important;
@@ -200,12 +210,6 @@ body.dark .btn {
200
210
  body.dark .context-menu .btn {
201
211
  color: white;
202
212
  }
203
- .context-menu.collapsed {
204
- /*
205
- max-height: 0;
206
- */
207
- display: none;
208
- }
209
213
  body.dark .open-menu, body.dark .browse {
210
214
  border: none !important;
211
215
  /*
@@ -507,7 +511,7 @@ body.dark aside .current.selected {
507
511
  -->
508
512
  <% if (running.length > 0) { %>
509
513
  <div class='running-apps'>
510
- <% running.forEach((item) => { %>
514
+ <% running.forEach((item, index) => { %>
511
515
  <a target="<%=item.target || '_self'%>" href="<%=item.browser_url%>" data-description="<%=item.description%>" data-index="<%=item.index%>" data-name="<%=item.name%>" data-title="<%=item.name%>" data-uri="<%=item.uri%>" data-icon="<%=item.icon%>" data-path="<%=item.path%>" data-iconpath="<%=item.iconpath ? item.iconpath : ''%>" class='line align-top'>
512
516
  <h3>
513
517
  <div class='item-icon'>
@@ -538,10 +542,10 @@ body.dark aside .current.selected {
538
542
  <button class='btn browse' data-src="<%=item.view_url%>">
539
543
  <i class="fa-regular fa-eye"></i> View
540
544
  </button>
541
- <button class='btn open-menu'>
545
+ <button class='btn open-menu' type='button' popovertarget="context-menu-running-<%=index%>" popovertargetaction="toggle" aria-haspopup="menu">
542
546
  <i class="fa-solid fa-bars"></i><span> Menu</span>
543
547
  </button>
544
- <div class='context-menu collapsed'>
548
+ <div id='context-menu-running-<%=index%>' class='context-menu' popover>
545
549
  <div class='context-menu-wrapper'>
546
550
  <button class='btn' data-filepath="<%=item.filepath%>">
547
551
  <i class="fa-solid fa-folder-open"></i> Open</button>
@@ -601,7 +605,7 @@ body.dark aside .current.selected {
601
605
  <!--
602
606
  <div class='header-label'>Not Running</div>
603
607
  -->
604
- <% notRunning.forEach((item) => { %>
608
+ <% notRunning.forEach((item, index) => { %>
605
609
  <a target="<%=item.target || '_self'%>" href="<%=item.browser_url%>" data-description="<%=item.description%>" data-index="<%=item.index%>" data-name="<%=item.name%>" data-title="<%=item.name%>" data-uri="<%=item.uri%>" data-icon="<%=item.icon%>" data-path="<%=item.path%>" data-iconpath="<%=item.iconpath ? item.iconpath : ''%>" class='line align-top'>
606
610
  <h3>
607
611
  <div class='item-icon'>
@@ -632,10 +636,10 @@ body.dark aside .current.selected {
632
636
  <button class='btn browse' data-src="<%=item.view_url%>">
633
637
  <i class="fa-regular fa-eye"></i> View
634
638
  </button>
635
- <button class='btn open-menu'>
639
+ <button class='btn open-menu' type='button' popovertarget="context-menu-not-running-<%=index%>" popovertargetaction="toggle" aria-haspopup="menu">
636
640
  <i class="fa-solid fa-bars"></i><span> Menu</span>
637
641
  </button>
638
- <div class='context-menu collapsed'>
642
+ <div id='context-menu-not-running-<%=index%>' class='context-menu' popover>
639
643
  <div class='context-menu-wrapper'>
640
644
  <button class='btn' data-filepath="<%=item.filepath%>">
641
645
  <i class="fa-solid fa-folder-open"></i> Open</button>
@@ -740,15 +744,7 @@ body.dark aside .current.selected {
740
744
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
741
745
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
742
746
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
743
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
744
- <div class='qr' style='padding:12px 10px; text-align:center;'>
745
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
746
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
747
- <div class='caption'>Scan to open</div>
748
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
749
- </a>
750
- </div>
751
- <% } %>
747
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
752
748
  </aside>
753
749
  </main>
754
750
  <script>
@@ -1101,6 +1097,47 @@ document.addEventListener("keydown", (e) => {
1101
1097
  }
1102
1098
  }
1103
1099
  });
1100
+ const supportsNativePopovers = typeof document.createElement('div').showPopover === 'function'
1101
+ if (!supportsNativePopovers) {
1102
+ document.querySelectorAll('.context-menu[popover]').forEach((menu) => {
1103
+ menu.style.display = 'none'
1104
+ })
1105
+ }
1106
+ const getMenuDimensions = (menu) => {
1107
+ let width = parseFloat(menu.dataset.cachedWidth)
1108
+ let height = parseFloat(menu.dataset.cachedHeight)
1109
+ if (!width || !height) {
1110
+ const prev = {
1111
+ visibility: menu.style.visibility,
1112
+ display: menu.style.display,
1113
+ position: menu.style.position,
1114
+ top: menu.style.top,
1115
+ left: menu.style.left,
1116
+ }
1117
+ menu.style.visibility = 'hidden'
1118
+ menu.style.display = 'block'
1119
+ menu.style.position = 'fixed'
1120
+ menu.style.top = '0'
1121
+ menu.style.left = '0'
1122
+ width = menu.offsetWidth
1123
+ height = menu.offsetHeight
1124
+ if (width > 0) {
1125
+ menu.dataset.cachedWidth = width
1126
+ }
1127
+ if (height > 0) {
1128
+ menu.dataset.cachedHeight = height
1129
+ }
1130
+ menu.style.visibility = prev.visibility
1131
+ menu.style.display = prev.display
1132
+ menu.style.position = prev.position
1133
+ menu.style.top = prev.top
1134
+ menu.style.left = prev.left
1135
+ }
1136
+ return {
1137
+ width: width || 0,
1138
+ height: height || 0
1139
+ }
1140
+ }
1104
1141
  document.addEventListener("click", async (e) => {
1105
1142
  if (e.target.classList.contains("shutdown")) {
1106
1143
  target = e.target
@@ -1170,11 +1207,74 @@ document.addEventListener("click", async (e) => {
1170
1207
  if (target) {
1171
1208
  e.preventDefault()
1172
1209
  e.stopPropagation()
1173
- let contextMenu = target.closest(".col").querySelector(".context-menu").classList.toggle("collapsed")
1174
- // target.classList.toggle("selected")
1210
+ let popoverId = target.getAttribute('popovertarget')
1211
+ let contextMenu
1212
+ if (popoverId) {
1213
+ contextMenu = document.getElementById(popoverId)
1214
+ }
1215
+ if (!contextMenu) {
1216
+ contextMenu = target.closest(".col")?.querySelector(".context-menu")
1217
+ }
1218
+ if (contextMenu) {
1219
+ const hasNativePopover = supportsNativePopovers && typeof contextMenu.showPopover === 'function'
1220
+ const isOpen = hasNativePopover ? contextMenu.matches(':popover-open') : contextMenu.dataset.open === 'true'
1221
+ if (isOpen) {
1222
+ if (hasNativePopover) {
1223
+ contextMenu.hidePopover()
1224
+ } else {
1225
+ contextMenu.style.display = 'none'
1226
+ contextMenu.dataset.open = 'false'
1227
+ }
1228
+ return
1229
+ }
1230
+ if (hasNativePopover) {
1231
+ document.querySelectorAll('.context-menu[popover]').forEach((menuEl) => {
1232
+ if (menuEl !== contextMenu && menuEl.matches(':popover-open') && typeof menuEl.hidePopover === 'function') {
1233
+ menuEl.hidePopover()
1234
+ }
1235
+ })
1236
+ } else {
1237
+ document.querySelectorAll('.context-menu[data-open="true"]').forEach((menuEl) => {
1238
+ if (menuEl !== contextMenu) {
1239
+ menuEl.style.display = 'none'
1240
+ menuEl.dataset.open = 'false'
1241
+ }
1242
+ })
1243
+ }
1244
+ const buttonRect = target.getBoundingClientRect()
1245
+ const menuRect = getMenuDimensions(contextMenu)
1246
+ const viewportPadding = 12
1247
+ let left = Math.min(buttonRect.left, window.innerWidth - menuRect.width - viewportPadding)
1248
+ left = Math.max(viewportPadding, left)
1249
+ let top = buttonRect.bottom + 6
1250
+ if (top + menuRect.height + viewportPadding > window.innerHeight) {
1251
+ top = buttonRect.top - menuRect.height - 6
1252
+ }
1253
+ const maxTop = Math.max(viewportPadding, window.innerHeight - menuRect.height - viewportPadding)
1254
+ top = Math.min(Math.max(viewportPadding, top), maxTop)
1255
+ if (hasNativePopover) {
1256
+ contextMenu.style.position = 'fixed'
1257
+ contextMenu.style.left = left + 'px'
1258
+ contextMenu.style.top = top + 'px'
1259
+ contextMenu.style.right = 'auto'
1260
+ contextMenu.style.bottom = 'auto'
1261
+ contextMenu.togglePopover()
1262
+ } else {
1263
+ const btnContainer = target.closest('.menu-btns')
1264
+ if (!btnContainer) {
1265
+ return
1266
+ }
1267
+ const containerRect = btnContainer.getBoundingClientRect()
1268
+ contextMenu.style.left = Math.max(0, left - containerRect.left) + 'px'
1269
+ contextMenu.style.top = top - containerRect.top + 'px'
1270
+ contextMenu.style.right = 'auto'
1271
+ contextMenu.style.bottom = 'auto'
1272
+ contextMenu.style.display = 'block'
1273
+ contextMenu.dataset.open = 'true'
1274
+ }
1275
+ }
1175
1276
  return
1176
1277
  }
1177
-
1178
1278
  if (e.target.classList.contains("move-menu")) {
1179
1279
  target = e.target
1180
1280
  } else {
@@ -1954,15 +1954,7 @@ body.dark .ace-editor {
1954
1954
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
1955
1955
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
1956
1956
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
1957
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
1958
- <div class='qr' style='padding:12px 10px; text-align:center;'>
1959
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
1960
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
1961
- <div class='caption'>Scan to open</div>
1962
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
1963
- </a>
1964
- </div>
1965
- <% } %>
1957
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
1966
1958
  </aside>
1967
1959
  </main>
1968
1960
  <div id='terminal-container' class='hidden'>
@@ -464,6 +464,37 @@ body.dark .net {
464
464
  display: block;
465
465
  background: none;
466
466
  }
467
+ .access-point-list {
468
+ list-style: none;
469
+ padding: 0;
470
+ margin: 6px 0 0 0;
471
+ display: flex;
472
+ flex-direction: column;
473
+ gap: 4px;
474
+ }
475
+ .access-point-list li {
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 6px;
479
+ font-size: 12px;
480
+ }
481
+ .access-point-list .badge {
482
+ display: inline-flex;
483
+ align-items: center;
484
+ justify-content: center;
485
+ padding: 0 6px;
486
+ font-size: 10px;
487
+ border-radius: 999px;
488
+ background: rgba(127, 91, 243, 0.12);
489
+ color: rgba(127, 91, 243, 0.9);
490
+ border: 1px solid rgba(127, 91, 243, 0.3);
491
+ text-transform: uppercase;
492
+ }
493
+ body.dark .access-point-list .badge {
494
+ background: rgba(127, 91, 243, 0.2);
495
+ color: #f0e9ff;
496
+ border-color: rgba(127, 91, 243, 0.4);
497
+ }
467
498
  .net .mark {
468
499
  background: rgba(0,0,0,0.9);
469
500
  border-radius: 3px;
@@ -666,7 +697,7 @@ document.addEventListener('DOMContentLoaded', function() {
666
697
  <h2><i class="fa-solid fa-wifi"></i> Local network</h2><div>accessible from any machine on the local network</div>
667
698
  </div>
668
699
  <div class='section'>
669
- <h2><i class="fa-solid fa-podcast"></i> Peer</h2><div>accessible from any pinokio peer on the local network <a class='explain' data-type='peer'>What is a "Peer"?</a></div>
700
+ <h2><i class="fa-solid fa-podcast"></i> Peer</h2><div>accessible from any pinokio peer on the local network</a></div>
670
701
  </div>
671
702
  </div>
672
703
  </div>
@@ -722,7 +753,45 @@ document.addEventListener('DOMContentLoaded', function() {
722
753
  <% } %>
723
754
  </div>
724
755
  <div class='section'>
725
- <% if (item.external_ip) { %>
756
+ <%
757
+ const directHosts = []
758
+ if (Array.isArray(item.external_hosts)) {
759
+ const seenHosts = new Set()
760
+ item.external_hosts.forEach((hostEntry) => {
761
+ if (!hostEntry || !hostEntry.url) {
762
+ return
763
+ }
764
+ const key = hostEntry.url.toLowerCase()
765
+ if (seenHosts.has(key)) {
766
+ return
767
+ }
768
+ seenHosts.add(key)
769
+ const scope = (hostEntry.scope || '').toLowerCase()
770
+ let badge = ''
771
+ if (scope === 'lan') badge = 'LAN'
772
+ else if (scope === 'cgnat') badge = 'VPN'
773
+ else if (scope === 'public') badge = 'Public'
774
+ else if (scope === 'loopback') badge = 'Local'
775
+ else if (scope === 'linklocal') badge = 'Link-Local'
776
+ directHosts.push({
777
+ url: hostEntry.url,
778
+ badge
779
+ })
780
+ })
781
+ }
782
+ %>
783
+ <% if (directHosts.length > 0) { %>
784
+ <ul class="access-point-list">
785
+ <% directHosts.forEach((point) => { %>
786
+ <li>
787
+ <a class='net' target="_blank" href="<%= point.url %>"><%= point.url %></a>
788
+ <% if (point.badge) { %>
789
+ <span class="badge"><%= point.badge %></span>
790
+ <% } %>
791
+ </li>
792
+ <% }) %>
793
+ </ul>
794
+ <% } else if (item.external_ip) { %>
726
795
  <a class='net' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
727
796
  <% } %>
728
797
  </div>
@@ -937,15 +1006,7 @@ document.addEventListener('DOMContentLoaded', function() {
937
1006
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
938
1007
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
939
1008
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
940
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
941
- <div class='qr' style='padding:12px 10px; text-align:center;'>
942
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
943
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
944
- <div class='caption'>Scan to open</div>
945
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
946
- </a>
947
- </div>
948
- <% } %>
1009
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
949
1010
  </aside>
950
1011
  </main>
951
1012
  <script>
@@ -1185,16 +1185,8 @@ document.addEventListener('DOMContentLoaded', function() {
1185
1185
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
1186
1186
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
1187
1187
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
1188
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
1189
- <div class='qr' style='padding:12px 10px; text-align:center;'>
1190
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
1191
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
1192
- <div class='caption'>Scan to open</div>
1193
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
1194
- </a>
1195
- </div>
1196
- <% } %>
1197
- </aside>
1188
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
1189
+ </aside>
1198
1190
  </main>
1199
1191
  <script>
1200
1192
  let interval
@@ -0,0 +1,27 @@
1
+ <%
2
+ const accessPoints = Array.isArray(peer_access_points) ? peer_access_points : []
3
+ const hasAccessPoints = accessPoints.length > 0
4
+ %>
5
+ <% if (hasAccessPoints) { %>
6
+ <% accessPoints.forEach((point) => { %>
7
+ <% if (!point || !point.url) { return } %>
8
+ <% const qrSrc = point.qr || (`/qr?data=${encodeURIComponent(point.url)}&s=4&m=0`) %>
9
+ <div class="qr">
10
+ <a href="<%= point.url %>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
11
+ <img src="<%= qrSrc %>" alt="Open <%= point.url %>" style="width:128px; height:128px; image-rendering: pixelated;"/>
12
+ <div class='caption'>
13
+ Scan to open<% if (point.scope_label) { %> (<%= point.scope_label %>)<% } %>
14
+ </div>
15
+ <div class='caption' style='font-size:10px; opacity:0.7;'><%= point.url %></div>
16
+ </a>
17
+ </div>
18
+ <% }) %>
19
+ <% } else if (typeof peer_qr !== 'undefined' && peer_qr) { %>
20
+ <div class='qr'>
21
+ <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
22
+ <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
23
+ <div class='caption'>Scan to open</div>
24
+ <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
25
+ </a>
26
+ </div>
27
+ <% } %>
@@ -948,10 +948,10 @@ body.dark .top-menu .btn2.selected {
948
948
  <button class='btn2' id='inspector' data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
949
949
  <button class='btn2' id='hidden-placeholder'><i class="fa-solid fa-bars"></i></button>
950
950
  <div class='mode-selector'>
951
- <a class="btn2 <%=type === 'review' ? 'selected' : ''%>" href="<%=review_tab%>"><div><i class="fa-regular fa-message"></i></div><div class='caption'>Forum</div></a>
952
- <a class="btn2 <%=type === 'files' ? 'selected' : ''%>" href="/p/<%=name%>/files"><div><i class="fa-solid fa-file-lines"></i></div><div class='caption'>Files</div></a>
953
- <a class="btn2 <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>"><div><i class="fa-solid fa-code"></i></div><div class='caption'>Dev</div></a>
954
951
  <a class="btn2 <%=type === 'run' ? 'selected' : ''%>" href="<%=run_tab%>"><div><i class="fa-solid fa-circle-play"></i></div><div class='caption'>Run</div></a>
952
+ <a class="btn2 <%=type === 'browse' ? 'selected' : ''%>" href="<%=dev_tab%>"><div><i class="fa-solid fa-code"></i></div><div class='caption'>Dev</div></a>
953
+ <a class="btn2 <%=type === 'files' ? 'selected' : ''%>" href="/p/<%=name%>/files"><div><i class="fa-solid fa-file-lines"></i></div><div class='caption'>Files</div></a>
954
+ <a class="btn2 <%=type === 'review' ? 'selected' : ''%>" href="<%=review_tab%>"><div><i class="fa-regular fa-message"></i></div><div class='caption'>Forum</div></a>
955
955
  </div>
956
956
  <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
957
957
  <div><i class="fa-solid fa-table-columns"></i></div>
@@ -776,15 +776,7 @@ document.addEventListener('DOMContentLoaded', function() {
776
776
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
777
777
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
778
778
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
779
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
780
- <div class='qr' style='padding:12px 10px; text-align:center;'>
781
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
782
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
783
- <div class='caption'>Scan to open</div>
784
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
785
- </a>
786
- </div>
787
- <% } %>
779
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
788
780
  </aside>
789
781
  </main>
790
782
  <script>
@@ -571,15 +571,7 @@ document.addEventListener('DOMContentLoaded', function() {
571
571
  <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
572
572
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
573
573
  <a class='tab selected' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
574
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
575
- <div class='qr' style='padding:12px 10px; text-align:center;'>
576
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
577
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
578
- <div class='caption'>Scan to open</div>
579
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
580
- </a>
581
- </div>
582
- <% } %>
574
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
583
575
  </aside>
584
576
  </main>
585
577
  <script>
@@ -1493,6 +1493,9 @@ document.addEventListener("DOMContentLoaded", async () => {
1493
1493
  this.term.focus()
1494
1494
  }, true)
1495
1495
  term.attachCustomKeyEventHandler(event => {
1496
+ if (event.type && event.type.toLowerCase() !== 'keydown') {
1497
+ return true
1498
+ }
1496
1499
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1497
1500
  const selection = term.getSelection();
1498
1501
  if (selection) {
@@ -1532,6 +1532,9 @@ document.addEventListener("DOMContentLoaded", async () => {
1532
1532
  this.term.focus()
1533
1533
  }, true)
1534
1534
  term.attachCustomKeyEventHandler(event => {
1535
+ if (event.type && event.type.toLowerCase() !== 'keydown') {
1536
+ return true
1537
+ }
1535
1538
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1536
1539
  const selection = term.getSelection();
1537
1540
  if (selection) {
@@ -1275,15 +1275,7 @@ document.addEventListener('DOMContentLoaded', function() {
1275
1275
  <a class='tab selected' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
1276
1276
  <a class='tab' href="/agents"><i class="fa-solid fa-robot"></i><div class='caption'>Agents</div></a>
1277
1277
  <a class='tab' href="/home?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
1278
- <% if (typeof peer_qr !== 'undefined' && peer_qr) { %>
1279
- <div class='qr' style='padding:12px 10px; text-align:center;'>
1280
- <a href="<%=peer_url%>" target="_blank" style="text-decoration:none; color:inherit; display:block;">
1281
- <img src="<%=peer_qr%>" alt="Open <%=peer_url%>" style="width:128px; height:128px; image-rendering: pixelated;"/>
1282
- <div class='caption'>Scan to open</div>
1283
- <div class='caption' style='font-size:10px; opacity:0.7;'><%=peer_url%></div>
1284
- </a>
1285
- </div>
1286
- <% } %>
1278
+ <%- include('partials/peer_access_points', { peer_access_points, peer_url, peer_qr }) %>
1287
1279
  </aside>
1288
1280
  </main>
1289
1281
  <script>