pinokiod 3.40.0 → 3.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/kernel/api/browser/index.js +3 -1
  2. package/kernel/api/cloudflare/index.js +3 -3
  3. package/kernel/api/index.js +187 -51
  4. package/kernel/api/loading/index.js +15 -0
  5. package/kernel/api/process/index.js +7 -0
  6. package/kernel/api/shell/index.js +0 -2
  7. package/kernel/bin/browserless.js +22 -0
  8. package/kernel/bin/caddy.js +36 -4
  9. package/kernel/bin/index.js +4 -1
  10. package/kernel/bin/setup.js +38 -5
  11. package/kernel/connect/backend.js +110 -0
  12. package/kernel/connect/config.js +171 -0
  13. package/kernel/connect/index.js +18 -7
  14. package/kernel/connect/providers/huggingface/index.js +98 -0
  15. package/kernel/connect/providers/x/index.js +0 -1
  16. package/kernel/environment.js +91 -19
  17. package/kernel/git.js +46 -3
  18. package/kernel/index.js +119 -39
  19. package/kernel/peer.js +40 -5
  20. package/kernel/plugin.js +3 -2
  21. package/kernel/procs.js +27 -20
  22. package/kernel/prototype.js +30 -16
  23. package/kernel/router/common.js +1 -1
  24. package/kernel/router/connector.js +1 -3
  25. package/kernel/router/index.js +38 -4
  26. package/kernel/router/localhost_home_router.js +5 -1
  27. package/kernel/router/localhost_port_router.js +27 -1
  28. package/kernel/router/localhost_static_router.js +93 -0
  29. package/kernel/router/localhost_variable_router.js +14 -9
  30. package/kernel/router/peer_peer_router.js +3 -0
  31. package/kernel/router/peer_static_router.js +43 -0
  32. package/kernel/router/peer_variable_router.js +15 -14
  33. package/kernel/router/processor.js +26 -1
  34. package/kernel/router/rewriter.js +59 -0
  35. package/kernel/scripts/git/commit +11 -1
  36. package/kernel/shell.js +8 -3
  37. package/kernel/util.js +65 -6
  38. package/package.json +2 -1
  39. package/server/index.js +1048 -970
  40. package/server/public/common.js +382 -1
  41. package/server/public/fscreator.js +0 -1
  42. package/server/public/loading.js +17 -0
  43. package/server/public/notifyinput.js +0 -1
  44. package/server/public/opener.js +4 -2
  45. package/server/public/style.css +310 -11
  46. package/server/socket.js +7 -1
  47. package/server/views/app.ejs +1747 -351
  48. package/server/views/columns.ejs +338 -0
  49. package/server/views/connect/huggingface.ejs +353 -0
  50. package/server/views/connect/index.ejs +410 -0
  51. package/server/views/connect/x.ejs +43 -9
  52. package/server/views/connect.ejs +709 -49
  53. package/server/views/container.ejs +357 -0
  54. package/server/views/d.ejs +251 -62
  55. package/server/views/download.ejs +54 -10
  56. package/server/views/editor.ejs +11 -0
  57. package/server/views/explore.ejs +40 -15
  58. package/server/views/file_explorer.ejs +25 -246
  59. package/server/views/form.ejs +44 -1
  60. package/server/views/frame.ejs +39 -1
  61. package/server/views/github.ejs +48 -11
  62. package/server/views/help.ejs +48 -7
  63. package/server/views/index.ejs +119 -58
  64. package/server/views/index2.ejs +3 -4
  65. package/server/views/init/index.ejs +651 -197
  66. package/server/views/install.ejs +1 -1
  67. package/server/views/mini.ejs +47 -18
  68. package/server/views/net.ejs +199 -67
  69. package/server/views/network.ejs +229 -93
  70. package/server/views/network2.ejs +3 -4
  71. package/server/views/old_network.ejs +3 -3
  72. package/server/views/prototype/index.ejs +48 -11
  73. package/server/views/review.ejs +1005 -0
  74. package/server/views/rows.ejs +341 -0
  75. package/server/views/screenshots.ejs +1020 -0
  76. package/server/views/settings.ejs +160 -23
  77. package/server/views/setup.ejs +49 -7
  78. package/server/views/setup_home.ejs +43 -10
  79. package/server/views/shell.ejs +7 -1
  80. package/server/views/start.ejs +14 -9
  81. package/server/views/terminal.ejs +13 -2
  82. package/server/views/tools.ejs +1015 -0
@@ -22,12 +22,28 @@
22
22
  padding: 0;
23
23
  box-sizing: border-box;
24
24
  }
25
- #terminal {
26
- margin-top: 50px;
25
+ #terminal-container {
26
+ display: flex;
27
+ justify-content: center;
28
+ align-items: center;
29
+ position: fixed;
30
+ top:0;
31
+ right:0;
32
+ left:0;
33
+ bottom:0;
34
+ z-index: 100000000;
35
+ }
36
+ body.dark #terminal2 {
37
+ border: 4px solid rgba(255,255,255,0.1);
38
+ }
39
+ #terminal2 {
40
+ width: 80%;
41
+ height: 80%;
42
+ border: 4px solid rgba(0,0,0,0.1);
27
43
  }
28
44
  #create-button {
29
45
  width: 100%;
30
- background: royalblue;
46
+ background: royalblue !important;
31
47
  font-size: 16px;
32
48
  font-weight: bold;
33
49
  border: none;
@@ -36,9 +52,9 @@
36
52
  padding: 20px;
37
53
  flex-grow: 1;
38
54
  background: rgba(0,0,100,0.04);
39
- border-top-right-radius: 20px;
40
- border-bottom-right-radius: 20px;
41
- border-bottom-left-radius: 20px;
55
+ border-top-right-radius: 10px;
56
+ border-bottom-right-radius: 10px;
57
+ border-bottom-left-radius: 10px;
42
58
 
43
59
  }
44
60
  body.dark #bootstrap-form {
@@ -166,6 +182,15 @@ body.dark .progress-bar .step.active {
166
182
  display: flex;
167
183
  }
168
184
 
185
+ /* Section labels (non-clickable) */
186
+ .category-label {
187
+ display: block;
188
+ font-size: 14px;
189
+ font-weight: 700;
190
+ padding: 10px;
191
+ }
192
+ body.dark .category-label { color: rgba(255,255,255,0.8); }
193
+
169
194
  .tab-nav {
170
195
  flex-shrink: 0;
171
196
  /*
@@ -182,26 +207,28 @@ body.dark .tab-nav {
182
207
 
183
208
  */
184
209
  .tab-button h5 {
185
- font-size: 15px;
210
+ font-size: 12px;
186
211
  width: 100%;
187
212
  }
188
213
  .tab-button {
214
+ word-break: break-word;
189
215
  /*
190
216
  display: block;
191
217
  */
192
218
  display: flex;
193
219
  flex-direction: column;
194
- width: 220px !important;
220
+ width: 250px !important;
195
221
  gap: 5px;
196
- padding: 15px 20px;
222
+ padding: 8px 10px;
197
223
  background: none;
198
224
  border: none;
199
225
  cursor: pointer;
200
- font-size: 14px;
226
+ font-size: 12px;
201
227
  font-weight: 500;
202
228
  color: rgba(0,0,0,0.6);
203
229
  transition: all 0.3s ease;
204
230
  width: 100%;
231
+ margin-left: 10px;
205
232
  text-align: left;
206
233
  /*
207
234
 
@@ -234,8 +261,10 @@ body.dark .tab-button:hover {
234
261
  .tab-button.active {
235
262
  color: #000;
236
263
  background: rgba(0,0,100,0.04);
237
- border-top-left-radius: 50px;
238
- border-bottom-left-radius: 50px;
264
+ /*
265
+ border-top-left-radius: 10px;
266
+ border-bottom-left-radius: 10px;
267
+ */
239
268
  /*
240
269
  background: rgba(0,0,0,0.02);
241
270
  */
@@ -251,8 +280,8 @@ body.dark .tab-button.active {
251
280
  align-items: center;
252
281
  }
253
282
  .row i {
254
- font-size: 20px;
255
- padding-right: 15px;
283
+ font-size: 14px;
284
+ padding-right: 10px;
256
285
  }
257
286
 
258
287
  .tab-content {
@@ -400,11 +429,11 @@ body.dark .conditional-options {
400
429
  */
401
430
 
402
431
 
403
- body.dark textarea, body.dark input[type="url"], body.dark input[type="text"] {
432
+ body.dark textarea, body.dark input[type="url"], body.dark input[type="text"], body.dark input[type="number"] {
404
433
  background: rgba(255, 255, 255, 0.1);
405
434
  color: white;
406
435
  }
407
- textarea, input[type="url"], input[type="text"] {
436
+ textarea, input[type="url"], input[type="text"], input[type="number"] {
408
437
  width: 100%;
409
438
  padding: 10px;
410
439
  border: 2px solid rgba(0,0,0,0.1);
@@ -417,7 +446,7 @@ textarea {
417
446
  min-height: 80px;
418
447
  }
419
448
 
420
- textarea:focus, input[type="url"]:focus, input[type="text"]:focus {
449
+ textarea:focus, input[type="url"]:focus, input[type="text"]:focus, input[type="number"]:focus {
421
450
  outline: none;
422
451
  }
423
452
 
@@ -437,6 +466,7 @@ textarea:focus, input[type="url"]:focus, input[type="text"]:focus {
437
466
  margin-top: 2rem;
438
467
  }
439
468
 
469
+ /*
440
470
  .btn {
441
471
  padding: 0.75rem 1.5rem;
442
472
  border: 1px solid #000000;
@@ -444,6 +474,7 @@ textarea:focus, input[type="url"]:focus, input[type="text"]:focus {
444
474
  font-weight: 500;
445
475
  transition: all 0.3s ease;
446
476
  }
477
+ */
447
478
 
448
479
 
449
480
  .results {
@@ -560,6 +591,7 @@ aside {
560
591
  main {
561
592
  flex-grow: 1;
562
593
  min-height: 0;
594
+ display: flex;
563
595
  /*
564
596
  background: rgba(0,0,100,0.04);
565
597
  height: 100%;
@@ -727,6 +759,9 @@ body.dark #new-window {
727
759
  */
728
760
  display: flex;
729
761
  }
762
+ body.dark .btn {
763
+ background: rgba(255,255,255,0.05) !important;
764
+ }
730
765
  .app-info .btn {
731
766
  width: 100%;
732
767
  box-sizing: border-box;
@@ -896,11 +931,13 @@ body.dark .grid-btns .btn2 {
896
931
  .label i {
897
932
  margin-right: 10px;
898
933
  }
934
+ /*
899
935
  .tab i {
900
936
  margin-right: 10px;
901
937
  font-size: 12px;
902
938
  width: 12px;
903
939
  }
940
+ */
904
941
  .menu-item-image {
905
942
  width: 30px !important;
906
943
  height: 30px !important;
@@ -1063,6 +1100,7 @@ body.dark #new-tab {
1063
1100
  border-top: 1px solid rgba(255,255,255,0.04);
1064
1101
  */
1065
1102
  }
1103
+ /*
1066
1104
  body.dark .submenu {
1067
1105
  border-left: 1px solid rgba(255,255,255,0.3);
1068
1106
  }
@@ -1071,6 +1109,7 @@ body.dark .submenu {
1071
1109
  margin-left: 15px;
1072
1110
  box-sizing: border-box;
1073
1111
  }
1112
+ */
1074
1113
  #menu-mobile {
1075
1114
  display: none;
1076
1115
  }
@@ -1286,6 +1325,30 @@ body.dark .command-fields {
1286
1325
  */
1287
1326
  margin-bottom: 20px;
1288
1327
  }
1328
+ .address-section {
1329
+ display: grid;
1330
+ grid-template-columns: repeat(2, 1fr);
1331
+ gap: 10px;
1332
+ }
1333
+ #address-result {
1334
+ font-weight: bold;
1335
+ display: flex;
1336
+ align-items: center;
1337
+ justify-content: center;
1338
+ gap: 10px;
1339
+ font-size: 16px;
1340
+ padding: 10px;
1341
+ padding: 50px 0;
1342
+ background: rgba(0,0,0,0.04);
1343
+ margin-top: 20px;
1344
+ }
1345
+ #remote-address, #local-address {
1346
+ text-align: center;
1347
+ font-size: 20px;
1348
+ flex-grow: 1;
1349
+ padding: 10px;
1350
+ box-sizing: border-box;
1351
+ }
1289
1352
 
1290
1353
  .ace-editor {
1291
1354
  width: 100%;
@@ -1294,10 +1357,127 @@ body.dark .command-fields {
1294
1357
  border-radius: 4px;
1295
1358
  font-family: 'Courier New', monospace;
1296
1359
  }
1360
+ aside {
1361
+ width: 200px;
1362
+ display: block;
1363
+ flex-shrink: 0;
1364
+ }
1365
+ body.dark aside .tab:hover, aside .tab:hover {
1366
+ color: royalblue !important;
1367
+ opacity: 1;
1368
+ }
1369
+ aside .tab i {
1370
+ width: 20px;
1371
+ text-align: center;
1372
+ }
1373
+ body.dark aside .tab {
1374
+ color: white;
1375
+ }
1376
+ aside .tab.hidden {
1377
+ padding: 0;
1378
+ }
1379
+ aside .tab {
1380
+ display: flex;
1381
+ align-items: center;
1382
+ gap: 5px;
1383
+ color: black;
1384
+ text-decoration: none;
1385
+ padding: 10px;
1386
+ font-size: 12px;
1387
+ opacity: 0.5;
1388
+ border-left: 10px solid transparent;
1389
+ }
1390
+ body.dark aside .tab.selected {
1391
+
1392
+ color: white;
1393
+ /*
1394
+ border-left: 10px solid white;
1395
+ */
1396
+ }
1397
+ aside .tab.selected {
1398
+ /*
1399
+ border-left: 10px solid black;
1400
+ */
1401
+ font-weight: bold;
1402
+ opacity: 1;
1403
+ }
1297
1404
 
1298
1405
  body.dark .ace-editor {
1299
1406
  border: 1px solid rgba(255,255,255,0.1);
1300
1407
  }
1408
+ @media only screen and (max-width: 800px) {
1409
+ body {
1410
+ display: flex !important;
1411
+ flex-direction: row !important;
1412
+ }
1413
+ }
1414
+ @media only screen and (max-width: 600px) {
1415
+ aside {
1416
+ width: unset;
1417
+ flex-shrink: unset;
1418
+ }
1419
+ aside {
1420
+ padding: 0 10px;
1421
+ }
1422
+ aside .tab i {
1423
+ width: 100%;
1424
+ }
1425
+ aside .tab .caption {
1426
+ display: none;
1427
+ }
1428
+ aside .tab {
1429
+ margin: 0;
1430
+ padding: 10px;
1431
+ border-left: none;
1432
+ }
1433
+ aside .btn-tab {
1434
+ flex-direction: column;
1435
+ padding: 10px 0;
1436
+ }
1437
+ aside .btn-tab .btn {
1438
+ display: flex;
1439
+ justify-content: center;
1440
+ }
1441
+ aside .btn-tab .btn .caption {
1442
+ display: none;
1443
+ }
1444
+ header .flexible {
1445
+ min-width: unset;
1446
+ }
1447
+ .tab-button {
1448
+ width: unset !important;
1449
+ margin: 0;
1450
+ }
1451
+ .tab-button i {
1452
+ display: block;
1453
+ width: 100%;
1454
+ text-align: center;
1455
+ padding: 0;
1456
+ }
1457
+ .category-label {
1458
+ text-align: center;
1459
+ }
1460
+ .tab-nav {
1461
+ flex-shrink: unset;
1462
+ display: flex;
1463
+ flex-direction: column;
1464
+ align-items: stretch;
1465
+ }
1466
+ .tab-nav .row div {
1467
+ display: none;
1468
+ }
1469
+ }
1470
+ @media only screen and (max-width: 480px) {
1471
+ .btn2 {
1472
+ padding: 5px;
1473
+ font-size: 11px;
1474
+ }
1475
+ .nav-btns {
1476
+ flex-grow: 1;
1477
+ justify-content: center;
1478
+ padding: 0;
1479
+ }
1480
+ }
1301
1481
  </style>
1302
1482
  <link href="/app.css" rel="stylesheet"/>
1303
1483
  <link href="/xterm.min.css" rel="stylesheet" />
@@ -1318,6 +1498,8 @@ body.dark .ace-editor {
1318
1498
  <script src="/xterm-theme.js"></script>
1319
1499
  <script src="/xterm-addon-search.js"></script>
1320
1500
  <script src="/xterm-addon-search-bar.js"></script>
1501
+ <script src="/popper.min.js"></script>
1502
+ <script src="/tippy-bundle.umd.min.js"></script>
1321
1503
  <script src="/filepond-plugin-file-validate-type.min.js"></script>
1322
1504
  <script src="/filepond-plugin-image-exif-orientation.min.js"></script>
1323
1505
  <script src="/filepond-plugin-image-preview.min.js"></script>
@@ -1332,62 +1514,106 @@ body.dark .ace-editor {
1332
1514
  <header class='navheader grabbable'>
1333
1515
  <h1>
1334
1516
  <a class='home' href="/"><img class='icon' src="/pinokio-black.png"></a>
1335
- <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
1336
- <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
1337
- <button class='btn2' id='refresh-page'><div><i class="fa-solid fa-rotate-right"></i></div><div>Refresh</div></button>
1517
+ <button class='btn2' id='back' data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
1518
+ <button class='btn2' id='forward' data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
1519
+ <button class='btn2' id='refresh-page' data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
1520
+ <button class='btn2' id='screenshot' data-tippy-content="take a screenshot"><i class="fa-solid fa-camera"></i></button>
1338
1521
  <div class='flexible'></div>
1339
- <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
1340
- <div class='nav-btns'>
1341
- <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
1342
- <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
1343
- <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
1344
- <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
1345
- <button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
1522
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
1523
+ <div><i class="fa-solid fa-table-columns"></i></div>
1524
+ </a>
1525
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
1526
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
1527
+ </a>
1528
+ <div class="dropdown btn2">
1529
+ <button class='btn2' id='window-management'>
1530
+ <div><i class="fa-solid fa-plus"></i></div>
1531
+ </button>
1532
+ <div class="dropdown-content" id="dropdown-content">
1533
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
1534
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
1535
+ </button>
1536
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
1537
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
1538
+ </button>
1539
+ </div>
1540
+ </div>
1541
+ <!--
1542
+ <div class="dropdown btn2">
1543
+ <button class='btn2' id='window-management'>
1544
+ <div><i class="fa-regular fa-window-restore"></i></div>
1545
+ </button>
1546
+ <div class="dropdown-content" id="dropdown-content">
1547
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
1548
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
1549
+ </button>
1550
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
1551
+ <div><i class="fa-solid fa-table-columns"></i><div>split columns</div></div>
1552
+ </a>
1553
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
1554
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i><div>split rows</div></div>
1555
+ </a>
1556
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
1557
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
1558
+ </button>
1559
+ </div>
1346
1560
  </div>
1561
+ -->
1562
+ <button class='btn2 hidden' id='close-window' data-tippy-content='close this section'>
1563
+ <div><i class="fa-solid fa-xmark"></i></div>
1564
+ </button>
1347
1565
  </h1>
1348
1566
  </header>
1349
1567
  <main>
1350
1568
  <div class="container">
1351
- <!--
1352
- <h1>Build a Launcher</h1>
1353
- <h1><img class='logo2' src="/pinokio-black.png"/> Project Setup</h1>
1354
- -->
1355
1569
  <div class="tab-container">
1356
1570
  <div class="tab-nav">
1571
+ <div class='category-label'>Create a new project</div>
1572
+ <!-- Create category subtabs -->
1357
1573
  <button type="button" class="tab-button active" data-tab="launcher">
1358
1574
  <div class='row'>
1359
1575
  <i class="fa-solid fa-cloud-arrow-down"></i>
1360
- <div>
1361
- <h5>Git Launcher</h5>
1362
- <div>Launch any git-based open source project</div>
1363
- </div>
1576
+ <div><h5>1-Click Launcher</h5></div>
1364
1577
  </div>
1365
1578
  </button>
1366
1579
  <button type="button" class="tab-button" data-tab="cli">
1367
1580
  <div class='row'>
1368
1581
  <i class="fa-solid fa-terminal"></i>
1369
- <div>
1370
- <h5>CLI App Launcher</h5>
1371
- <div>Run any Terminal app in the browser.</div>
1372
- </div>
1582
+ <div><h5>Terminal App Launcher</h5></div>
1373
1583
  </div>
1374
1584
  </button>
1375
1585
  <button type="button" class="tab-button" data-tab="project">
1376
1586
  <div class='row'>
1377
1587
  <i class="fa-regular fa-pen-to-square"></i>
1378
- <div>
1379
- <h5>Empty Launcher</h5>
1380
- <div>Start a new project with a built-in 1-click launcher.</div>
1381
- </div>
1588
+ <div><h5>Create a New App</h5></div>
1589
+ </div>
1590
+ </button>
1591
+ <div class='category-label'>Import an existing project</div>
1592
+ <!-- Import category subtabs -->
1593
+ <button type="button" class="tab-button" data-tab="import-copy">
1594
+ <div class='row'>
1595
+ <i class="fa-solid fa-clone"></i>
1596
+ <div><h5>Import</h5></div>
1597
+ </div>
1598
+ </button>
1599
+ <button type="button" class="tab-button" data-tab="import-link">
1600
+ <div class='row'>
1601
+ <i class="fa-solid fa-link"></i>
1602
+ <div><h5>Link</h5></div>
1382
1603
  </div>
1383
1604
  </button>
1384
- <button type="button" class="tab-button" data-tab="quickcreate">
1605
+ <button type="button" class="tab-button" data-tab="import-clone">
1385
1606
  <div class='row'>
1386
- <i class="fa-solid fa-robot"></i>
1387
- <div>
1388
- <h5>AI Engineer</h5>
1389
- <div>Let AI build a launcher for anything.</div>
1390
- </div>
1607
+ <i class="fa-solid fa-download"></i>
1608
+ <div><h5>Download</h5></div>
1609
+ </div>
1610
+ </button>
1611
+ <div class='category-label'>Create a web domain</div>
1612
+ <!-- Domain category subtab -->
1613
+ <button type="button" class="tab-button" data-tab="dns">
1614
+ <div class='row'>
1615
+ <i class="fa-solid fa-link"></i>
1616
+ <div><h5>Web Domain</h5></div>
1391
1617
  </div>
1392
1618
  </button>
1393
1619
  </div>
@@ -1398,12 +1624,10 @@ body.dark .ace-editor {
1398
1624
  <!--
1399
1625
  <h2>Create a 1-Click Launcher</h2>
1400
1626
  -->
1401
- <!--
1402
1627
  <div class="header-description">
1403
1628
  <img class='logo2' src="/pinokio-black.png"/>
1404
- <div class='bubble'>Create a launcher for any open source project via git.</div>
1629
+ <div class='bubble'>Create a launcher for any open source project cloned from git.</div>
1405
1630
  </div>
1406
- -->
1407
1631
  <div class="git-url-input">
1408
1632
  <blockquote>Git URL</blockquote>
1409
1633
  <div class='explain'>Enter a git repository URL to clone from.</div>
@@ -1488,12 +1712,10 @@ body.dark .ace-editor {
1488
1712
 
1489
1713
  <!-- CLI Tab -->
1490
1714
  <div class="tab-content" id="cli-tab">
1491
- <!--
1492
1715
  <div class="header-description">
1493
1716
  <img class='logo2' src="/pinokio-black.png"/>
1494
- <div class='bubble'>Create a launcher for any terminal apps (npx, uvx, etc.)</div>
1717
+ <div class='bubble'>Run any terminal app in the browser.</div>
1495
1718
  </div>
1496
- -->
1497
1719
  <blockquote>Type</blockquote>
1498
1720
  <div class='explain'>Single-command apps vs. Installable apps.</div>
1499
1721
  <div class="project-options">
@@ -1537,32 +1759,66 @@ body.dark .ace-editor {
1537
1759
  </div>
1538
1760
  </div>
1539
1761
  </div>
1762
+ <!-- Import: Copy Tab -->
1763
+ <div class="tab-content" id="import-copy-tab">
1764
+ <div class="header-description">
1765
+ <img class='logo2' src="/pinokio-black.png"/>
1766
+ <div class='bubble'>Import an external folder into Pinokio</div>
1767
+ </div>
1768
+ <div class="git-url-input">
1769
+ <blockquote>Folder Path</blockquote>
1770
+ <input id='importCopy' type="text" name="importPath" placeholder="folder path">
1771
+ </div>
1772
+ <input type="hidden" name="importTypeCopy" value="copy" />
1773
+ </div>
1774
+
1775
+ <!-- Import: Link Tab -->
1776
+ <div class="tab-content" id="import-link-tab">
1777
+ <div class="header-description">
1778
+ <img class='logo2' src="/pinokio-black.png"/>
1779
+ <div class='bubble'>Create a symbolic link to an external folder</div>
1780
+ </div>
1781
+ <div class="git-url-input">
1782
+ <blockquote>Folder Path</blockquote>
1783
+ <input id='importLink' type="text" name="importPath" placeholder="folder path">
1784
+ </div>
1785
+ <input type="hidden" name="importTypeLink" value="link" />
1786
+ </div>
1787
+
1788
+ <!-- Import: Download (Clone) Tab -->
1789
+ <div class="tab-content" id="import-clone-tab">
1790
+ <div class="header-description">
1791
+ <img class='logo2' src="/pinokio-black.png"/>
1792
+ <div class='bubble'>Download a project by cloning its Git repository</div>
1793
+ </div>
1794
+ <div class="git-url-input">
1795
+ <blockquote>Git URL</blockquote>
1796
+ <input type="url" id="importDownload" name="importDownload" placeholder="example: https://github.com/username/repository.git">
1797
+ </div>
1798
+ </div>
1799
+
1800
+
1801
+
1540
1802
 
1541
1803
  <!-- Project Tab -->
1542
1804
  <div class="tab-content" id="project-tab">
1543
- <!--
1544
1805
  <div class="header-description">
1545
1806
  <img class='logo2' src="/pinokio-black.png"/>
1546
- <div class='bubble'>Create a new project with a built-in 1-click launcher</div>
1807
+ <div class='bubble'>Set up a new project with a built-in 1-click launcher.</div>
1547
1808
  </div>
1548
- -->
1809
+ <!--
1549
1810
  <blockquote>Type</blockquote>
1550
1811
  <div class='explain'>Set up a new 1-click launcher project.</div>
1812
+ -->
1551
1813
  <div class="project-options">
1552
1814
  <div class="radio-group">
1553
- <label>
1554
- <input type="radio" name="projectType" value="empty">
1555
- <img class='appicon' src="/asset/prototype/system/empty/icon.png"/>
1556
- <span>Empty Project</span>
1557
- </label>
1558
-
1815
+ <!--
1559
1816
  <label>
1560
1817
  <input type="radio" name="projectType" value="documentation">
1561
1818
  <img class='appicon' src="/asset/prototype/system/documentation/icon.png"/>
1562
1819
  <span>Documentation</span>
1563
1820
  </label>
1564
1821
 
1565
- <!-- Documentation Options -->
1566
1822
  <div id="documentation-options" class="conditional-options">
1567
1823
  <div class="radio-group">
1568
1824
  <label>
@@ -1584,16 +1840,33 @@ body.dark .ace-editor {
1584
1840
  </div>
1585
1841
  </div>
1586
1842
  </div>
1843
+ -->
1587
1844
  <label>
1588
1845
  <input type="radio" name="projectType" value="nodejs">
1589
1846
  <img class='appicon' src="/asset/prototype/system/nodejs/icon.png"/>
1590
- <span>Node.js</span>
1847
+ <span>Node.js starter</span>
1591
1848
  </label>
1592
1849
  <label>
1593
1850
  <input type="radio" name="projectType" value="python">
1594
1851
  <img class='appicon' src="/asset/prototype/system/python/icon.png"/>
1595
- <span>Python</span>
1852
+ <span>Python starter</span>
1596
1853
  </label>
1854
+ <label>
1855
+ <input type="radio" name="projectType" value="empty">
1856
+ <img class='appicon' src="/asset/prototype/system/empty/icon.png"/>
1857
+ <span>Empty starter</span>
1858
+ </label>
1859
+ <label>
1860
+ <input type="radio" name="projectType" value="blank">
1861
+ <img class='appicon' src="/asset/prototype/system/empty/icon.png"/>
1862
+ <span>Empty folder</span>
1863
+ </label>
1864
+ <label>
1865
+ <input type="radio" name="projectType" value="ai">
1866
+ <img class='appicon' src="/asset/prototype/system/cli/icon.png"/>
1867
+ <span>Initialize using AI</span>
1868
+ </label>
1869
+
1597
1870
  </div>
1598
1871
 
1599
1872
 
@@ -1621,50 +1894,54 @@ body.dark .ace-editor {
1621
1894
  </div>
1622
1895
  </div>
1623
1896
 
1624
- </div>
1625
-
1626
- <!-- Quick Create Tab -->
1627
- <div class="tab-content" id="quickcreate-tab">
1628
- <div class="ai-prompt-section">
1629
- <div class='ai-tabs'>
1630
- <% ai.forEach((item, i) => { %>
1631
- <div data-title="<%=item.title%>" class='ai-tab <%=i===0 ? "selected" : ""%>'><%=item.title%></div>
1632
- <% }) %>
1897
+ <!-- AI Options under New Project -->
1898
+ <div id="ai-options" class="conditional-options">
1899
+ <div class="header-description">
1900
+ <img class='logo2' src="/pinokio-black.png"/>
1901
+ <div class='bubble'>Use AI prompts to build a 1-click launcher</div>
1633
1902
  </div>
1634
- <div class='ai-container'>
1635
- <% ai.forEach((item, i) => { %>
1636
- <div data-meta="<%=JSON.stringify(item.data)%>" class='ai-tab-content <%=i===0 ? "selected" : ""%>' data-title="<%=item.title%>">
1637
- <h1><%=item.title%></h1>
1638
- <div class='md-desc'><%=item.description%></div>
1639
- <% Object.keys(item.meta).forEach((key) => { %>
1640
- <% if (!key.startsWith(".")) { %>
1641
- <input class='ai-input' type='text' placeholder='<%=item.meta[key]%>' name='<%=key%>'>
1903
+ <div class="ai-prompt-section">
1904
+ <div class='ai-tabs'>
1905
+ <% ai.forEach((item, i) => { %>
1906
+ <div data-title="<%=item.title%>" class='ai-tab <%=i===0 ? "selected" : ""%>'><%=item.title%></div>
1907
+ <% }) %>
1908
+ </div>
1909
+ <div class='ai-container'>
1910
+ <% ai.forEach((item, i) => { %>
1911
+ <div data-meta="<%=JSON.stringify(item.data)%>" class='ai-tab-content <%=i===0 ? "selected" : ""%>' data-title="<%=item.title%>">
1912
+ <h1><%=item.title%></h1>
1913
+ <div class='md-desc'><%=item.description%></div>
1914
+ <% Object.keys(item.meta).forEach((key) => { %>
1915
+ <% if (!key.startsWith(".")) { %>
1916
+ <input class='ai-input' type='text' placeholder='<%=item.meta[key]%>' name='<%=key%>'>
1917
+ <% } %>
1918
+ <% }) %>
1919
+ <% if (item.placeholder) { %>
1920
+ <textarea placeholder="<%=item.placeholder%>"><%=item.content%></textarea>
1921
+ <% } else { %>
1922
+ <textarea placeholder="<%=item.description%>"><%=item.content%></textarea>
1642
1923
  <% } %>
1643
- <% }) %>
1644
- <textarea placeholder="<%=item.description%>"><%=item.content%></textarea>
1645
- <pre class='hidden'><%=item.content%></pre>
1646
- </div>
1647
- <% }) %>
1648
- </div>
1649
- <!--
1650
- <blockquote>What do you want to build?</blockquote>
1651
- -->
1652
- <textarea class='hidden' id='aiPrompt' data-name="aiPrompt" data-placeholder="Describe what you want to build..." placeholder="What do you want to build?"></textarea>
1653
- <!--
1654
- <div id="aiPrompt" class="ace-editor" data-name="aiPrompt" data-placeholder="Describe what you want to build..." style="height: 150px;"></div>
1655
- -->
1656
-
1657
- <!--
1658
- <div class='explain'>
1659
- <div class='well'>
1660
- <ol>
1661
- <li>Describe what the project should do, and click "create".</li>
1662
- <li>This will be saved inside README.md.</li>
1663
- <li>AI coding tools will automatically read the README.md file to figure out what to build. You just need to tell them to "build".</li>
1664
- </ol>
1665
- </div>
1924
+ <pre class='hidden'><%=item.content%></pre>
1925
+ </div>
1926
+ <% }) %>
1666
1927
  </div>
1667
- -->
1928
+ <textarea class='hidden' id='aiPrompt' data-name="aiPrompt" data-placeholder="Describe what you want to build..." placeholder="What do you want to build?"></textarea>
1929
+ </div>
1930
+ </div>
1931
+
1932
+ </div>
1933
+
1934
+ <!-- Instant HTTPS Tab -->
1935
+ <div class="tab-content" id="dns-tab">
1936
+ <div class="header-description">
1937
+ <img class='logo2' src="/pinokio-black.png"/>
1938
+ <div class='bubble'>Instantly get domain names for any localhost server running on your machine. Just enter the port number and the domain you want.</div>
1939
+ </div>
1940
+ <div class='address-section'>
1941
+ <input type='text' placeholder="domain name" id='dns_name'>
1942
+ <input type='number' placeholder="port number" id='dns_port'>
1943
+ </div>
1944
+ <div id='address-result'>
1668
1945
  </div>
1669
1946
  </div>
1670
1947
 
@@ -1673,19 +1950,61 @@ body.dark .ace-editor {
1673
1950
  </div>
1674
1951
  </form>
1675
1952
  </div>
1676
- <div id='terminal'></div>
1677
1953
  <div id='end'></div>
1678
1954
  </div>
1955
+ <aside>
1956
+ <div class='btn-tab'>
1957
+ <a href="/init" class='btn'><i class="fa-solid fa-plus"></i><div class='caption'>Create</div></a>
1958
+ <a class='btn' id='explore' href="/?mode=explore"><i class="fa-solid fa-globe"></i><div class='caption'>Discover</div></a>
1959
+ </div>
1960
+ <a href="/" class='tab'><i class='fas fa-laptop-code'></i><div class='caption'>This machine</div></a>
1961
+ <a href="/network" class='tab'><i class="fa-solid fa-wifi"></i><div class='caption'>Local network</div></a>
1962
+ <% if (list.length > 0) { %>
1963
+ <% let brands = { win32: "windows", darwin: "apple", linux: "Linux" } %>
1964
+ <% list.forEach(({ host, name, platform, processes }, index) => { %>
1965
+ <a href="/net/<%=name%>" class='submenu tab'><i class="fa-brands fa-<%=brands[platform]%>"></i><div class='caption'><%=name%> (<%=current_host === host ? 'this machine' : host%>)</div></a>
1966
+ <% }) %>
1967
+ <% } %>
1968
+ <a href="/connect" class='tab'><i class="fa-solid fa-plug"></i><div class='caption'>Login</div></a>
1969
+ <a class='tab' href="<%=portal%>" target="_blank"><i class="fa-solid fa-question"></i><div class='caption'>Help</div></a>
1970
+ <a class='tab' id='genlog'><i class="fa-solid fa-laptop-code"></i><div class='caption'>Logs</div></a>
1971
+ <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><i class="fa-solid fa-download"></i><div class='caption'>Download logs</div></a>
1972
+ <a class='tab' href="/screenshots"><i class="fa-solid fa-camera"></i><div class='caption'>Screenshots</div></a>
1973
+ <a class='tab' href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Installed Tools</div></a>
1974
+ <a class='tab' href="/?mode=settings"><i class="fa-solid fa-gear"></i><div class='caption'>Settings</div></a>
1975
+ </aside>
1679
1976
  </main>
1977
+ <div id='terminal-container' class='hidden'>
1978
+ <div id='terminal2'></div>
1979
+ </div>
1680
1980
  <script>
1981
+ let shell_id
1982
+ let socket = new Socket()
1681
1983
  const resizeAITextarea = () => {
1682
1984
  let textarea = document.querySelector(".ai-container .ai-tab-content.selected textarea")
1683
1985
  textarea.style.height = "";
1684
1986
  textarea.style.height = textarea.scrollHeight + "px";
1685
1987
  }
1988
+ const refreshAddress = () => {
1989
+ let port = document.querySelector("#dns_port").value
1990
+ let name = document.querySelector("#dns_name").value
1991
+ document.querySelector("#address-result").innerHTML = `<div>https://${name || '[NAME]'}.localhost</div><i class="fa-solid fa-arrow-right"></i></div><div>http://localhost:${port || '[PORT]'}</div>`
1992
+ return {
1993
+ dns_name: name,
1994
+ dns_port: port,
1995
+ }
1996
+ }
1686
1997
  document.addEventListener('DOMContentLoaded', function() {
1687
- // Initialize tabs
1998
+ // Initialize tabs and categories
1688
1999
  initializeTabs();
2000
+ refreshAddress()
2001
+
2002
+ document.querySelector("#dns_port").addEventListener("input", (e) => {
2003
+ refreshAddress()
2004
+ })
2005
+ document.querySelector("#dns_name").addEventListener("input", (e) => {
2006
+ refreshAddress()
2007
+ })
1689
2008
 
1690
2009
  document.querySelector(".ai-tabs").addEventListener("click", (e) => {
1691
2010
  if (e.target.classList.contains("ai-tab")) {
@@ -1723,8 +2042,7 @@ document.addEventListener('DOMContentLoaded', function() {
1723
2042
  })
1724
2043
  })
1725
2044
 
1726
-
1727
- // Show launcher tab by default
2045
+ // Show default subtab
1728
2046
  showTab('launcher');
1729
2047
 
1730
2048
  // Add tab click handlers
@@ -1741,6 +2059,23 @@ document.addEventListener('DOMContentLoaded', function() {
1741
2059
  radio.addEventListener('change', handleProjectTypeChange);
1742
2060
  });
1743
2061
 
2062
+ // File pickers for import tabs
2063
+ document.querySelectorAll('#importCopy, #importLink').forEach((el) => {
2064
+ el.addEventListener('click', (e) => {
2065
+ let socket = new Socket()
2066
+ socket.run({
2067
+ method: "kernel.bin.filepicker",
2068
+ params: { title: "Select a path", type: "folder" }
2069
+ }, async (packet) => {
2070
+ if (packet.type === "result") {
2071
+ if (packet.data.paths.length > 0) {
2072
+ let p = packet.data.paths[0]
2073
+ e.target.value = p
2074
+ }
2075
+ }
2076
+ }).then((res) => { console.log("Ended", res) })
2077
+ })
2078
+ })
1744
2079
 
1745
2080
  const cliTypeRadios = document.querySelectorAll('input[name="cliType"]');
1746
2081
  cliTypeRadios.forEach(radio => {
@@ -1755,26 +2090,7 @@ document.addEventListener('DOMContentLoaded', function() {
1755
2090
  // Form submission
1756
2091
  document.getElementById('bootstrap-form').addEventListener('submit', handleSubmit);
1757
2092
 
1758
- document.querySelector("#local").addEventListener("click", (e) => {
1759
- let socket = new Socket()
1760
- socket.run({
1761
- method: "kernel.bin.filepicker",
1762
- params: {
1763
- cwd: "<%-JSON.stringify(cwd).slice(1, -1)%>",
1764
- title: "Select a path",
1765
- type: "folder",
1766
- }
1767
- }, async (packet) => {
1768
- if (packet.type === "result") {
1769
- if (packet.data.paths.length > 0) {
1770
- let p = packet.data.paths[0]
1771
- e.target.value = p
1772
- }
1773
- }
1774
- }).then((res) => {
1775
- console.log("Ended", res)
1776
- })
1777
- })
2093
+ // Removed old single import picker; using per-tab pickers above
1778
2094
 
1779
2095
  // Initialize Ace editors
1780
2096
  initializeAceEditors();
@@ -1829,6 +2145,8 @@ function initializeTabs() {
1829
2145
  // Initialize tab functionality
1830
2146
  }
1831
2147
 
2148
+ // removed showCategory; all subtabs are always visible under labels
2149
+
1832
2150
  function showTab(tabName) {
1833
2151
  // Hide all tab contents
1834
2152
  document.querySelectorAll('.tab-content').forEach(tab => {
@@ -1878,9 +2196,9 @@ function showTab(tabName) {
1878
2196
  clearCliInputs();
1879
2197
  }
1880
2198
 
1881
- if (tabName === "quickcreate") {
1882
- resizeAITextarea()
1883
- }
2199
+ // Resize AI textarea if AI option visible
2200
+ const aiSelected = document.querySelector('input[name="projectType"][value="ai"]')?.checked
2201
+ if (tabName === 'project' && aiSelected) resizeAITextarea()
1884
2202
 
1885
2203
  }
1886
2204
 
@@ -1891,12 +2209,13 @@ function hideAllConditionalOptions() {
1891
2209
  document.getElementById('installable-cli-options').classList.remove('show');
1892
2210
  document.getElementById('venv-option').classList.remove('show');
1893
2211
  document.getElementById('clone-command-fields').classList.remove('show');
1894
- document.getElementById('documentation-options').classList.remove('show');
1895
- document.getElementById('local-doc-options').classList.remove('show');
1896
- document.getElementById('remote-doc-options').classList.remove('show');
2212
+ const ai = document.getElementById('ai-options');
2213
+ if (ai) ai.classList.remove('show');
2214
+ // document.getElementById('documentation-options').classList.remove('show');
2215
+ // document.getElementById('local-doc-options').classList.remove('show');
2216
+ // document.getElementById('remote-doc-options').classList.remove('show');
1897
2217
  }
1898
2218
 
1899
-
1900
2219
  function handleProjectTypeChange(event) {
1901
2220
  const projectType = event.target.value;
1902
2221
  const activeTab = document.querySelector('.tab-content.active').id;
@@ -1933,8 +2252,14 @@ function handleProjectTypeChange(event) {
1933
2252
  } else {
1934
2253
  document.getElementById('python-options').classList.add('show');
1935
2254
  }
2255
+ } else if (projectType === 'ai') {
2256
+ const ai = document.getElementById('ai-options');
2257
+ if (ai) {
2258
+ ai.classList.add('show');
2259
+ setTimeout(resizeAITextarea, 0);
2260
+ }
1936
2261
  } else if (projectType === 'documentation') {
1937
- document.getElementById('documentation-options').classList.add('show');
2262
+ // document.getElementById('documentation-options').classList.add('show');
1938
2263
  }
1939
2264
  }
1940
2265
 
@@ -2061,8 +2386,8 @@ function handleDocTypeChange(event) {
2061
2386
  const docType = event.target.value;
2062
2387
 
2063
2388
  // Hide all documentation sub-options first
2064
- document.getElementById('local-doc-options').classList.remove('show');
2065
- document.getElementById('remote-doc-options').classList.remove('show');
2389
+ // document.getElementById('local-doc-options').classList.remove('show');
2390
+ // document.getElementById('remote-doc-options').classList.remove('show');
2066
2391
 
2067
2392
  // Clear documentation input fields
2068
2393
  document.querySelector('input[name="localDocPath"]').value = '';
@@ -2070,9 +2395,9 @@ function handleDocTypeChange(event) {
2070
2395
 
2071
2396
  // Show appropriate documentation options
2072
2397
  if (docType === 'local') {
2073
- document.getElementById('local-doc-options').classList.add('show');
2398
+ // document.getElementById('local-doc-options').classList.add('show');
2074
2399
  } else if (docType === 'remote') {
2075
- document.getElementById('remote-doc-options').classList.add('show');
2400
+ // document.getElementById('remote-doc-options').classList.add('show');
2076
2401
  }
2077
2402
  }
2078
2403
 
@@ -2080,8 +2405,11 @@ function validateForm() {
2080
2405
  const activeTab = document.querySelector('.tab-content.active').id;
2081
2406
  const isLauncher = activeTab === 'launcher-tab';
2082
2407
  const isCli = activeTab === 'cli-tab';
2083
- const isQuickCreate = activeTab === 'quickcreate-tab';
2084
- const gitUrl = document.getElementById('gitUrl').value.trim();
2408
+ const isDNS = activeTab === 'dns-tab';
2409
+ const isImportCopy = activeTab === 'import-copy-tab';
2410
+ const isImportLink = activeTab === 'import-link-tab';
2411
+ const isImportClone = activeTab === 'import-clone-tab';
2412
+ const isImport = isImportCopy || isImportLink || isImportClone;
2085
2413
 
2086
2414
  // Validate Git URL for launcher tab
2087
2415
  if (isLauncher) {
@@ -2126,13 +2454,23 @@ function validateForm() {
2126
2454
  }
2127
2455
  }
2128
2456
 
2129
- // Validate quick create tab
2130
- if (isQuickCreate) {
2131
- // No additional validation needed for quick create
2457
+ if (isImportCopy || isImportLink) {
2458
+ const importPath = (isImportCopy ? document.getElementById('importCopy') : document.getElementById('importLink')).value.trim();
2459
+ if (!importPath) {
2460
+ alert('Please enter a folder to import');
2461
+ return false;
2462
+ }
2463
+ }
2464
+ if (isImportClone) {
2465
+ const gitUrl = document.getElementById('importDownload').value.trim();
2466
+ if (!gitUrl) {
2467
+ alert('Please enter a Git repository URL.');
2468
+ return false;
2469
+ }
2132
2470
  }
2133
2471
 
2134
2472
  // Validate project tab
2135
- if (!isLauncher && !isCli && !isQuickCreate) {
2473
+ if (!isLauncher && !isCli && !isDNS && !isImport) {
2136
2474
  const projectType = document.querySelector('input[name="projectType"]:checked');
2137
2475
  if (!projectType) {
2138
2476
  alert('Please select a project type.');
@@ -2168,26 +2506,44 @@ function validateForm() {
2168
2506
 
2169
2507
  function handleSubmit(event) {
2170
2508
  event.preventDefault();
2509
+ const activeTab = document.querySelector('.tab-content.active').id;
2171
2510
 
2172
2511
  let aiTab = document.querySelector(".ai-tab-content.selected textarea")
2173
2512
  let aiMeta = null
2174
2513
  if (aiTab && aiTab.value) {
2175
2514
  document.querySelector("#aiPrompt").value = aiTab.value
2176
2515
  let ai_meta_str = aiTab.closest(".ai-tab-content").getAttribute("data-meta")
2177
- aiMeta = JSON.parse(ai_meta_str)
2516
+ if (ai_meta_str) {
2517
+ aiMeta = JSON.parse(ai_meta_str)
2518
+ }
2178
2519
  }
2179
2520
  console.log({ aiMeta })
2180
2521
 
2522
+ const isDNS = activeTab === 'dns-tab';
2523
+ let name
2524
+ let dnsPort
2525
+ if (isDNS) {
2526
+ let res = refreshAddress()
2527
+ name = res.dns_name
2528
+ dnsPort = res.dns_port
2529
+ if (!name) {
2530
+ return; // User cancelled or entered empty name
2531
+ }
2532
+ if (!dnsPort) {
2533
+ return; // User cancelled or entered empty name
2534
+ }
2535
+ }
2181
2536
 
2182
-
2183
2537
  if (!validateForm()) {
2184
2538
  return;
2185
2539
  }
2186
2540
 
2187
2541
  // Prompt for folder name
2188
- let name = prompt('Enter a folder name to create:');
2189
2542
  if (!name) {
2190
- return; // User cancelled or entered empty name
2543
+ name = prompt('Enter a folder name to create:');
2544
+ if (!name) {
2545
+ return; // User cancelled or entered empty name
2546
+ }
2191
2547
  }
2192
2548
 
2193
2549
  name = name.trim();
@@ -2202,11 +2558,26 @@ function handleSubmit(event) {
2202
2558
  }
2203
2559
 
2204
2560
  const formData = new FormData(event.target);
2205
- const activeTab = document.querySelector('.tab-content.active').id;
2206
2561
  const isLauncher = activeTab === 'launcher-tab';
2207
2562
  const isCli = activeTab === 'cli-tab';
2208
- const isQuickCreate = activeTab === 'quickcreate-tab';
2563
+ const isImportCopy = activeTab === 'import-copy-tab';
2564
+ const isImportLink = activeTab === 'import-link-tab';
2565
+ const isImportClone = activeTab === 'import-clone-tab';
2566
+ const isImport = isImportCopy || isImportLink || isImportClone;
2567
+ const isProject = activeTab === 'project-tab';
2209
2568
  const gitUrl = isLauncher ? formData.get('gitUrl').trim() : '';
2569
+ let importPath
2570
+ let importType
2571
+ if (isImportCopy) {
2572
+ importType = "copy"
2573
+ importPath = document.getElementById("importCopy").value || ""
2574
+ } else if (isImportLink) {
2575
+ importType = "link"
2576
+ importPath = document.getElementById("importLink").value || ""
2577
+ } else if (isImportClone) {
2578
+ importType = "download"
2579
+ importPath = document.getElementById("importDownload").value || ""
2580
+ }
2210
2581
 
2211
2582
  // Get values from Ace editors and clean them
2212
2583
  const cloneInstallCommandText = aceEditors['cloneInstallCommand'] ? aceEditors['cloneInstallCommand'].getValue().trim() : '';
@@ -2216,20 +2587,27 @@ function handleSubmit(event) {
2216
2587
 
2217
2588
  // Only get AI prompt text if Quick Create tab is active
2218
2589
  // const aiPromptText = isQuickCreate && aceEditors['aiPrompt'] ? aceEditors['aiPrompt'].getValue().trim() : '';
2219
- const aiPromptText = isQuickCreate && document.querySelector("#aiPrompt") ? document.querySelector("#aiPrompt").value.trim() : '';
2590
+ const isAI = isProject && formData.get('projectType') === 'ai';
2591
+ const aiPromptText = isAI && document.querySelector("#aiPrompt") ? document.querySelector("#aiPrompt").value.trim() : '';
2220
2592
 
2221
2593
  // Set startType and projectType based on active tab
2222
2594
  let startType, projectType;
2595
+
2223
2596
  if (isLauncher) {
2224
2597
  startType = 'clone';
2225
2598
  projectType = formData.get('projectType');
2226
2599
  } else if (isCli) {
2227
2600
  startType = 'new';
2228
2601
  projectType = 'cli';
2229
- } else if (isQuickCreate) {
2602
+ } else if (isAI) {
2230
2603
  startType = 'new';
2231
- //projectType = 'empty';
2232
2604
  projectType = 'ai';
2605
+ } else if (isDNS) {
2606
+ startType = 'new'
2607
+ projectType = 'dns'
2608
+ } else if (isImportCopy || isImportLink || isImportClone) {
2609
+ startType = 'new'
2610
+ projectType = 'import'
2233
2611
  } else {
2234
2612
  startType = 'new';
2235
2613
  projectType = formData.get('projectType');
@@ -2264,24 +2642,22 @@ function handleSubmit(event) {
2264
2642
  remoteDocUrl: formData.get('remoteDocUrl'),
2265
2643
  aiPrompt: aiPromptText,
2266
2644
  aiMeta,
2645
+ dnsPort,
2646
+ importType,
2647
+ importPath,
2267
2648
  };
2268
-
2269
2649
  displayResults(config);
2270
2650
  }
2271
-
2272
- async function displayResults(config) {
2273
- let shell_id
2274
- <% if (theme === "dark") { %>
2275
- let _theme = xtermTheme.FrontEndDelight
2276
- <% } else { %>
2277
- let _theme = xtermTheme.Tomorrow
2278
- <% } %>
2651
+ const createTerm = async (_theme) => {
2652
+ //const theme = Object.assign({ }, xtermTheme.Terminal_Basic, {
2279
2653
  const theme = Object.assign({ }, _theme, {
2280
2654
  selectionBackground: "red",
2281
2655
  selectionForeground: "white"
2282
2656
  })
2283
-
2284
- let term_config = {
2657
+ <% if (theme !== "dark") { %>
2658
+ theme.foreground = "black"
2659
+ <% } %>
2660
+ let config = {
2285
2661
  scrollback: 9999999,
2286
2662
  fontSize: 14,
2287
2663
  theme,
@@ -2290,35 +2666,44 @@ async function displayResults(config) {
2290
2666
  return res.json()
2291
2667
  })
2292
2668
  if (res && res.config) {
2293
- term_config = res.config
2669
+ config = res.config
2294
2670
  }
2295
- let socket = new Socket()
2296
- const term = new Terminal(term_config)
2297
- term.open(document.querySelector("#terminal"))
2298
- term.attachCustomKeyEventHandler(event => {
2299
- console.log({ event })
2300
- if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
2301
- const selection = term.getSelection();
2302
- if (selection) {
2303
- navigator.clipboard.writeText(selection);
2304
- return false;
2305
- }
2671
+ const term = new Terminal(config)
2672
+ term.open(document.querySelector("#terminal2"))
2673
+ document.querySelector("#terminal-container").classList.remove("hidden")
2674
+ /*
2675
+ document.querySelector("#terminal-container").addEventListener("click", (e) => {
2676
+ console.log("e.target", e.target)
2677
+ if (e.target.querySelector("#terminal2")) {
2678
+ console.log("frame")
2679
+ document.querySelector("#terminal-container").classList.add("hidden")
2680
+ } else {
2681
+ console.log("inside terminal")
2306
2682
  }
2307
- if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
2308
- navigator.clipboard.readText().then((text) => {
2309
- console.log({ text })
2310
- socket.run({
2311
- //key: "\x1b[200~" + text + "\x1b[201~",
2312
- key: text,
2313
- id: shell_id,
2314
- paste: true
2315
- })
2683
+ })
2684
+ */
2316
2685
 
2317
- })
2318
- return false
2686
+ term.attachCustomKeyEventHandler(event => {
2687
+ if (event.ctrlKey && event.key === 'c' && term.hasSelection()) {
2688
+ return false;
2689
+ }
2690
+ if (event.ctrlKey && event.key === 'v' && this._options.pasteWithCtrlV) {
2691
+ return false;
2319
2692
  }
2320
2693
  return true;
2321
2694
  });
2695
+
2696
+ fitAddon = new FitAddon.FitAddon();
2697
+ term.loadAddon(fitAddon);
2698
+ <% if (agent === "electron") { %>
2699
+ term.loadAddon(new WebLinksAddon.WebLinksAddon((event, uri) => {
2700
+ window.open(uri, "_blank")
2701
+ }))
2702
+ <% } else { %>
2703
+ term.loadAddon(new WebLinksAddon.WebLinksAddon());
2704
+ <% } %>
2705
+ fitAddon.fit();
2706
+
2322
2707
  term.onKey(({ key }) => {
2323
2708
  console.log({ key, shell_id })
2324
2709
  if (socket) {
@@ -2330,16 +2715,14 @@ async function displayResults(config) {
2330
2715
  }
2331
2716
  }
2332
2717
  });
2333
- const fitAddon = new FitAddon.FitAddon();
2334
- term.loadAddon(fitAddon);
2335
- fitAddon.fit();
2336
- term.focus()
2718
+
2337
2719
  let observer = new ResizeObserver(() => {
2338
2720
  fitAddon.fit()
2339
2721
  console.log(`Resized to ${term.cols}x${term.rows}`);
2340
2722
  // Trigger your custom handler here
2341
2723
  if (socket) {
2342
2724
  if (shell_id) {
2725
+ console.log("RESIZE", { shell_id, cols: term.cols, rows: term.rows })
2343
2726
  socket.run({
2344
2727
  resize: {
2345
2728
  cols: term.cols,
@@ -2351,14 +2734,85 @@ async function displayResults(config) {
2351
2734
  }
2352
2735
  });
2353
2736
  observer.observe(document.body)
2737
+
2738
+ term.focus()
2739
+ console.log(`Resized to ${term.cols}x${term.rows}`);
2740
+ return term
2741
+ }
2742
+
2743
+ async function displayResults(config) {
2744
+ // <% if (theme === "dark") { %>
2745
+ // let _theme = xtermTheme.FrontEndDelight
2746
+ // <% } else { %>
2747
+ // let _theme = xtermTheme.Tomorrow
2748
+ // <% } %>
2749
+ // const theme = Object.assign({ }, _theme, {
2750
+ // selectionBackground: "red",
2751
+ // selectionForeground: "white"
2752
+ // })
2753
+ //
2754
+ // let term_config = {
2755
+ // scrollback: 9999999,
2756
+ // fontSize: 14,
2757
+ // theme,
2758
+ // }
2759
+ // let res = await fetch("/xterm_config").then((res) => {
2760
+ // return res.json()
2761
+ // })
2762
+ // if (res && res.config) {
2763
+ // term_config = res.config
2764
+ // }
2765
+ // const term = new Terminal(term_config)
2766
+
2767
+
2768
+ <% if (theme === "dark") { %>
2769
+ let term = await createTerm(xtermTheme.FrontEndDelight)
2770
+ <% } else { %>
2771
+ let term = await createTerm(xtermTheme.Tomorrow)
2772
+ <% } %>
2773
+
2774
+ // term.open(document.querySelector("#terminal2"))
2775
+ // term.attachCustomKeyEventHandler(event => {
2776
+ // console.log({ event })
2777
+ // if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
2778
+ // const selection = term.getSelection();
2779
+ // if (selection) {
2780
+ // navigator.clipboard.writeText(selection);
2781
+ // return false;
2782
+ // }
2783
+ // }
2784
+ // if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
2785
+ // navigator.clipboard.readText().then((text) => {
2786
+ // console.log({ text })
2787
+ // socket.run({
2788
+ // //key: "\x1b[200~" + text + "\x1b[201~",
2789
+ // key: text,
2790
+ // id: shell_id,
2791
+ // paste: true
2792
+ // })
2793
+ //
2794
+ // })
2795
+ // return false
2796
+ // }
2797
+ // return true;
2798
+ // });
2799
+ // const fitAddon = new FitAddon.FitAddon();
2800
+ // term.loadAddon(fitAddon);
2801
+ // fitAddon.fit();
2802
+ // term.focus()
2354
2803
  const write = (text) => {
2355
2804
  if (text !== "\u0007") {
2356
2805
  term.write(text)
2357
2806
  }
2358
2807
  }
2359
2808
  socket.run({
2809
+ id: "kernel.proto.create",
2360
2810
  method: "kernel.proto.create",
2361
2811
  cwd: "<%-JSON.stringify(cwd).slice(1, -1)%>",
2812
+ client: {
2813
+ cols: term.cols,
2814
+ rows: term.rows,
2815
+ },
2362
2816
  // name: "<%=name%>",
2363
2817
  params: config
2364
2818
  }, async (packet) => {