pinokiod 7.5.44 → 7.5.46
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/package.json +1 -1
- package/server/index.js +403 -24
- package/server/public/style.css +0 -264
- package/server/public/urldropdown.css +7 -0
- package/server/views/app.ejs +30 -3
- package/server/views/app_search_test.ejs +2 -0
- package/server/views/connect/x.ejs +2 -0
- package/server/views/container.ejs +2 -0
- package/server/views/create.ejs +3 -0
- package/server/views/editor.ejs +3 -0
- package/server/views/env_editor.ejs +2 -0
- package/server/views/explore.ejs +2 -0
- package/server/views/general_editor.ejs +3 -0
- package/server/views/help.ejs +2 -0
- package/server/views/index.ejs +539 -2
- package/server/views/init/index.ejs +2 -0
- package/server/views/keys.ejs +2 -0
- package/server/views/mini.ejs +2 -0
- package/server/views/net.ejs +2 -0
- package/server/views/network.ejs +887 -94
- package/server/views/partials/app_navheader.ejs +2 -0
- package/server/views/partials/home_action_modal.ejs +22 -0
- package/server/views/partials/home_server_popover.ejs +21 -0
- package/server/views/partials/home_server_popover_assets.ejs +1262 -0
- package/server/views/partials/main_sidebar.ejs +1 -221
- package/server/views/pro.ejs +7 -0
- package/server/views/prototype/index.ejs +2 -0
- package/server/views/review.ejs +2 -0
- package/server/views/screenshots.ejs +2 -0
- package/server/views/settings.ejs +2 -0
- package/server/views/shell.ejs +7 -0
- package/server/views/task.ejs +2 -0
- package/server/views/terminal.ejs +2 -0
- package/server/views/terminals.ejs +2 -0
- package/test/launch-settings-ui.test.js +366 -0
- package/test/main-sidebar.test.js +26 -16
package/server/views/network.ejs
CHANGED
|
@@ -1079,14 +1079,14 @@ body.network-page .container {
|
|
|
1079
1079
|
margin: 0;
|
|
1080
1080
|
}
|
|
1081
1081
|
body.network-page .network-config-section {
|
|
1082
|
-
padding-top:
|
|
1082
|
+
padding-top: 12px;
|
|
1083
1083
|
border-top: 0;
|
|
1084
1084
|
}
|
|
1085
1085
|
body.network-page .network-config-head {
|
|
1086
1086
|
align-items: center;
|
|
1087
|
-
margin-bottom:
|
|
1088
|
-
padding-bottom:
|
|
1089
|
-
border-bottom:
|
|
1087
|
+
margin-bottom: 10px;
|
|
1088
|
+
padding-bottom: 0;
|
|
1089
|
+
border-bottom: 0;
|
|
1090
1090
|
}
|
|
1091
1091
|
body.network-page .config {
|
|
1092
1092
|
margin: 0;
|
|
@@ -1098,13 +1098,13 @@ body.network-page .config-header {
|
|
|
1098
1098
|
}
|
|
1099
1099
|
body.network-page .config-body {
|
|
1100
1100
|
display: grid;
|
|
1101
|
-
grid-template-columns: minmax(0,
|
|
1102
|
-
gap:
|
|
1101
|
+
grid-template-columns: minmax(0, 420px);
|
|
1102
|
+
gap: 8px;
|
|
1103
1103
|
padding: 0;
|
|
1104
1104
|
border: 0;
|
|
1105
1105
|
border-radius: 0;
|
|
1106
1106
|
background: transparent;
|
|
1107
|
-
align-items:
|
|
1107
|
+
align-items: center;
|
|
1108
1108
|
}
|
|
1109
1109
|
body.network-page .config-row {
|
|
1110
1110
|
padding: 0;
|
|
@@ -1168,18 +1168,23 @@ body.network-page .switcher input:checked + .slider {
|
|
|
1168
1168
|
}
|
|
1169
1169
|
body.network-page #network_name {
|
|
1170
1170
|
display: grid;
|
|
1171
|
-
|
|
1171
|
+
grid-template-columns: auto minmax(180px, 320px);
|
|
1172
|
+
align-items: center;
|
|
1173
|
+
gap: 10px;
|
|
1174
|
+
width: fit-content;
|
|
1175
|
+
max-width: 100%;
|
|
1172
1176
|
}
|
|
1173
1177
|
body.network-page .network-name {
|
|
1174
1178
|
display: block;
|
|
1175
1179
|
}
|
|
1176
1180
|
body.network-page .network-name input[type=text] {
|
|
1177
|
-
width:
|
|
1181
|
+
width: 320px;
|
|
1182
|
+
max-width: 100%;
|
|
1178
1183
|
min-width: 0;
|
|
1179
|
-
min-height:
|
|
1180
|
-
padding:
|
|
1184
|
+
min-height: 34px;
|
|
1185
|
+
padding: 6px 11px;
|
|
1181
1186
|
border: 1px solid var(--task-border-strong);
|
|
1182
|
-
border-radius:
|
|
1187
|
+
border-radius: 8px;
|
|
1183
1188
|
background: color-mix(in srgb, var(--task-panel) 92%, var(--task-soft));
|
|
1184
1189
|
color: var(--task-text);
|
|
1185
1190
|
box-sizing: border-box;
|
|
@@ -1191,14 +1196,14 @@ body.network-page #network_name .label {
|
|
|
1191
1196
|
margin-bottom: 0;
|
|
1192
1197
|
}
|
|
1193
1198
|
body.network-page .advanced {
|
|
1194
|
-
margin-top:
|
|
1199
|
+
margin-top: 8px;
|
|
1195
1200
|
justify-content: flex-start;
|
|
1196
1201
|
}
|
|
1197
1202
|
body.network-page .troubleshoot,
|
|
1198
1203
|
body.dark.network-page .troubleshoot {
|
|
1199
1204
|
color: var(--task-muted);
|
|
1200
1205
|
padding: 0;
|
|
1201
|
-
font-size:
|
|
1206
|
+
font-size: 12px;
|
|
1202
1207
|
line-height: 1.5;
|
|
1203
1208
|
}
|
|
1204
1209
|
body.network-page .troubleshoot .link-label {
|
|
@@ -1215,33 +1220,97 @@ body.network-page .loading {
|
|
|
1215
1220
|
background: transparent;
|
|
1216
1221
|
color: var(--task-muted);
|
|
1217
1222
|
}
|
|
1223
|
+
body.network-page .net-summary {
|
|
1224
|
+
display: flex;
|
|
1225
|
+
align-items: center;
|
|
1226
|
+
justify-content: flex-end;
|
|
1227
|
+
gap: 10px;
|
|
1228
|
+
color: var(--task-muted);
|
|
1229
|
+
font-size: 13px;
|
|
1230
|
+
white-space: nowrap;
|
|
1231
|
+
}
|
|
1232
|
+
body.network-page .net-summary-item + .net-summary-item {
|
|
1233
|
+
padding-left: 10px;
|
|
1234
|
+
border-left: 1px solid var(--task-border);
|
|
1235
|
+
}
|
|
1236
|
+
body.network-page .net-summary strong {
|
|
1237
|
+
color: var(--task-text);
|
|
1238
|
+
}
|
|
1239
|
+
body.network-page .network-routes-section {
|
|
1240
|
+
margin-top: 14px;
|
|
1241
|
+
padding-top: 14px;
|
|
1242
|
+
border-top: 1px solid var(--task-border);
|
|
1243
|
+
}
|
|
1244
|
+
body.network-page .net-section-head {
|
|
1245
|
+
display: flex;
|
|
1246
|
+
align-items: center;
|
|
1247
|
+
justify-content: space-between;
|
|
1248
|
+
gap: 16px;
|
|
1249
|
+
margin-bottom: 10px;
|
|
1250
|
+
}
|
|
1251
|
+
body.network-page .net-toolbar {
|
|
1252
|
+
flex: 0 0 auto;
|
|
1253
|
+
padding: 0;
|
|
1254
|
+
}
|
|
1255
|
+
body.network-page .net-search-field {
|
|
1256
|
+
width: 260px;
|
|
1257
|
+
min-height: 34px;
|
|
1258
|
+
padding: 0 10px;
|
|
1259
|
+
gap: 8px;
|
|
1260
|
+
border-radius: 8px;
|
|
1261
|
+
background: color-mix(in srgb, var(--task-panel) 94%, var(--task-soft));
|
|
1262
|
+
border: 1px solid var(--task-border-strong);
|
|
1263
|
+
box-shadow: none;
|
|
1264
|
+
}
|
|
1265
|
+
body.network-page .net-search-field .task-library-search-input {
|
|
1266
|
+
min-height: 0;
|
|
1267
|
+
height: 32px;
|
|
1268
|
+
padding: 0;
|
|
1269
|
+
border: 0 !important;
|
|
1270
|
+
border-radius: 0;
|
|
1271
|
+
background: transparent !important;
|
|
1272
|
+
box-shadow: none;
|
|
1273
|
+
color: var(--task-text);
|
|
1274
|
+
}
|
|
1275
|
+
body.network-page .net-search-field .task-library-search-input:focus {
|
|
1276
|
+
outline: 0;
|
|
1277
|
+
box-shadow: none;
|
|
1278
|
+
}
|
|
1218
1279
|
body.network-page .running-apps {
|
|
1219
1280
|
display: grid;
|
|
1220
|
-
gap:
|
|
1281
|
+
gap: 0;
|
|
1221
1282
|
}
|
|
1222
1283
|
body.network-page .line {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
border-
|
|
1226
|
-
|
|
1284
|
+
box-sizing: border-box;
|
|
1285
|
+
padding: 11px 14px;
|
|
1286
|
+
border-top: 1px solid var(--task-border);
|
|
1287
|
+
border-radius: 6px;
|
|
1288
|
+
background: transparent;
|
|
1289
|
+
transition: background-color 120ms ease-out;
|
|
1290
|
+
}
|
|
1291
|
+
body.network-page .line:hover,
|
|
1292
|
+
body.network-page .line.selected {
|
|
1293
|
+
background: color-mix(in srgb, var(--task-soft) 34%, transparent);
|
|
1227
1294
|
}
|
|
1228
1295
|
body.network-page .line.align-top h3 {
|
|
1229
1296
|
margin: 0;
|
|
1230
1297
|
display: grid;
|
|
1231
|
-
grid-template-columns:
|
|
1232
|
-
gap:
|
|
1298
|
+
grid-template-columns: 24px minmax(0, 1fr);
|
|
1299
|
+
gap: 9px;
|
|
1233
1300
|
align-items: start;
|
|
1301
|
+
font-size: inherit;
|
|
1234
1302
|
}
|
|
1235
1303
|
body.network-page .placeholder-icon,
|
|
1236
1304
|
body.network-page .line img {
|
|
1237
|
-
width:
|
|
1238
|
-
height:
|
|
1239
|
-
border-radius:
|
|
1240
|
-
background:
|
|
1305
|
+
width: 22px;
|
|
1306
|
+
height: 22px;
|
|
1307
|
+
border-radius: 5px;
|
|
1308
|
+
background: transparent;
|
|
1241
1309
|
display: flex;
|
|
1242
1310
|
align-items: center;
|
|
1243
1311
|
justify-content: center;
|
|
1244
|
-
color: var(--task-
|
|
1312
|
+
color: var(--task-muted);
|
|
1313
|
+
font-size: 11px;
|
|
1245
1314
|
}
|
|
1246
1315
|
body.network-page .line img {
|
|
1247
1316
|
object-fit: cover;
|
|
@@ -1249,60 +1318,274 @@ body.network-page .line img {
|
|
|
1249
1318
|
}
|
|
1250
1319
|
body.network-page .line .col {
|
|
1251
1320
|
min-width: 0;
|
|
1321
|
+
padding-left: 0;
|
|
1322
|
+
display: grid;
|
|
1323
|
+
grid-template-columns: minmax(160px, 240px) minmax(0, 1fr);
|
|
1324
|
+
gap: 4px 18px;
|
|
1325
|
+
align-items: start;
|
|
1252
1326
|
}
|
|
1253
1327
|
body.network-page .line .title {
|
|
1254
1328
|
display: flex;
|
|
1255
1329
|
align-items: center;
|
|
1256
1330
|
gap: 8px;
|
|
1257
1331
|
color: var(--task-text);
|
|
1258
|
-
font-size:
|
|
1332
|
+
font-size: 14px;
|
|
1259
1333
|
font-weight: 700;
|
|
1334
|
+
line-height: 1.25;
|
|
1335
|
+
margin: 0;
|
|
1336
|
+
min-width: 0;
|
|
1260
1337
|
}
|
|
1261
1338
|
body.network-page .line .title i {
|
|
1262
|
-
|
|
1339
|
+
flex: 0 0 auto;
|
|
1340
|
+
font-size: 8px;
|
|
1263
1341
|
color: color-mix(in srgb, #84cc16 75%, var(--task-panel));
|
|
1264
1342
|
}
|
|
1343
|
+
body.network-page .line .title span {
|
|
1344
|
+
min-width: 0;
|
|
1345
|
+
overflow-wrap: anywhere;
|
|
1346
|
+
}
|
|
1347
|
+
body.network-page .line .description {
|
|
1348
|
+
grid-column: 1;
|
|
1349
|
+
margin: 2px 0 0;
|
|
1350
|
+
padding: 0;
|
|
1351
|
+
color: var(--task-muted);
|
|
1352
|
+
font-size: 12px;
|
|
1353
|
+
line-height: 1.45;
|
|
1354
|
+
}
|
|
1355
|
+
body.network-page .line .grid-3,
|
|
1356
|
+
body.network-page .line .grid-1,
|
|
1357
|
+
body.network-page .line .launch-btns {
|
|
1358
|
+
grid-column: 2;
|
|
1359
|
+
grid-row: 1 / span 3;
|
|
1360
|
+
}
|
|
1265
1361
|
body.network-page .grid-3 {
|
|
1266
|
-
|
|
1362
|
+
display: grid;
|
|
1363
|
+
grid-template-columns: minmax(0, 1.15fr) repeat(2, minmax(0, 1fr));
|
|
1364
|
+
padding: 4px 0 0;
|
|
1267
1365
|
gap: 14px;
|
|
1366
|
+
background: none !important;
|
|
1367
|
+
margin-top: 0;
|
|
1268
1368
|
}
|
|
1269
|
-
body.network-page .grid-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
background: color-mix(in srgb, var(--task-panel) 96%, var(--task-soft));
|
|
1369
|
+
body.network-page .grid-1 {
|
|
1370
|
+
padding: 0;
|
|
1371
|
+
background: none !important;
|
|
1372
|
+
margin-top: 0;
|
|
1274
1373
|
}
|
|
1275
|
-
body.network-page .grid-3 .section
|
|
1276
|
-
|
|
1277
|
-
|
|
1374
|
+
body.network-page .line .grid-3 .section,
|
|
1375
|
+
body.network-page .line .grid-1 .section {
|
|
1376
|
+
border: 0;
|
|
1377
|
+
border-radius: 0;
|
|
1378
|
+
padding: 0;
|
|
1379
|
+
background: none;
|
|
1380
|
+
min-height: 0;
|
|
1381
|
+
}
|
|
1382
|
+
body.network-page .line .grid-3 .section:first-child,
|
|
1383
|
+
body.network-page .line .grid-1 .section:first-child {
|
|
1384
|
+
border-left: 0;
|
|
1385
|
+
padding-left: 0;
|
|
1386
|
+
}
|
|
1387
|
+
body.network-page .line .grid-3 .section:last-child,
|
|
1388
|
+
body.network-page .line .grid-1 .section:last-child {
|
|
1389
|
+
padding-right: 0;
|
|
1390
|
+
}
|
|
1391
|
+
body.network-page .grid-3 .section h2,
|
|
1392
|
+
body.network-page .grid-1 .section h2 {
|
|
1393
|
+
display: flex;
|
|
1394
|
+
align-items: center;
|
|
1395
|
+
gap: 6px;
|
|
1396
|
+
margin: 0 0 5px;
|
|
1397
|
+
font-size: 11px;
|
|
1278
1398
|
font-weight: 700;
|
|
1279
1399
|
color: var(--task-text);
|
|
1400
|
+
line-height: 1.2;
|
|
1401
|
+
letter-spacing: 0;
|
|
1402
|
+
}
|
|
1403
|
+
body.network-page .grid-3 .section h2 i,
|
|
1404
|
+
body.network-page .grid-1 .section h2 i {
|
|
1405
|
+
width: 14px;
|
|
1406
|
+
color: var(--task-muted);
|
|
1407
|
+
text-align: center;
|
|
1280
1408
|
}
|
|
1281
1409
|
body.network-page .net {
|
|
1282
|
-
|
|
1410
|
+
display: flex;
|
|
1411
|
+
align-items: center;
|
|
1412
|
+
gap: 6px;
|
|
1413
|
+
max-width: 100%;
|
|
1414
|
+
width: fit-content;
|
|
1415
|
+
margin: 0 0 4px;
|
|
1283
1416
|
padding: 0;
|
|
1284
1417
|
border: 0;
|
|
1285
1418
|
color: var(--task-muted);
|
|
1419
|
+
line-height: 1.45;
|
|
1420
|
+
overflow-wrap: anywhere;
|
|
1286
1421
|
}
|
|
1287
1422
|
body.dark.network-page .net {
|
|
1288
1423
|
color: var(--task-muted);
|
|
1289
1424
|
}
|
|
1425
|
+
body.network-page .net:hover,
|
|
1426
|
+
body.dark.network-page .net:hover {
|
|
1427
|
+
color: var(--task-text);
|
|
1428
|
+
}
|
|
1429
|
+
body.network-page .access-point-list {
|
|
1430
|
+
list-style: none;
|
|
1431
|
+
padding: 0;
|
|
1432
|
+
margin: 0;
|
|
1433
|
+
display: grid;
|
|
1434
|
+
gap: 4px;
|
|
1435
|
+
}
|
|
1436
|
+
body.network-page .access-point-list li {
|
|
1437
|
+
align-items: center;
|
|
1438
|
+
gap: 8px;
|
|
1439
|
+
min-width: 0;
|
|
1440
|
+
}
|
|
1441
|
+
body.network-page .access-point-list .net {
|
|
1442
|
+
margin: 0;
|
|
1443
|
+
display: inline-flex;
|
|
1444
|
+
}
|
|
1290
1445
|
body.network-page .get-dns-btn,
|
|
1291
1446
|
body.network-page .btn,
|
|
1292
1447
|
body.network-page .proxy a {
|
|
1293
|
-
border-radius:
|
|
1448
|
+
border-radius: 6px;
|
|
1294
1449
|
}
|
|
1295
1450
|
body.network-page .get-dns-btn {
|
|
1296
|
-
margin-top:
|
|
1297
|
-
max-width:
|
|
1298
|
-
background:
|
|
1451
|
+
margin-top: 6px;
|
|
1452
|
+
max-width: fit-content;
|
|
1453
|
+
background: transparent;
|
|
1299
1454
|
border: 1px solid var(--task-border);
|
|
1300
1455
|
color: var(--task-text);
|
|
1456
|
+
padding: 0 10px;
|
|
1457
|
+
min-height: 30px;
|
|
1458
|
+
font-weight: 650;
|
|
1459
|
+
width: auto;
|
|
1301
1460
|
}
|
|
1302
1461
|
body.network-page .badge,
|
|
1303
1462
|
body.dark.network-page .badge {
|
|
1304
|
-
|
|
1305
|
-
|
|
1463
|
+
min-height: 18px;
|
|
1464
|
+
padding: 0 6px;
|
|
1465
|
+
border: 1px solid color-mix(in srgb, var(--task-accent) 22%, var(--task-border));
|
|
1466
|
+
border-radius: 999px;
|
|
1467
|
+
background: color-mix(in srgb, var(--task-accent) 10%, var(--task-panel));
|
|
1468
|
+
color: color-mix(in srgb, var(--task-accent) 62%, var(--task-text));
|
|
1469
|
+
display: inline-flex;
|
|
1470
|
+
align-items: center;
|
|
1471
|
+
justify-content: center;
|
|
1472
|
+
font-size: 10px;
|
|
1473
|
+
font-weight: 650;
|
|
1474
|
+
line-height: 1;
|
|
1475
|
+
transform: translateY(-1px);
|
|
1476
|
+
}
|
|
1477
|
+
body.network-page .dns-modal-popup,
|
|
1478
|
+
body.network-page .dns-progress-popup {
|
|
1479
|
+
width: min(560px, calc(100vw - 32px)) !important;
|
|
1480
|
+
padding: 18px !important;
|
|
1481
|
+
border-radius: 8px !important;
|
|
1482
|
+
border: 1px solid var(--task-border) !important;
|
|
1483
|
+
background: var(--task-panel) !important;
|
|
1484
|
+
color: var(--task-text) !important;
|
|
1485
|
+
box-shadow: 0 18px 50px rgba(15, 23, 42, 0.16) !important;
|
|
1486
|
+
}
|
|
1487
|
+
body.dark.network-page .dns-modal-popup,
|
|
1488
|
+
body.dark.network-page .dns-progress-popup {
|
|
1489
|
+
background: #1b1c1d !important;
|
|
1490
|
+
box-shadow: 0 24px 72px rgba(0, 0, 0, 0.42) !important;
|
|
1491
|
+
}
|
|
1492
|
+
body.network-page .dns-modal-popup .swal2-title,
|
|
1493
|
+
body.network-page .dns-progress-popup .swal2-title {
|
|
1494
|
+
margin: 0 0 12px !important;
|
|
1495
|
+
font-size: 18px !important;
|
|
1496
|
+
line-height: 1.25 !important;
|
|
1497
|
+
font-weight: 600 !important;
|
|
1498
|
+
letter-spacing: 0 !important;
|
|
1499
|
+
color: var(--task-text) !important;
|
|
1500
|
+
text-align: left !important;
|
|
1501
|
+
}
|
|
1502
|
+
body.network-page .dns-modal-popup .swal2-html-container,
|
|
1503
|
+
body.network-page .dns-progress-popup .swal2-html-container {
|
|
1504
|
+
margin: 0 !important;
|
|
1505
|
+
padding: 0 !important;
|
|
1506
|
+
color: var(--task-text) !important;
|
|
1507
|
+
text-align: left !important;
|
|
1508
|
+
}
|
|
1509
|
+
body.network-page .dns-modal-popup .swal2-actions,
|
|
1510
|
+
body.network-page .dns-progress-popup .swal2-actions {
|
|
1511
|
+
gap: 8px !important;
|
|
1512
|
+
justify-content: flex-end !important;
|
|
1513
|
+
margin-top: 16px !important;
|
|
1514
|
+
}
|
|
1515
|
+
body.network-page .dns-modal-popup .swal2-confirm.swal2-styled,
|
|
1516
|
+
body.network-page .dns-progress-popup .swal2-confirm.swal2-styled,
|
|
1517
|
+
body.network-page .dns-modal-popup .swal2-cancel.swal2-styled,
|
|
1518
|
+
body.network-page .dns-progress-popup .swal2-cancel.swal2-styled {
|
|
1519
|
+
min-height: 32px !important;
|
|
1520
|
+
padding: 0 12px !important;
|
|
1521
|
+
border-radius: 6px !important;
|
|
1522
|
+
box-shadow: none !important;
|
|
1523
|
+
font-size: 13px !important;
|
|
1524
|
+
font-weight: 600 !important;
|
|
1525
|
+
}
|
|
1526
|
+
body.network-page .dns-modal-popup .swal2-confirm.swal2-styled,
|
|
1527
|
+
body.network-page .dns-progress-popup .swal2-confirm.swal2-styled {
|
|
1528
|
+
background: color-mix(in srgb, var(--task-panel) 78%, var(--task-accent)) !important;
|
|
1529
|
+
border: 1px solid color-mix(in srgb, var(--task-panel) 72%, var(--task-accent)) !important;
|
|
1530
|
+
color: var(--task-accent) !important;
|
|
1531
|
+
}
|
|
1532
|
+
body.network-page .dns-modal-popup .swal2-cancel.swal2-styled,
|
|
1533
|
+
body.network-page .dns-progress-popup .swal2-cancel.swal2-styled {
|
|
1534
|
+
background: color-mix(in srgb, var(--task-panel) 96%, var(--task-soft)) !important;
|
|
1535
|
+
border: 1px solid var(--task-border) !important;
|
|
1536
|
+
color: var(--task-text) !important;
|
|
1537
|
+
}
|
|
1538
|
+
body.network-page .dns-modal .address-section {
|
|
1539
|
+
display: grid;
|
|
1540
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1541
|
+
gap: 10px;
|
|
1542
|
+
margin-bottom: 12px;
|
|
1543
|
+
}
|
|
1544
|
+
body.network-page .dns-modal .address-section input {
|
|
1545
|
+
min-width: 0;
|
|
1546
|
+
height: 38px;
|
|
1547
|
+
padding: 0 12px;
|
|
1548
|
+
border-radius: 6px;
|
|
1549
|
+
border: 1px solid var(--task-border-strong);
|
|
1550
|
+
background: color-mix(in srgb, var(--task-panel) 98%, white);
|
|
1551
|
+
color: var(--task-text);
|
|
1552
|
+
font-size: 13px;
|
|
1553
|
+
box-sizing: border-box;
|
|
1554
|
+
}
|
|
1555
|
+
body.dark.network-page .dns-modal .address-section input {
|
|
1556
|
+
background: color-mix(in srgb, var(--task-panel) 95%, black);
|
|
1557
|
+
}
|
|
1558
|
+
body.network-page .dns-modal .address-result,
|
|
1559
|
+
body.network-page .dns-modal-terminal-container {
|
|
1560
|
+
border: 1px solid var(--task-border);
|
|
1561
|
+
border-radius: 8px;
|
|
1562
|
+
background: color-mix(in srgb, var(--task-panel) 96%, var(--task-soft));
|
|
1563
|
+
}
|
|
1564
|
+
body.network-page .dns-modal .address-result {
|
|
1565
|
+
min-height: 52px;
|
|
1566
|
+
padding: 10px 12px;
|
|
1567
|
+
display: flex;
|
|
1568
|
+
align-items: center;
|
|
1569
|
+
justify-content: space-between;
|
|
1570
|
+
gap: 10px;
|
|
1571
|
+
font-size: 13px;
|
|
1572
|
+
font-weight: 600;
|
|
1573
|
+
color: var(--task-text);
|
|
1574
|
+
}
|
|
1575
|
+
body.network-page .dns-modal .address-result i {
|
|
1576
|
+
color: var(--task-muted);
|
|
1577
|
+
flex: 0 0 auto;
|
|
1578
|
+
}
|
|
1579
|
+
body.network-page .dns-progress-modal {
|
|
1580
|
+
margin-top: 0;
|
|
1581
|
+
}
|
|
1582
|
+
body.network-page .dns-modal-terminal-container {
|
|
1583
|
+
width: 100%;
|
|
1584
|
+
height: 300px;
|
|
1585
|
+
overflow: hidden;
|
|
1586
|
+
}
|
|
1587
|
+
body.network-page .dns-modal-terminal-container .xterm .xterm-viewport {
|
|
1588
|
+
width: initial !important;
|
|
1306
1589
|
}
|
|
1307
1590
|
@media only screen and (max-width: 820px) {
|
|
1308
1591
|
body.network-page .task-shell-header,
|
|
@@ -1310,6 +1593,25 @@ body.dark.network-page .badge {
|
|
|
1310
1593
|
padding-left: 18px;
|
|
1311
1594
|
padding-right: 18px;
|
|
1312
1595
|
}
|
|
1596
|
+
body.network-page .net-summary {
|
|
1597
|
+
justify-content: flex-start;
|
|
1598
|
+
}
|
|
1599
|
+
body.network-page .net-section-head {
|
|
1600
|
+
align-items: flex-start;
|
|
1601
|
+
}
|
|
1602
|
+
body.network-page .line .col {
|
|
1603
|
+
grid-template-columns: 1fr;
|
|
1604
|
+
}
|
|
1605
|
+
body.network-page .line .grid-3,
|
|
1606
|
+
body.network-page .line .grid-1,
|
|
1607
|
+
body.network-page .line .launch-btns {
|
|
1608
|
+
grid-column: 1;
|
|
1609
|
+
grid-row: auto;
|
|
1610
|
+
}
|
|
1611
|
+
body.network-page .grid-3 {
|
|
1612
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
1613
|
+
padding-top: 6px;
|
|
1614
|
+
}
|
|
1313
1615
|
body.network-page .config-save-btn {
|
|
1314
1616
|
width: auto;
|
|
1315
1617
|
}
|
|
@@ -1324,11 +1626,33 @@ body.dark.network-page .badge {
|
|
|
1324
1626
|
body.network-page .network-config-head {
|
|
1325
1627
|
align-items: flex-start;
|
|
1326
1628
|
}
|
|
1629
|
+
body.network-page .config-body,
|
|
1630
|
+
body.network-page #network_name {
|
|
1631
|
+
width: 100%;
|
|
1632
|
+
}
|
|
1633
|
+
body.network-page #network_name {
|
|
1634
|
+
grid-template-columns: 1fr;
|
|
1635
|
+
gap: 6px;
|
|
1636
|
+
}
|
|
1637
|
+
body.network-page .network-name input[type=text] {
|
|
1638
|
+
width: 100%;
|
|
1639
|
+
}
|
|
1327
1640
|
body.network-page .config-toggle-actions {
|
|
1328
1641
|
gap: 10px;
|
|
1329
1642
|
}
|
|
1643
|
+
body.network-page .net-section-head {
|
|
1644
|
+
flex-direction: column;
|
|
1645
|
+
gap: 10px;
|
|
1646
|
+
}
|
|
1647
|
+
body.network-page .net-toolbar,
|
|
1648
|
+
body.network-page .net-search-field {
|
|
1649
|
+
width: 100%;
|
|
1650
|
+
}
|
|
1330
1651
|
body.network-page .line.align-top h3 {
|
|
1331
|
-
grid-template-columns: 1fr;
|
|
1652
|
+
grid-template-columns: 28px minmax(0, 1fr);
|
|
1653
|
+
}
|
|
1654
|
+
body.network-page .line .title {
|
|
1655
|
+
margin-bottom: 2px;
|
|
1332
1656
|
}
|
|
1333
1657
|
body.network-page .item-icon {
|
|
1334
1658
|
margin-bottom: 2px;
|
|
@@ -1336,6 +1660,18 @@ body.dark.network-page .badge {
|
|
|
1336
1660
|
body.network-page .grid-3 {
|
|
1337
1661
|
grid-template-columns: 1fr;
|
|
1338
1662
|
}
|
|
1663
|
+
body.network-page .line .grid-3 .section,
|
|
1664
|
+
body.network-page .line .grid-1 .section {
|
|
1665
|
+
border-left: 0;
|
|
1666
|
+
padding: 0;
|
|
1667
|
+
}
|
|
1668
|
+
body.network-page .dns-modal .address-section {
|
|
1669
|
+
grid-template-columns: 1fr;
|
|
1670
|
+
}
|
|
1671
|
+
body.network-page .dns-modal .address-result {
|
|
1672
|
+
align-items: flex-start;
|
|
1673
|
+
flex-direction: column;
|
|
1674
|
+
}
|
|
1339
1675
|
body.network-page .network-name {
|
|
1340
1676
|
display: block;
|
|
1341
1677
|
}
|
|
@@ -1349,6 +1685,13 @@ body.dark.network-page .badge {
|
|
|
1349
1685
|
<script src="/tippy-bundle.umd.min.js"></script>
|
|
1350
1686
|
<script src="/hotkeys.min.js"></script>
|
|
1351
1687
|
<script src="/sweetalert2.js"></script>
|
|
1688
|
+
<script src="/xterm.js"></script>
|
|
1689
|
+
<script src="/xterm-inline-bridge.js"></script>
|
|
1690
|
+
<script src="/xterm-addon-fit.js"></script>
|
|
1691
|
+
<script src="/xterm-addon-web-links.js"></script>
|
|
1692
|
+
<script src="/xterm-theme.js"></script>
|
|
1693
|
+
<script src="/Socket.js"></script>
|
|
1694
|
+
<script src="/install.js"></script>
|
|
1352
1695
|
<script src="/common.js"></script>
|
|
1353
1696
|
<script src="/opener.js"></script>
|
|
1354
1697
|
<script src="/nav.js"></script>
|
|
@@ -1385,6 +1728,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1385
1728
|
<div class='url-dropdown' id='url-dropdown'></div>
|
|
1386
1729
|
</div>
|
|
1387
1730
|
</form>
|
|
1731
|
+
<%- include('partials/home_server_popover') %>
|
|
1388
1732
|
<a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
|
|
1389
1733
|
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
1390
1734
|
</a>
|
|
@@ -1400,13 +1744,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1400
1744
|
</button>
|
|
1401
1745
|
</h1>
|
|
1402
1746
|
</header>
|
|
1747
|
+
<%- include('partials/home_server_popover_assets') %>
|
|
1403
1748
|
<main>
|
|
1404
1749
|
<div class='task-container'>
|
|
1405
1750
|
<section class='task-shell network-shell'>
|
|
1406
1751
|
<header class='task-shell-header'>
|
|
1407
1752
|
<div class='task-shell-header-main'>
|
|
1408
|
-
<h1 class='task-title'>
|
|
1409
|
-
<p class='task-description'>
|
|
1753
|
+
<h1 class='task-title'>Home Server</h1>
|
|
1754
|
+
<p class='task-description'>Share this Pinokio and running app Web UIs on your home network.</p>
|
|
1755
|
+
</div>
|
|
1756
|
+
<div class='task-header-actions net-summary'>
|
|
1757
|
+
<%- include('partials/net_summary') %>
|
|
1410
1758
|
</div>
|
|
1411
1759
|
</header>
|
|
1412
1760
|
<div class='task-shell-body network-shell-body'>
|
|
@@ -1451,60 +1799,505 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1451
1799
|
<i class="fa-solid fa-circle-notch fa-spin"></i> Stand by. Do not close this window..
|
|
1452
1800
|
</div>
|
|
1453
1801
|
</section>
|
|
1454
|
-
<
|
|
1455
|
-
<div class='
|
|
1456
|
-
|
|
1457
|
-
<
|
|
1458
|
-
<
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
<div class='placeholder-icon'>
|
|
1467
|
-
<i class="fa-solid fa-clock-rotate-left"></i>
|
|
1468
|
-
</div>
|
|
1469
|
-
</div>
|
|
1470
|
-
<div class='col'>
|
|
1471
|
-
<div class='title'><i class="fa-solid fa-circle"></i><span><%=route.name%></span></div>
|
|
1472
|
-
<div class='grid-3'>
|
|
1473
|
-
<div class='section'>
|
|
1474
|
-
<% route.internal_router.forEach((domain) => { %>
|
|
1475
|
-
<% if (domain.endsWith(".localhost")) { %>
|
|
1476
|
-
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
1477
|
-
<% } else { %>
|
|
1478
|
-
<a class='net' target="_blank" href="http://<%=domain%>">http://<%=domain%></a>
|
|
1479
|
-
<% } %>
|
|
1480
|
-
<% }) %>
|
|
1481
|
-
</div>
|
|
1482
|
-
<div class='section'>
|
|
1483
|
-
<% if (route.external_ip) { %>
|
|
1484
|
-
<a class='net' target="_blank" href="http://<%=route.external_ip%>">http://<%=route.external_ip%></a>
|
|
1485
|
-
<% } %>
|
|
1486
|
-
</div>
|
|
1487
|
-
<div class='section'>
|
|
1488
|
-
<% route.external_router.forEach((domain) => { %>
|
|
1489
|
-
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
1490
|
-
<% }) %>
|
|
1491
|
-
</div>
|
|
1492
|
-
</div>
|
|
1493
|
-
</div>
|
|
1494
|
-
</h3>
|
|
1495
|
-
</div>
|
|
1496
|
-
<% }) %>
|
|
1497
|
-
-->
|
|
1802
|
+
<section class='task-section network-routes-section net-routes-section' aria-label='This machine routes'>
|
|
1803
|
+
<div class='task-section-head net-section-head'>
|
|
1804
|
+
<div>
|
|
1805
|
+
<h2 class='task-section-title'>This machine routes</h2>
|
|
1806
|
+
<p class='task-section-subcopy'>Open local, LAN, and peer URLs for apps running here.</p>
|
|
1807
|
+
</div>
|
|
1808
|
+
<form class='search net-toolbar' role='search'>
|
|
1809
|
+
<label class='task-library-search-field net-search-field'>
|
|
1810
|
+
<i class='fa-solid fa-magnifying-glass' aria-hidden='true'></i>
|
|
1811
|
+
<input type='search' class="task-library-search-input flexible" placeholder='Search routes' autocomplete='off'>
|
|
1812
|
+
</label>
|
|
1813
|
+
</form>
|
|
1498
1814
|
</div>
|
|
1499
|
-
|
|
1815
|
+
<div class='container'>
|
|
1816
|
+
<div
|
|
1817
|
+
class='running-apps net-route-list'
|
|
1818
|
+
data-net-routes-active="<%= peer_active && !requirements_pending ? 'true' : 'false' %>"
|
|
1819
|
+
data-net-routes-endpoint="/net/<%= encodeURIComponent(selected_name) %>/routes"
|
|
1820
|
+
>
|
|
1821
|
+
<% if (requirements_pending) { %>
|
|
1822
|
+
<div class='loader'>
|
|
1823
|
+
<i class="fa-solid fa-circle-notch fa-spin"></i>
|
|
1824
|
+
</div>
|
|
1825
|
+
<% } else if (!peer_active) { %>
|
|
1826
|
+
<div class='task-empty-state'>
|
|
1827
|
+
<p class='task-empty-copy'>Home Server is off. Turn it on above to create home-network routes.</p>
|
|
1828
|
+
</div>
|
|
1829
|
+
<% } else { %>
|
|
1830
|
+
<%- include('partials/net_route_list') %>
|
|
1831
|
+
<% } %>
|
|
1832
|
+
</div>
|
|
1833
|
+
</div>
|
|
1834
|
+
</section>
|
|
1500
1835
|
</div>
|
|
1501
1836
|
</div>
|
|
1502
1837
|
</section>
|
|
1503
1838
|
</div>
|
|
1839
|
+
<template id='dns-modal-template'>
|
|
1840
|
+
<div class='dns-modal'>
|
|
1841
|
+
<div class='address-section'>
|
|
1842
|
+
<input type='text' placeholder='domain name' id='dns-modal-name'>
|
|
1843
|
+
<input type='number' placeholder='port number' id='dns-modal-port' min='1'>
|
|
1844
|
+
</div>
|
|
1845
|
+
<div class='address-result' id='dns-modal-result'>
|
|
1846
|
+
<div>https://[NAME].localhost</div>
|
|
1847
|
+
<i class='fa-solid fa-arrow-right'></i>
|
|
1848
|
+
<div>http://localhost:[PORT]</div>
|
|
1849
|
+
</div>
|
|
1850
|
+
</div>
|
|
1851
|
+
</template>
|
|
1504
1852
|
<%- include('partials/main_sidebar', { selected: 'network' }) %>
|
|
1505
1853
|
</main>
|
|
1506
1854
|
<script>
|
|
1507
1855
|
let interval
|
|
1856
|
+
const dnsCwd = "<%- typeof cwd !== 'undefined' ? JSON.stringify(cwd).slice(1, -1) : '' %>"
|
|
1857
|
+
const dnsModalTemplate = document.querySelector('#dns-modal-template')
|
|
1858
|
+
const peerName = "<%= peer && peer.name ? peer.name : '' %>".trim().toLowerCase()
|
|
1859
|
+
const allowDnsCreation = <%= allow_dns_creation ? 'true' : 'false' %>
|
|
1860
|
+
let dnsModalActive = false
|
|
1861
|
+
let dnsSocketInstance = null
|
|
1862
|
+
let dnsResizeObserver = null
|
|
1863
|
+
|
|
1864
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
1865
|
+
const normalizeHost = (value) => {
|
|
1866
|
+
if (!value) return null
|
|
1867
|
+
let str = String(value).trim().toLowerCase()
|
|
1868
|
+
if (!str) return null
|
|
1869
|
+
str = str.replace(/^https?:\/\//, '')
|
|
1870
|
+
const host = str.split('/')[0]
|
|
1871
|
+
return host || null
|
|
1872
|
+
}
|
|
1873
|
+
const waitForDnsPropagation = async (dnsName, { timeout = 20000, interval = 1000 } = {}) => {
|
|
1874
|
+
const base = (dnsName || '').trim().toLowerCase()
|
|
1875
|
+
if (!base) return false
|
|
1876
|
+
const targets = new Set()
|
|
1877
|
+
const pushTarget = (val) => {
|
|
1878
|
+
const host = normalizeHost(val)
|
|
1879
|
+
if (host) targets.add(host)
|
|
1880
|
+
}
|
|
1881
|
+
pushTarget(`${base}.localhost`)
|
|
1882
|
+
if (peerName) {
|
|
1883
|
+
pushTarget(`${base}.${peerName}.localhost`)
|
|
1884
|
+
}
|
|
1885
|
+
const deadline = Date.now() + timeout
|
|
1886
|
+
while (Date.now() < deadline) {
|
|
1887
|
+
try {
|
|
1888
|
+
const info = await fetch('/pinokio/peer', { headers: { 'cache-control': 'no-cache' } }).then((res) => {
|
|
1889
|
+
if (!res.ok) throw new Error('peer info unavailable')
|
|
1890
|
+
return res.json()
|
|
1891
|
+
})
|
|
1892
|
+
const advertisedHosts = new Set()
|
|
1893
|
+
const addHost = (value) => {
|
|
1894
|
+
const host = normalizeHost(value)
|
|
1895
|
+
if (host) advertisedHosts.add(host)
|
|
1896
|
+
}
|
|
1897
|
+
if (info && Array.isArray(info.router_info)) {
|
|
1898
|
+
info.router_info.forEach((proc) => {
|
|
1899
|
+
;(proc.internal_router || []).forEach(addHost)
|
|
1900
|
+
;(proc.external_router || []).forEach(addHost)
|
|
1901
|
+
})
|
|
1902
|
+
}
|
|
1903
|
+
if (info && info.router) {
|
|
1904
|
+
Object.values(info.router).forEach((list) => {
|
|
1905
|
+
if (Array.isArray(list)) {
|
|
1906
|
+
list.forEach(addHost)
|
|
1907
|
+
}
|
|
1908
|
+
})
|
|
1909
|
+
}
|
|
1910
|
+
if (Array.from(targets).some((host) => advertisedHosts.has(host))) {
|
|
1911
|
+
return true
|
|
1912
|
+
}
|
|
1913
|
+
} catch (err) {
|
|
1914
|
+
console.warn('router poll failed', err)
|
|
1915
|
+
}
|
|
1916
|
+
await delay(interval)
|
|
1917
|
+
}
|
|
1918
|
+
return false
|
|
1919
|
+
}
|
|
1920
|
+
const renderDnsPreview = (popup, name, port) => {
|
|
1921
|
+
const preview = popup.querySelector('#dns-modal-result')
|
|
1922
|
+
if (!preview) return
|
|
1923
|
+
const safeName = (name && name.length > 0) ? name : '[NAME]'
|
|
1924
|
+
const safePort = (port && port.length > 0) ? port : '[PORT]'
|
|
1925
|
+
preview.innerHTML = ''
|
|
1926
|
+
const left = document.createElement('div')
|
|
1927
|
+
left.textContent = `https://${safeName}.localhost`
|
|
1928
|
+
const icon = document.createElement('i')
|
|
1929
|
+
icon.className = 'fa-solid fa-arrow-right'
|
|
1930
|
+
const right = document.createElement('div')
|
|
1931
|
+
right.textContent = `http://localhost:${safePort}`
|
|
1932
|
+
preview.append(left, icon, right)
|
|
1933
|
+
}
|
|
1934
|
+
const promptForDnsMapping = async ({ port, name }) => {
|
|
1935
|
+
if (!dnsModalTemplate) {
|
|
1936
|
+
alert('DNS modal unavailable')
|
|
1937
|
+
return null
|
|
1938
|
+
}
|
|
1939
|
+
const result = await Swal.fire({
|
|
1940
|
+
title: 'Get .localhost domain',
|
|
1941
|
+
html: dnsModalTemplate.innerHTML,
|
|
1942
|
+
confirmButtonText: 'Create domain',
|
|
1943
|
+
customClass: {
|
|
1944
|
+
popup: 'dns-modal-popup'
|
|
1945
|
+
},
|
|
1946
|
+
didOpen: () => {
|
|
1947
|
+
dnsModalActive = true
|
|
1948
|
+
const popup = Swal.getPopup()
|
|
1949
|
+
const nameInput = popup.querySelector('#dns-modal-name')
|
|
1950
|
+
const portInput = popup.querySelector('#dns-modal-port')
|
|
1951
|
+
if (nameInput) nameInput.value = name || ''
|
|
1952
|
+
if (portInput) portInput.value = port || ''
|
|
1953
|
+
const updatePreview = () => {
|
|
1954
|
+
renderDnsPreview(popup, nameInput ? nameInput.value.trim() : '', portInput ? portInput.value.trim() : '')
|
|
1955
|
+
}
|
|
1956
|
+
nameInput && nameInput.addEventListener('input', updatePreview)
|
|
1957
|
+
portInput && portInput.addEventListener('input', updatePreview)
|
|
1958
|
+
updatePreview()
|
|
1959
|
+
if (nameInput) {
|
|
1960
|
+
setTimeout(() => nameInput.focus(), 50)
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
willClose: () => {
|
|
1964
|
+
dnsModalActive = false
|
|
1965
|
+
},
|
|
1966
|
+
preConfirm: () => {
|
|
1967
|
+
const popup = Swal.getPopup()
|
|
1968
|
+
const nameInput = popup.querySelector('#dns-modal-name')
|
|
1969
|
+
const portInput = popup.querySelector('#dns-modal-port')
|
|
1970
|
+
const dnsName = nameInput ? nameInput.value.trim() : ''
|
|
1971
|
+
const dnsPort = portInput ? portInput.value.trim() : ''
|
|
1972
|
+
if (!dnsName) {
|
|
1973
|
+
Swal.showValidationMessage('Enter a domain name')
|
|
1974
|
+
return false
|
|
1975
|
+
}
|
|
1976
|
+
if (!dnsPort) {
|
|
1977
|
+
Swal.showValidationMessage('Enter a port number')
|
|
1978
|
+
return false
|
|
1979
|
+
}
|
|
1980
|
+
if (!/^\d+$/.test(dnsPort)) {
|
|
1981
|
+
Swal.showValidationMessage('Port must be numeric')
|
|
1982
|
+
return false
|
|
1983
|
+
}
|
|
1984
|
+
return { dnsName, dnsPort }
|
|
1985
|
+
}
|
|
1986
|
+
})
|
|
1987
|
+
return result.isConfirmed ? result.value : null
|
|
1988
|
+
}
|
|
1989
|
+
const createDnsTerminal = async (container) => {
|
|
1990
|
+
if (!container) {
|
|
1991
|
+
throw new Error('Terminal container missing')
|
|
1992
|
+
}
|
|
1993
|
+
if (typeof Terminal === 'undefined') {
|
|
1994
|
+
throw new Error('Terminal unavailable')
|
|
1995
|
+
}
|
|
1996
|
+
let config = {
|
|
1997
|
+
scrollback: 9999999,
|
|
1998
|
+
fontSize: 12,
|
|
1999
|
+
fontFamily: 'monospace',
|
|
2000
|
+
theme: document.body.classList.contains('dark') ? xtermTheme.FrontEndDelight : xtermTheme.Tomorrow,
|
|
2001
|
+
}
|
|
2002
|
+
try {
|
|
2003
|
+
const res = await fetch('/xterm_config').then((r) => r.json())
|
|
2004
|
+
if (res && res.config) {
|
|
2005
|
+
config = res.config
|
|
2006
|
+
}
|
|
2007
|
+
} catch (err) {
|
|
2008
|
+
console.warn('Failed to load xterm config', err)
|
|
2009
|
+
}
|
|
2010
|
+
const term = new Terminal(config)
|
|
2011
|
+
const fitAddon = new FitAddon.FitAddon()
|
|
2012
|
+
term.loadAddon(fitAddon)
|
|
2013
|
+
if (typeof WebLinksAddon !== 'undefined' && WebLinksAddon.WebLinksAddon) {
|
|
2014
|
+
<% if (agent === "electron") { %>
|
|
2015
|
+
term.loadAddon(new WebLinksAddon.WebLinksAddon((event, uri) => {
|
|
2016
|
+
window.open(uri, "_blank", "browser")
|
|
2017
|
+
}))
|
|
2018
|
+
<% } else { %>
|
|
2019
|
+
term.loadAddon(new WebLinksAddon.WebLinksAddon())
|
|
2020
|
+
<% } %>
|
|
2021
|
+
}
|
|
2022
|
+
container.innerHTML = ''
|
|
2023
|
+
term.open(container)
|
|
2024
|
+
fitAddon.fit()
|
|
2025
|
+
if (window.PinokioTouch && typeof window.PinokioTouch.bindTerminalFocus === 'function') {
|
|
2026
|
+
window.PinokioTouch.bindTerminalFocus(term, container)
|
|
2027
|
+
}
|
|
2028
|
+
const observer = new ResizeObserver(() => {
|
|
2029
|
+
try {
|
|
2030
|
+
fitAddon.fit()
|
|
2031
|
+
} catch (_) {}
|
|
2032
|
+
})
|
|
2033
|
+
observer.observe(container)
|
|
2034
|
+
return { term, observer }
|
|
2035
|
+
}
|
|
2036
|
+
const showDnsError = (message) => {
|
|
2037
|
+
Swal.close()
|
|
2038
|
+
Swal.fire('Domain error', message, 'error')
|
|
2039
|
+
}
|
|
2040
|
+
const startDnsCreationSocket = (term, dnsName, dnsPort) => {
|
|
2041
|
+
if (dnsSocketInstance && typeof dnsSocketInstance.close === 'function') {
|
|
2042
|
+
try { dnsSocketInstance.close() } catch (_) {}
|
|
2043
|
+
}
|
|
2044
|
+
const socket = new Socket()
|
|
2045
|
+
dnsSocketInstance = socket
|
|
2046
|
+
const write = (text) => {
|
|
2047
|
+
if (text && text !== '\u0007') {
|
|
2048
|
+
term.write(text)
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
socket.run({
|
|
2052
|
+
id: 'kernel.proto.create',
|
|
2053
|
+
method: 'kernel.proto.create',
|
|
2054
|
+
cwd: dnsCwd,
|
|
2055
|
+
client: {
|
|
2056
|
+
cols: term.cols,
|
|
2057
|
+
rows: term.rows,
|
|
2058
|
+
},
|
|
2059
|
+
params: {
|
|
2060
|
+
name: dnsName,
|
|
2061
|
+
startType: 'new',
|
|
2062
|
+
projectType: 'dns',
|
|
2063
|
+
dnsPort,
|
|
2064
|
+
},
|
|
2065
|
+
}, (packet) => {
|
|
2066
|
+
if (packet.type === 'stream') {
|
|
2067
|
+
if (packet.data && packet.data.raw) {
|
|
2068
|
+
write(packet.data.raw)
|
|
2069
|
+
} else if (packet.data && packet.data.json) {
|
|
2070
|
+
write(JSON.stringify(packet.data.json).replace(/\n/g, '\r\n'))
|
|
2071
|
+
write('\r\n')
|
|
2072
|
+
} else if (packet.data && packet.data.json2) {
|
|
2073
|
+
write(JSON.stringify(packet.data.json2, null, 2).replace(/\n/g, '\r\n'))
|
|
2074
|
+
write('\r\n')
|
|
2075
|
+
}
|
|
2076
|
+
} else if (packet.type === 'result') {
|
|
2077
|
+
if (packet.data && packet.data.error) {
|
|
2078
|
+
write(`\r\n${packet.data.error}\r\n`)
|
|
2079
|
+
showDnsError(packet.data.error)
|
|
2080
|
+
} else {
|
|
2081
|
+
write('\r\nDomain created. Waiting for router to update...\r\n')
|
|
2082
|
+
const hostname = `${dnsName}.localhost`
|
|
2083
|
+
waitForDnsPropagation(dnsName)
|
|
2084
|
+
.then((ready) => {
|
|
2085
|
+
write(ready ? `Router detected ${hostname}. Reloading...\r\n` : 'Router update took longer than expected. Reloading anyway...\r\n')
|
|
2086
|
+
})
|
|
2087
|
+
.catch((err) => {
|
|
2088
|
+
console.warn('dns propagation wait failed', err)
|
|
2089
|
+
write('Unable to confirm router update. Reloading...\r\n')
|
|
2090
|
+
})
|
|
2091
|
+
.finally(() => {
|
|
2092
|
+
setTimeout(() => {
|
|
2093
|
+
window.location.reload()
|
|
2094
|
+
}, 500)
|
|
2095
|
+
})
|
|
2096
|
+
}
|
|
2097
|
+
} else if (packet.type === 'error') {
|
|
2098
|
+
const message = (packet.data && (packet.data.error || packet.data.message)) || 'Unknown error'
|
|
2099
|
+
write(`\r\n${message}\r\n`)
|
|
2100
|
+
showDnsError(message)
|
|
2101
|
+
}
|
|
2102
|
+
}).then(() => {
|
|
2103
|
+
dnsSocketInstance = null
|
|
2104
|
+
}).catch((err) => {
|
|
2105
|
+
console.error('DNS creation failed', err)
|
|
2106
|
+
showDnsError(err.message || 'Unable to create domain')
|
|
2107
|
+
})
|
|
2108
|
+
}
|
|
2109
|
+
const runDnsCreation = ({ dnsName, dnsPort }) => {
|
|
2110
|
+
Swal.fire({
|
|
2111
|
+
title: 'Creating domain',
|
|
2112
|
+
html: `<div class='dns-progress-modal'><div class='dns-modal-terminal-container'></div></div>`,
|
|
2113
|
+
showConfirmButton: false,
|
|
2114
|
+
allowOutsideClick: false,
|
|
2115
|
+
allowEscapeKey: false,
|
|
2116
|
+
customClass: {
|
|
2117
|
+
popup: 'dns-progress-popup'
|
|
2118
|
+
},
|
|
2119
|
+
didOpen: async () => {
|
|
2120
|
+
dnsModalActive = true
|
|
2121
|
+
try {
|
|
2122
|
+
const popup = Swal.getPopup()
|
|
2123
|
+
const container = popup.querySelector('.dns-modal-terminal-container')
|
|
2124
|
+
const { term, observer } = await createDnsTerminal(container)
|
|
2125
|
+
dnsResizeObserver = observer
|
|
2126
|
+
startDnsCreationSocket(term, dnsName, dnsPort)
|
|
2127
|
+
} catch (err) {
|
|
2128
|
+
console.error('Failed to initialize DNS terminal', err)
|
|
2129
|
+
showDnsError(err.message || 'Unable to start terminal')
|
|
2130
|
+
}
|
|
2131
|
+
},
|
|
2132
|
+
willClose: () => {
|
|
2133
|
+
if (dnsSocketInstance && typeof dnsSocketInstance.close === 'function') {
|
|
2134
|
+
try { dnsSocketInstance.close() } catch (_) {}
|
|
2135
|
+
dnsSocketInstance = null
|
|
2136
|
+
}
|
|
2137
|
+
if (dnsResizeObserver) {
|
|
2138
|
+
try { dnsResizeObserver.disconnect() } catch (_) {}
|
|
2139
|
+
dnsResizeObserver = null
|
|
2140
|
+
}
|
|
2141
|
+
dnsModalActive = false
|
|
2142
|
+
}
|
|
2143
|
+
})
|
|
2144
|
+
}
|
|
2145
|
+
document.addEventListener('click', async (event) => {
|
|
2146
|
+
if (!allowDnsCreation) return
|
|
2147
|
+
const trigger = event.target.closest('.get-dns-btn')
|
|
2148
|
+
if (!trigger) return
|
|
2149
|
+
event.preventDefault()
|
|
2150
|
+
const response = await promptForDnsMapping({
|
|
2151
|
+
port: trigger.getAttribute('data-port') || '',
|
|
2152
|
+
name: trigger.getAttribute('data-default-dns') || '',
|
|
2153
|
+
})
|
|
2154
|
+
if (response) {
|
|
2155
|
+
runDnsCreation(response)
|
|
2156
|
+
}
|
|
2157
|
+
})
|
|
2158
|
+
|
|
2159
|
+
let routeSearchList = []
|
|
2160
|
+
const routeSearchInput = () => document.querySelector(".net-toolbar input[type=search]")
|
|
2161
|
+
const rebuildNetRouteSearchIndex = () => {
|
|
2162
|
+
routeSearchList = []
|
|
2163
|
+
document.querySelectorAll(".line[data-index]").forEach((el) => {
|
|
2164
|
+
const titleEl = el.querySelector(".title span, .title")
|
|
2165
|
+
const routeEls = [...el.querySelectorAll(".net")]
|
|
2166
|
+
const routes = routeEls.map((node) => (node.textContent || '').trim()).filter(Boolean)
|
|
2167
|
+
const haystack = [
|
|
2168
|
+
el.getAttribute("data-name") || '',
|
|
2169
|
+
titleEl ? titleEl.textContent || '' : '',
|
|
2170
|
+
el.getAttribute("data-description") || '',
|
|
2171
|
+
...routes
|
|
2172
|
+
].join(' ').toLowerCase()
|
|
2173
|
+
routeSearchList.push({
|
|
2174
|
+
index: parseInt(el.getAttribute("data-index")),
|
|
2175
|
+
haystack
|
|
2176
|
+
})
|
|
2177
|
+
})
|
|
2178
|
+
filteredCount = routeSearchList.length
|
|
2179
|
+
filteredBtnsCount = 0
|
|
2180
|
+
}
|
|
2181
|
+
const normalizeSearchValue = (value) => {
|
|
2182
|
+
return String(value || '').toLowerCase().trim().replace(/\s+/g, ' ')
|
|
2183
|
+
}
|
|
2184
|
+
const searchRoutes = (items, value) => {
|
|
2185
|
+
const query = normalizeSearchValue(value)
|
|
2186
|
+
if (!query) {
|
|
2187
|
+
return items.map((item) => ({ item }))
|
|
2188
|
+
}
|
|
2189
|
+
const terms = query.split(' ').filter(Boolean)
|
|
2190
|
+
return items
|
|
2191
|
+
.filter((item) => {
|
|
2192
|
+
const haystack = normalizeSearchValue(item.haystack)
|
|
2193
|
+
return terms.every((term) => haystack.includes(term))
|
|
2194
|
+
})
|
|
2195
|
+
.map((item) => ({ item }))
|
|
2196
|
+
}
|
|
2197
|
+
let selectedIndex = -1
|
|
2198
|
+
let selectedBtnIndex = 0
|
|
2199
|
+
let filteredCount = 0
|
|
2200
|
+
let filteredBtnsCount = 0
|
|
2201
|
+
const renderSearch = () => {
|
|
2202
|
+
const target = routeSearchInput()
|
|
2203
|
+
if (!target) return
|
|
2204
|
+
const result = target.value.trim().length === 0
|
|
2205
|
+
? routeSearchList.map((item) => ({ item }))
|
|
2206
|
+
: searchRoutes(routeSearchList, target.value)
|
|
2207
|
+
document.querySelectorAll(".line[data-index]").forEach((el) => {
|
|
2208
|
+
el.classList.add("hidden")
|
|
2209
|
+
el.classList.remove("selected")
|
|
2210
|
+
})
|
|
2211
|
+
filteredCount = result.length
|
|
2212
|
+
result.forEach((selector, index) => {
|
|
2213
|
+
const el = document.querySelector(".line[data-index='" + selector.item.index + "']")
|
|
2214
|
+
if (!el) return
|
|
2215
|
+
el.classList.remove("hidden")
|
|
2216
|
+
const btns = el.querySelectorAll(".btns a")
|
|
2217
|
+
if (index === selectedIndex) {
|
|
2218
|
+
el.classList.add("selected")
|
|
2219
|
+
filteredBtnsCount = btns.length
|
|
2220
|
+
btns.forEach((btn, btnIndex) => {
|
|
2221
|
+
btn.classList.toggle("selected", selectedBtnIndex === btnIndex)
|
|
2222
|
+
})
|
|
2223
|
+
} else {
|
|
2224
|
+
btns.forEach((btn) => btn.classList.remove("selected"))
|
|
2225
|
+
}
|
|
2226
|
+
})
|
|
2227
|
+
}
|
|
2228
|
+
rebuildNetRouteSearchIndex()
|
|
2229
|
+
renderSearch()
|
|
2230
|
+
const netRouteInput = routeSearchInput()
|
|
2231
|
+
if (netRouteInput) {
|
|
2232
|
+
netRouteInput.addEventListener("input", (e) => {
|
|
2233
|
+
selectedIndex = 0
|
|
2234
|
+
selectedBtnIndex = 0
|
|
2235
|
+
renderSearch()
|
|
2236
|
+
const p = e.target.value && e.target.value.trim().length > 0
|
|
2237
|
+
? location.pathname + "?selected=" + encodeURIComponent(e.target.value)
|
|
2238
|
+
: location.pathname
|
|
2239
|
+
window.history.pushState({ path: p }, '', p)
|
|
2240
|
+
})
|
|
2241
|
+
const params = new URLSearchParams(location.search)
|
|
2242
|
+
const selected = params.get("selected")
|
|
2243
|
+
if (selected) {
|
|
2244
|
+
netRouteInput.value = selected
|
|
2245
|
+
netRouteInput.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }))
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
const routeListEl = document.querySelector(".net-route-list")
|
|
2249
|
+
const netRoutesEndpoint = routeListEl ? routeListEl.getAttribute("data-net-routes-endpoint") : ""
|
|
2250
|
+
let netRoutesFingerprint = null
|
|
2251
|
+
let netRoutesInFlight = false
|
|
2252
|
+
const netRoutesActive = () => {
|
|
2253
|
+
return routeListEl && routeListEl.getAttribute("data-net-routes-active") === "true"
|
|
2254
|
+
}
|
|
2255
|
+
const applyNetRouteFragment = (payload) => {
|
|
2256
|
+
const routesEl = document.querySelector(".net-route-list")
|
|
2257
|
+
const summaryEl = document.querySelector(".net-summary")
|
|
2258
|
+
if (!routesEl || !summaryEl) return
|
|
2259
|
+
const searchInput = routeSearchInput()
|
|
2260
|
+
const searchHadFocus = searchInput && document.activeElement === searchInput
|
|
2261
|
+
const scrollX = window.scrollX
|
|
2262
|
+
const scrollY = window.scrollY
|
|
2263
|
+
summaryEl.innerHTML = payload.summary_html || ''
|
|
2264
|
+
routesEl.innerHTML = payload.routes_html || ''
|
|
2265
|
+
selectedIndex = -1
|
|
2266
|
+
selectedBtnIndex = 0
|
|
2267
|
+
rebuildNetRouteSearchIndex()
|
|
2268
|
+
renderSearch()
|
|
2269
|
+
if (searchHadFocus && searchInput) {
|
|
2270
|
+
searchInput.focus()
|
|
2271
|
+
}
|
|
2272
|
+
window.scrollTo(scrollX, scrollY)
|
|
2273
|
+
}
|
|
2274
|
+
const refreshNetRouteFragment = async () => {
|
|
2275
|
+
if (!netRoutesEndpoint || !netRoutesActive() || dnsModalActive || netRoutesInFlight) return
|
|
2276
|
+
netRoutesInFlight = true
|
|
2277
|
+
try {
|
|
2278
|
+
const response = await fetch(netRoutesEndpoint, { cache: "no-store" })
|
|
2279
|
+
const payload = await response.json()
|
|
2280
|
+
if (!payload || !payload.success || !payload.fingerprint) {
|
|
2281
|
+
return
|
|
2282
|
+
}
|
|
2283
|
+
if (netRoutesFingerprint === null) {
|
|
2284
|
+
netRoutesFingerprint = payload.fingerprint
|
|
2285
|
+
return
|
|
2286
|
+
}
|
|
2287
|
+
if (payload.fingerprint !== netRoutesFingerprint) {
|
|
2288
|
+
netRoutesFingerprint = payload.fingerprint
|
|
2289
|
+
applyNetRouteFragment(payload)
|
|
2290
|
+
}
|
|
2291
|
+
} catch (error) {
|
|
2292
|
+
console.warn("net route refresh failed", error)
|
|
2293
|
+
} finally {
|
|
2294
|
+
netRoutesInFlight = false
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
if (netRoutesActive()) {
|
|
2298
|
+
refreshNetRouteFragment()
|
|
2299
|
+
setInterval(refreshNetRouteFragment, 2000)
|
|
2300
|
+
}
|
|
1508
2301
|
document.querySelector("#reset-label").addEventListener("click", async (e) => {
|
|
1509
2302
|
e.preventDefault()
|
|
1510
2303
|
e.stopPropagation()
|
|
@@ -1538,7 +2331,7 @@ document.querySelector("#reset-label").addEventListener("click", async (e) => {
|
|
|
1538
2331
|
clearInterval(i2)
|
|
1539
2332
|
if (res.peer_name) {
|
|
1540
2333
|
console.log("> 1")
|
|
1541
|
-
location.href = "/
|
|
2334
|
+
location.href = "/network"
|
|
1542
2335
|
} else {
|
|
1543
2336
|
console.log("> 2")
|
|
1544
2337
|
location.reload()
|
|
@@ -1702,7 +2495,7 @@ document.querySelector("main").addEventListener("click", async (e) => {
|
|
|
1702
2495
|
clearInterval(i3)
|
|
1703
2496
|
if (res.peer_name) {
|
|
1704
2497
|
console.log("> 1")
|
|
1705
|
-
location.href = "/
|
|
2498
|
+
location.href = "/network"
|
|
1706
2499
|
} else {
|
|
1707
2500
|
console.log("> 2")
|
|
1708
2501
|
location.reload()
|