pinokiod 3.9.28 → 3.9.30

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.
@@ -1,9 +1,8 @@
1
1
  <html>
2
2
  <head>
3
- <title>Pinokio</title>
3
+ <title><%=config.title%></title>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
6
- <link href="/xterm.min.css" rel="stylesheet" />
7
6
  <link href="/css/fontawesome.min.css" rel="stylesheet">
8
7
  <link href="/css/solid.min.css" rel="stylesheet">
9
8
  <link href="/css/regular.min.css" rel="stylesheet">
@@ -18,191 +17,6 @@
18
17
  <link href="/electron.css" rel="stylesheet"/>
19
18
  <% } %>
20
19
  <style>
21
- .line2 {
22
- display: flex;
23
- align-items: center;
24
- cursor: pointer;
25
- background: rgba(0,0,100,0.04);
26
- }
27
- .line2 a {
28
- text-decoration: none;
29
- color: black;
30
- }
31
- .status {
32
- padding: 10px;
33
- margin: 10px;
34
- border-radius: 10px;
35
- }
36
- .status.offline {
37
- background: silver;
38
- }
39
- .status.online {
40
- background: yellowgreen;
41
- }
42
- .switch {
43
- padding: 10px;
44
- margin: 10px 0;
45
- }
46
- .switch[data-online=true] {
47
- color: yellowgreen;
48
- }
49
- .button {
50
- padding: 10px;
51
- }
52
- .on, .off {
53
- display: flex;
54
- align-items: center;
55
- }
56
- header .runner {
57
- padding: 10px 0 0;
58
- }
59
- .proxy {
60
- padding: 0 10px;
61
- text-align: right;
62
- }
63
- .proxy a {
64
- text-decoration: none;
65
- display: inline-block;
66
- padding: 5px 10px;
67
- border-radius: 2px;
68
- background: black;
69
- color: white;
70
- }
71
- .browser-options-btns {
72
- display: flex;
73
- padding-top: 5px;
74
- }
75
- .browser-options-row {
76
- /*
77
- border-top: 1px solid rgba(0,0,0,0.1);
78
- border-bottom: 1px solid rgba(0,0,0,0.1);
79
- */
80
- display: flex;
81
- padding: 10px;
82
- align-items: flex-start;
83
- }
84
- .browser-options-row h3 {
85
- margin: 0;
86
- font-size: 14px;
87
- margin-bottom: 3px;
88
- }
89
- .browser-options-row .btn.disabled {
90
- opacity: 0.7;
91
- }
92
- .browser-options-row .btn {
93
- font-weight: bold;
94
- width: 100px;
95
- text-align: center;
96
- display: block;
97
- margin-right: 10px;
98
- flex-shrink: 0;
99
- padding: 5px;
100
- }
101
- .badge {
102
- font-size: 12px;
103
- padding: 5px 10px;
104
- background: rgba(0,0,100,0.1);
105
- border-radius: 5px;
106
- display: inline-block;
107
- margin-top: 5px;
108
- }
109
- a.badge {
110
- color: royalblue;
111
- text-decoration: none;
112
- cursor: pointer;
113
- }
114
- .badge a {
115
- color: royalblue;
116
- text-decoration: none;
117
- }
118
- body.dark .btn {
119
- color: white;
120
- border: 1px solid rgba(255,255,255,0.3);
121
- }
122
- body.dark .badge {
123
- background: rgba(255,255,255,0.1);
124
- }
125
- .folder-templates {
126
- background: rgba(0,0,100, 0.02);
127
- }
128
- .folder-templates .folder {
129
- text-decoration: none;
130
- color: rgba(0,0,0,0.9);
131
- font-size: 12px;
132
- padding: 10px;
133
- display: flex;
134
- align-items: center;
135
- }
136
- .folder-templates .folder .title {
137
- font-weight: bold;
138
- font-size: 15px;
139
- text-transform: capitalize;
140
- }
141
- .folder-templates .folder img {
142
- width: 30px;
143
- height: 30px;
144
- margin-right: 10px;
145
- border-radius: 10px;
146
- }
147
- body.dark .prototype-header {
148
- color: white;
149
- }
150
- .prototype-header .btn {
151
- border: 1px solid rgba(255,255,255,0.3);
152
- }
153
- .prototype-header {
154
- align-items: center;
155
- display: flex;
156
- padding: 40px 20px;
157
- }
158
- .prototype-header h1 {
159
- letter-spacing: -1px;
160
- }
161
- .prototype-header .desc {
162
- color: silver;
163
- }
164
- /*
165
- body.dark .browser-options-row {
166
- border-top: 1px solid rgba(255,255,255,0.05);
167
- border-bottom: 1px solid rgba(255,255,255,0.05);
168
- }
169
- */
170
- .meta {
171
- display: flex;
172
- align-items: flex-start;
173
- padding: 20px;
174
- }
175
- .meta-content {
176
- padding: 10px;
177
- }
178
- .meta-content h2 {
179
- margin: 0;
180
- font-size: 30px;
181
- }
182
- .meta-path {
183
- padding: 10px 0;
184
- opacity: 0.7;
185
- }
186
- .meta img {
187
- width: 100px;
188
- }
189
- @media only screen and (max-width: 480px) {
190
- .btn2 {
191
- padding: 5px;
192
- font-size: 11px;
193
- }
194
- .nav-btns {
195
- flex-grow: 1;
196
- justify-content: center;
197
- padding: 0;
198
- }
199
- }
200
-
201
-
202
-
203
-
204
-
205
-
206
20
  .online {
207
21
  width: 10px;
208
22
  height: 10px;
@@ -292,6 +106,11 @@ main iframe {
292
106
  height: 100%;
293
107
  border: none;
294
108
  }
109
+ /*
110
+ .header-item.indent {
111
+ padding-left: 23px !important;
112
+ }
113
+ */
295
114
  .header-item {
296
115
  /*
297
116
  display: block;
@@ -308,7 +127,6 @@ main iframe {
308
127
  .header-item .btn2 {
309
128
  margin: 0;
310
129
  border: none;
311
- padding: 5px;
312
130
  /*
313
131
  color: var(--light-color);
314
132
  */
@@ -347,7 +165,7 @@ body.dark .header-item.btn {
347
165
  color: var(--dark-color);
348
166
  }
349
167
  .header-item.btn {
350
- padding: 10px;
168
+ padding: 8px;
351
169
  background: none !important;
352
170
  color: var(--light-color);
353
171
  }
@@ -378,6 +196,9 @@ body.dark #new-window {
378
196
  background: rgba(255,255,255,0.04);
379
197
  }
380
198
  */
199
+ .app-info-card img {
200
+ padding: 10px;
201
+ }
381
202
  .app-info {
382
203
 
383
204
  /*
@@ -396,12 +217,6 @@ body.dark #new-window {
396
217
  grid-template-columns: repeat(4, 1fr);
397
218
  gap: 0px;
398
219
  }
399
- .header-btns {
400
- padding: 0 !important;
401
- }
402
- body.dark .header-btns {
403
- border-top: 1px solid rgba(255,255,255,0.04);
404
- }
405
220
  .app-info-edit {
406
221
  text-decoration: none;
407
222
  color: black;
@@ -435,13 +250,45 @@ body.dark .header-btns {
435
250
  */
436
251
  }
437
252
  .app-info-card {
253
+ /*
438
254
  padding: 10px !important;
255
+ */
256
+ display: flex;
439
257
  }
440
258
  .app-info .btn {
441
259
  width: 100%;
260
+ box-sizing: border-box;
261
+ }
262
+ body.dark .app-info .mode-section {
263
+ background: rgba(0,0,0,0.4);
264
+ }
265
+ .app-info .mode-section {
266
+ background: rgba(0,0,0,0.06);
267
+ padding: 20px;
268
+ box-sizing: border-box;
269
+ width: 100%;
270
+ text-align: left;
271
+ }
272
+ .app-info .mode-section .desc {
273
+ font-weight: normal;
274
+ opacity: 0.7;
275
+ }
276
+ .app-info .mode-section h3 {
277
+ font-size: 18px;
278
+ line-height: 26px;
279
+ margin: 0;
280
+ }
281
+ body.dark .app-info .mode-section .btn {
282
+ border: 1px solid rgba(255,255,255,0.1);
283
+ }
284
+ .app-info .mode-section .btn {
285
+ margin-top: 10px;
286
+ text-align: center;
442
287
  }
443
288
  .app-info-card > * {
289
+ /*
444
290
  padding: 5px;
291
+ */
445
292
  color: var(--light-color);
446
293
  text-decoration: none;
447
294
  box-sizing: border-box;
@@ -449,7 +296,346 @@ body.dark .header-btns {
449
296
  body.dark .app-info-card > * {
450
297
  color: var(--dark-color);
451
298
  }
299
+ .app-info-title {
300
+ padding: 15px 0;
301
+ }
302
+ .footer {
303
+ /*
304
+ display: flex;
305
+ */
306
+ display: grid;
307
+ grid-template-columns: repeat(4, 1fr);
308
+ }
309
+ .footer > * {
310
+ padding: 10px !important;
311
+ display: block;
312
+ box-sizing: border-box;
313
+ font-size: 12px !important;
314
+ text-align: center;
315
+ }
316
+ .footer i {
317
+ margin: 0 !important;
318
+ padding: 5px;
319
+ }
320
+ .footer a {
321
+ font-size: 14px;
322
+ }
323
+ body .footer {
324
+ border-bottom: 1px solid rgba(0,0,0,0.04);
325
+ }
326
+ body.dark .footer {
327
+ border-bottom: 1px solid rgba(255,255,255,0.04);
328
+ }
329
+ body.dark .header-item.cursor {
330
+ background: rgba(0,0,0, 0.2) !important;
331
+ }
332
+ .header-item.cursor {
333
+ background: rgba(0,0,100, 0.03) !important;
334
+ /*
335
+ border-left: 5px solid gold;
336
+ */
337
+ }
338
+ .frame-link {
339
+ cursor: pointer;
340
+ }
341
+ body .frame-link.selected {
342
+ /*
343
+ border-right: 10px solid gold;
344
+ */
345
+ background: black !important;
346
+ color: white;
347
+ /*
348
+ background: rgba(0,0,100,0.06) !important;
349
+ */
350
+ }
351
+ .frame-link.selected .del {
352
+ color: white;
353
+ }
354
+ body.dark .frame-link.selected {
355
+ background: rgba(0,0,0,0.7) !important;
356
+ }
357
+ .frame-link .loader {
358
+ /*
359
+ padding: 5px;
360
+ */
361
+ }
362
+ .loader .btn:hover {
363
+ color: royalblue;
364
+ border-color: royalblue;
365
+ }
366
+ .loader .btn {
367
+ padding: 4px 8px;
368
+ border: 1px solid rgba(255,255,255,0.3);
369
+ }
370
+ .frame-link .tab {
371
+ flex-grow: 1;
372
+ /*
373
+ width: 100%;
374
+ */
375
+ word-break: break-word;
376
+ /*
377
+ margin-right: 10px;
378
+ */
379
+ }
380
+ .frame-link .tab .flexible {
381
+ min-width: 5px;
382
+ }
383
+ .frame-link .tab .display {
384
+ word-break: break-word;
385
+ }
386
+ body.dark .grid-btns {
387
+ border-top: 1px solid rgba(255,255,255,0.04);
388
+ border-bottom: 1px solid rgba(255,255,255,0.04);
389
+ }
390
+ .grid-btns {
391
+ border-top: 1px solid rgba(0,0,0,0.04);
392
+ border-bottom: 1px solid rgba(0,0,0,0.04);
393
+ display: grid;
394
+ grid-template-columns: repeat(2, 1fr);
395
+ grid-template-rows: repeat(2, 1fr);
396
+ /*
397
+ grid-template-columns: repeat(3, 1fr);
398
+ grid-template-rows: repeat(1, 1fr);
399
+ */
400
+ box-sizing: border-box;
401
+ }
402
+ .grid-btns .btn2 {
403
+ padding: 10px 10px;
404
+ color: var(--light-color);
405
+ border-radius: 0;
406
+ cursor: pointer;
407
+ font-size: 11px;
408
+ }
409
+ body.dark .grid-btns .btn2 {
410
+ /*
411
+ color: white;
412
+ */
413
+ color: var(--dark-btn-color);
414
+ }
415
+ .footer {
416
+ width: 100%;
417
+ }
418
+ .tab {
419
+ flex-grow: 1;
420
+ display: flex;
421
+ align-items: center;
422
+ padding-right: 10px;
423
+ }
424
+ .label i {
425
+ margin-right: 10px;
426
+ }
427
+ .tab i {
428
+ margin-right: 10px;
429
+ font-size: 12px;
430
+ width: 12px;
431
+ }
432
+ .menu-item-image {
433
+ width: 30px !important;
434
+ height: 30px !important;
435
+ padding: 5px;
436
+ border-radius: 40px;
437
+ margin-right: 10px;
438
+ background: white;
439
+ }
440
+ .temp-menu {
441
+ /*
442
+ border-top: 1px solid rgba(0,0,0,0.1);
443
+ */
444
+ }
445
+ .temp-menu .del i {
446
+ margin: 0;
447
+ }
448
+ #delete {
449
+ color: brown;
450
+ }
451
+ body .footer {
452
+ }
453
+ body.dark nav {
454
+ /*
455
+ border-bottom: 1px solid rgba(255,255,255,0.04);
456
+ */
457
+ border-top: 1px solid rgba(255,255,255,0.04);
458
+ }
459
+ /*
460
+ .container:hover nav {
461
+ display: block;
462
+ }
463
+ */
464
+ nav {
465
+ /*
466
+ overflow: hidden;
467
+ max-height: 0;
468
+ transition: max-height 0.4s ease-out;
469
+ display: none;
470
+ */
471
+
472
+ background: white;
473
+ /*
474
+ position: absolute;
475
+ bottom: 0;
476
+ left: 0;
477
+ right: 0;
478
+ */
479
+ /*
480
+ padding: 5px;
481
+ */
482
+ padding: 0;
483
+ cursor: grab !important;
484
+ -webkit-app-region: drag !important;
485
+ /*
486
+ border-bottom: 1px solid rgba(0,0,0,0.04);
487
+ */
488
+ border-top: 1px solid rgba(0,0,0,0.04);
489
+ box-sizing: border-box;
490
+ }
491
+ body.dark nav .btn2 {
492
+ border-right: 1px solid rgba(255,255,255,0.04);
493
+ }
494
+ nav .btn2 {
495
+ padding: 15px;
496
+ font-size: 12px;
497
+ /*
498
+ width: 70px;
499
+ */
500
+ box-sizing: border-box;
501
+ border-right: 1px solid rgba(0,0,0,0.04);
502
+ border-radius: 0;
503
+ text-transform: lowercase;
504
+ flex-shrink: 0;
505
+
506
+ display: flex;
507
+ /*
508
+ flex-direction: column;
509
+ */
510
+ justify-content: center;
511
+ align-items: center;
512
+ }
513
+ nav .logo {
514
+ font-size: 14px;
515
+ padding: 10px;
516
+ }
517
+ .error-message .btn i {
518
+ margin-right: 10px;
519
+ }
520
+ .error-message .btn {
521
+ margin-top: 20px;
522
+ align-items: center;
523
+ display: flex;
524
+ letter-spacing: 0px;
525
+ background: black;
526
+ color: yellow;
527
+ font-size: 20px;
528
+ padding: 10px 30px;
529
+ }
530
+ .error-message {
531
+ display: flex;
532
+ flex-direction: column;
533
+ justify-content: center;
534
+ align-items: center;
535
+ font-weight: bold;
536
+ background: yellow !important;
537
+ flex-grow: 1;
538
+ padding: 0 50px;
539
+ font-size: 40px;
540
+ line-height: 40px;
541
+ color: black;
542
+ letter-spacing: -2px;
543
+ }
544
+ body.dark #location {
545
+ color: white;
546
+ background: rgba(255,255,255,0.03);
547
+ }
548
+ #location {
549
+ background: rgba(0,0,0,0.04);
550
+ padding: 10px;
551
+ margin: 0 10px;
552
+ font-size: 12px;
553
+ font-family: verdana;
554
+ box-sizing: border-box;
555
+ flex-grow: 1;
556
+ border: none;
557
+ outline: none;
558
+ }
559
+ .bar {
560
+ -webkit-app-region: no-drag !important;
561
+ cursor: auto;
562
+ display: flex;
563
+ align-items: stretch;
564
+ width: 100%;
565
+ flex-wrap: wrap;
566
+ }
567
+ body.dark .header-item.cursor.header-top {
568
+ background: none !important;
569
+ }
570
+ .header-item.cursor.header-top {
571
+ background: none !important;
572
+ }
573
+ .header-item.header-top {
574
+ display: block;
575
+ }
576
+ #new-tab:hover {
577
+ background: rgba(0,0,0,0.06) !important;
578
+ color: rgb(200,0,0) !important;
579
+ }
580
+
581
+ #new-tab {
582
+ cursor: pointer !important;
583
+ justify-content: center;
584
+ /*
585
+ border-top: 1px solid rgba(0,0,0,0.04);
586
+ */
587
+ }
588
+
589
+ body.dark #new-tab {
590
+ /*
591
+ border-top: 1px solid rgba(255,255,255,0.04);
592
+ */
593
+ }
594
+ body.dark .submenu {
595
+ border-left: 1px solid rgba(255,255,255,0.3);
596
+ }
597
+ .submenu {
598
+ border-left: 1px solid black;
599
+ margin-left: 15px;
600
+ box-sizing: border-box;
601
+ }
602
+ #menu-mobile {
603
+ display: none;
604
+ }
605
+ .tabmenu .label {
606
+ padding: 10px;
607
+ border-bottom: 1px solid rgba(255,255,255,0.04);
608
+ text-transform: uppercase;
609
+ font-size: 12px;
610
+ font-weight: bold;
611
+ }
612
+ .disk-usage {
613
+ flex-grow: 1;
614
+ text-align: right;
615
+ font-weight: bold;
616
+ }
617
+ .disk-usage i {
618
+ margin-right: 5px;
619
+ font-size: 16px !important;
620
+ }
621
+
622
+
623
+ .custom-label {
624
+ position: absolute;
625
+ top: 0;
626
+ left: 0;
627
+ right: 0;
628
+ bottom: 0;
629
+ display: flex;
630
+ align-items: center;
631
+ justify-content: center;
632
+ pointer-events: none; /* Let clicks fall through to FilePond */
633
+ font-size: 14px;
634
+ color: rgba(0, 0, 0, 0.5);
635
+ z-index: 5;
636
+ }
452
637
  .appcanvas {
638
+ overflow: auto;
453
639
  display: flex;
454
640
  flex-grow: 1;
455
641
  border-top: 1px solid rgba(0, 0,0 ,0.04);
@@ -506,31 +692,18 @@ body.dark .appcanvas {
506
692
  display: flex;
507
693
  }
508
694
  }
509
- body.dark .header-item.cursor.header-top {
510
- background: none !important;
511
- }
512
- .header-item.cursor.header-top {
513
- background: none !important;
514
- }
515
- .header-item.header-top {
516
- display: block;
517
- }
518
695
  </style>
696
+ <link href="/app.css" rel="stylesheet"/>
519
697
  <script src="/hotkeys.min.js"></script>
520
698
  <script src="/sweetalert2.js"></script>
521
- <script src="/noty.js"></script>
522
- <script src="/notyq.js"></script>
523
- <script src="/xterm.js"></script>
524
- <script src="/xterm-addon-fit.js"></script>
525
- <script src="/xterm-addon-web-links.js"></script>
526
- <script src="/xterm-theme.js"></script>
527
- <script src="/Socket.js"></script>
528
- <script src="/install.js"></script>
699
+ <script src="/nav.js"></script>
529
700
  <script src="/common.js"></script>
530
701
  <script src="/opener.js"></script>
531
- <script src="/nav.js"></script>
532
- <script src="/fuse.js"></script>
533
702
  <script src="/report.js"></script>
703
+ <script src="/normalize.js"></script>
704
+ <script src="/Socket.js"></script>
705
+ <script src="/noty.js"></script>
706
+ <script src="/notyq.js"></script>
534
707
  <script src="/filepond-plugin-file-validate-type.min.js"></script>
535
708
  <script src="/filepond-plugin-image-exif-orientation.min.js"></script>
536
709
  <script src="/filepond-plugin-image-preview.min.js"></script>
@@ -539,108 +712,592 @@ body.dark .header-item.cursor.header-top {
539
712
  <script src="/filepond-plugin-image-resize.min.js"></script>
540
713
  <script src="/filepond-plugin-image-transform.min.js"></script>
541
714
  <script src="/filepond.min.js"></script>
542
- <script src="/commander.js"></script>
715
+ <script src="/fseditor.js"></script>
543
716
  </head>
544
- <body class='<%=theme%>'>
545
- <header class='navheader grabbable'>
546
- <h1>
547
- <a class='home' href="/"><i class="fa-solid fa-house"></i></a>
548
- <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
549
- <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
550
- <a class='btn2 create-new' id='create-new-folder'><div><i class='fa-solid fa-plus'></i></div><div>New</div></a>
551
- <a class='btn2' id='explore' href="/?mode=explore"><div><i class="fa-solid fa-magnifying-glass"></i></div><div>Discover</div></a>
552
- <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
553
- <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
554
- <div class='flexible'></div>
555
- <div class='nav-btns'>
556
- <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
557
- <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
558
- <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
559
- <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>
560
- <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
561
- <% if (agent === "electron") { %>
562
- <a href="/" target="_blank" class='btn2' features="browser"><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
563
- <% } else { %>
564
- <a href="/" target="_blank" class='btn2'><div><i class="fa-solid fa-square-up-right"></i></div><div>Open</div></a>
565
- <% } %>
566
- <button id='new-window' title='open a new window' class='btn2'><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
567
- </div>
568
- </h1>
569
- </header>
717
+ <body class='<%=theme%>' data-platform="<%=platform%>">
570
718
  <div class='appcanvas'>
571
- <aside class='active'>
572
- <div class='header-top header-item'>
573
- <div class='app-info'>
574
- <div class='app-info-card'>
575
- <% if (config.icon) { %>
576
- <img src="<%=config.icon%>"/>
577
- <% } else { %>
578
- <i class="fa-regular fa-circle-dot"></i>
579
- <% } %>
580
- <div><%=config.title%></div>
581
- <div class="folderpath"><%=config.web_path%></div>
582
- </div>
583
- </div>
584
- </div>
585
- </aside>
586
- <main>
587
- <div class='container'>
588
- <div class='prototype-header'>
589
- <div>
590
- <h1><%=title%></h1>
591
- <div class='desc'><%=description%></div>
592
- </div>
593
- <div class='flexible'></div>
594
- <a class='btn' id='download'><i class='fa-solid fa-download'></i> Download a custom prototype script</a>
595
- </div>
596
- <div class='folder-templates drawer'>
597
- <% items.forEach((folder, i) => { %>
598
- <a class='line folder' href="/prototype/show/<%=folder.id%>?path=<%=path%>">
599
- <% if (folder.icon) { %>
600
- <img src="<%=folder.icon%>">
601
- <% } else { %>
602
- <% if (theme === 'dark') { %>
603
- <img src="/pinokio-white.png">
604
- <% } else { %>
605
- <img src="/pinokio-black.png">
606
- <% } %>
607
- <% } %>
608
- <div class='col flexible'>
609
- <div class='title'><%=folder.title%></div>
610
- <div class='description'><%=folder.description%></div>
611
- </div>
612
- </a>
613
- <% }) %>
614
- </div>
615
- </div>
616
- </main>
719
+ <aside class='active'>
720
+ <div class='m h menu' data-type='h'>
721
+ <% if (config.menu) { %>
722
+ <%- include('./partials/menu', { menu: config.menu, }) %>
723
+ <% } %>
724
+ </div>
725
+ <div class='flexible'></div>
726
+ </aside>
727
+ <div class='container'>
728
+ <main class='browserview'></main>
617
729
  </div>
618
730
  <script>
619
- let paths = location.pathname.split("/").filter(x=>x)
620
- var term
621
- const n = new N()
622
- let socket = new Socket()
623
- let list = []
624
- document.querySelectorAll(".line").forEach((el, index) => {
625
- list.push({
626
- index: parseInt(el.getAttribute("data-index")),
627
- name: el.getAttribute("data-name"),
628
- description: el.getAttribute("data-description"),
731
+ let started_script
732
+ let loaded = {}
733
+ let cursorIndex = 0;
734
+ let interacted = false
735
+ document.addEventListener("click", (e) => {
736
+ interacted = true
629
737
  })
630
- })
631
- const search = (items, value) => {
632
- let filtered = []
633
- for(let i=0; i<items.length; i++) {
634
- let item = items[i]
635
- let re = new RegExp(value, "i")
636
- if (re.test(item.name)) {
637
- filtered.push({ item })
638
- } else if (re.test(item.description)) {
639
- filtered.push({ item })
738
+ const n = new N()
739
+ const getTarget = (href) => {
740
+ let u
741
+ if (href.startsWith("http")) {
742
+ return href
743
+ } else if (href.startsWith("/")) {
744
+ u = new URL("http://localhost" + href)
745
+ u.search = ""
746
+ return u.pathname
747
+ } else {
748
+ u = new URL("http://localhost/" + href)
749
+ u.search = ""
750
+ return u.pathname
640
751
  }
641
752
  }
642
- return filtered
643
- }
644
- </script>
753
+ const subUrlOf = (s, f) => {
754
+
755
+ let subsetUrl = (s.startsWith("http") ? s : "http://localhost" + s)
756
+ let fullUrl = (f.startsWith("http") ? f: "http://localhost" + f)
757
+
758
+ const subsetParsed = new URL(subsetUrl)
759
+ const fullParsed = new URL(fullUrl)
760
+
761
+ if (subsetParsed.pathname !== fullParsed.pathname) {
762
+ return false;
763
+ }
764
+
765
+ // Check if all search parameters in the subset URL are present in the full URL
766
+ for (const [key, val] of subsetParsed.searchParams) {
767
+
768
+ if (!fullParsed.searchParams.has(key)) {
769
+ return false
770
+ }
771
+ if (fullParsed.searchParams.get(key) !== val) {
772
+ return false
773
+ }
774
+ }
775
+
776
+ return true;
777
+ }
778
+ const renderCursor = () => {
779
+ document.querySelectorAll(".selectable").forEach((el) => {
780
+ el.classList.remove("cursor")
781
+ })
782
+ const target = document.querySelector(".selectable[data-index='" + cursorIndex + "']")
783
+ target.classList.add("cursor")
784
+ }
785
+ const nav = (type) => {
786
+ let targetFrame = document.querySelector("iframe:not(.hidden)")
787
+ if (type === "back") {
788
+ // targetFrame.contentWindow.history.back()
789
+ targetFrame.contentWindow.postMessage({
790
+ action: "back"
791
+ }, "*")
792
+ } else if (type === "forward") {
793
+ // targetFrame.contentWindow.history.forward()
794
+ targetFrame.contentWindow.postMessage({
795
+ action: "forward"
796
+ }, "*")
797
+ }
798
+
799
+
800
+
801
+ // if (location.hash && location.hash.length > 0) {
802
+ //// let id = getTarget(location.hash.slice(1))
803
+ //// let target = document.querySelector("aside [target='" + id + "']")
804
+ // let id = location.hash.slice(1)
805
+ // let type = id[0]
806
+ // let index = id.slice(1)
807
+ // let target = document.querySelector(`aside .${type} [data-index='${index}']`)
808
+ // let targetFrame = document.querySelector(`iframe[name='${target.target}']`)
809
+ // console.log({ target, targetFrame })
810
+ // if (type === "back") {
811
+ // targetFrame.contentWindow.history.back()
812
+ // } else if (type === "forward") {
813
+ // targetFrame.contentWindow.history.forward()
814
+ // }
815
+ // /*
816
+ // targetFrame.contentWindow.postMessage({
817
+ // action: type
818
+ // }, "*")
819
+ // */
820
+ // }
821
+ }
822
+ const renderSelection = (e, force) => {
823
+ let init = document.querySelector("[data-init]")
824
+ if (init) {
825
+ location.href = init.href
826
+ return
827
+ }
828
+ if (location.hash && location.hash.length > 0) {
829
+ let target
830
+ if (location.hash && location.hash.length > 0) {
831
+ let id = location.hash.slice(1)
832
+ let selector
833
+ if (id.startsWith("@")) {
834
+ selector = `aside [target='${id}']`
835
+ } else {
836
+ let type = id[0]
837
+ let index = id.slice(1)
838
+ console.log({ type, index })
839
+ selector = `aside .${type} [data-index='${index}']`
840
+ }
841
+ if (e) {
842
+ // If the renderSelection is triggered by an event, look for the frame-link that's triggering the event
843
+ if (e.target.classList.contains("frame-link")) {
844
+ target = e.target
845
+ } else {
846
+ target = e.target.closest(".frame-link")
847
+ }
848
+ } else {
849
+ // if not triggered by an event, look for the target from the location hash
850
+ target = document.querySelector(selector)
851
+ }
852
+ }
853
+
854
+
855
+ // if the target was found either through:
856
+ // 1. looking for the frame-link that triggered the event
857
+ // 2. or by looking at the location hash
858
+ //
859
+ // => select that tab and frame
860
+
861
+ if (target) {
862
+
863
+ // display the menu button at the top
864
+ // maximize container
865
+ // minimize aside
866
+
867
+ document.querySelector(".container").classList.add("active")
868
+ document.querySelector("aside").classList.remove("active")
869
+ } else {
870
+ document.querySelector(".container").classList.remove("active")
871
+ document.querySelector("aside").classList.add("active")
872
+ }
873
+
874
+ if (target) {
875
+
876
+
877
+ // Instantiate a frame with the selected target's href
878
+ let url = target.href
879
+
880
+ if (!url) {
881
+ return
882
+ }
883
+ // document.querySelector("#open-browser").href = url
884
+ // document.querySelector("#clone-tab").setAttribute("data-href", url)
885
+
886
+ //document.querySelector("#location").value = url
887
+
888
+ if (target.pathname.startsWith("/run")) {
889
+ document.querySelector("#location").value = target.pathname.slice(4)
890
+ } else {
891
+ document.querySelector("#location").value = target.pathname
892
+ }
893
+
894
+
895
+ // select the selected target element (tab)
896
+ document.querySelectorAll(".frame-link").forEach((el) => {
897
+ el.classList.remove("selected")
898
+ })
899
+ target.classList.add("selected")
900
+
901
+ // hide all frames
902
+ document.querySelectorAll("iframe").forEach((el) => {
903
+ el.classList.add("hidden")
904
+ })
905
+
906
+
907
+
908
+ // Look for the frame
909
+ // 1. if it's hard (".h")
910
+ // look for the url that matches
911
+ // 2. if it's soft (".s")
912
+ // find using the name attribute
913
+ let targetFrame;
914
+ if (target.closest(".h")) {
915
+ let hardFrames = document.querySelectorAll(".browserview iframe[name^='@']")
916
+ let port = new URL(target.href).port || 80
917
+ for(let frame of hardFrames) {
918
+ console.log({ src: frame.src, href: target.href, target: target.target })
919
+ // Types of links in the tab
920
+ let targetPath = new URL(target.href).pathname
921
+ let framePath = new URL(frame.src).pathname
922
+ if (targetPath === framePath) {
923
+ let beforeName = frame.name
924
+ frame.name = target.target
925
+ console.log("SET FRAME", { before: beforeName, after: frame.name, target: target.target })
926
+ targetFrame = frame
927
+ break
928
+ }
929
+ // find the frame whose path matches
930
+ }
931
+ } else if (target.closest(".s")) {
932
+ targetFrame = document.querySelector(`iframe[name='${target.target}']`)
933
+ } else if (target.closest(".n")) {
934
+ targetFrame = document.querySelector(`iframe[name='${target.target}']`)
935
+ }
936
+
937
+ /*
938
+ // Load the link in the frame
939
+
940
+ 1. if the frame was found:
941
+ - if "refresh": true => refresh
942
+ - otherwise,
943
+ - if the target frame has been already loaded, don't do anything
944
+ - frame: start.json?mode=abc, link: start.json?mode=abc => don't touch
945
+ - link === frame
946
+ - frame: start.json?mode=abc, link: start.json?mode=def => should load
947
+ - link !== frame && !frame.includes(link)
948
+ - frame: start.json?mode=abc, link: start.json => don't touch
949
+ - link !== frame && frame.includes(link)
950
+
951
+
952
+
953
+ if the link is different
954
+
955
+ - if the target frame has not been loaded, refresh
956
+ 2. if the frame was NOT found => create one
957
+
958
+ */
959
+
960
+ if (targetFrame) {
961
+ targetFrame.classList.remove("hidden")
962
+ let mode = target.getAttribute("data-mode")
963
+ if (mode === "refresh") {
964
+ targetFrame.src = target.href
965
+ } else {
966
+ if (e) {
967
+ if (target.closest(".h")) {
968
+ e.preventDefault()
969
+ e.stopPropagation()
970
+ let sub = subUrlOf(target.href, targetFrame.src)
971
+ if (!sub) {
972
+ targetFrame.src = target.href
973
+ }
974
+ //if (target.href === targetFrame.src) {
975
+ // // same url. don't do anything
976
+ // e.preventDefault()
977
+ // e.stopPropagation()
978
+ //} else {
979
+ // if (targetFrame.src.includes(target.href)) {
980
+ // // different url, but the frame's url contains the tab url => don't touch
981
+ // e.preventDefault()
982
+ // e.stopPropagation()
983
+ // } else {
984
+ // // different url, and the frame's url does not contain the tab url => change the src of the frame and load
985
+ // targetFrame.src = target.href
986
+ // e.preventDefault()
987
+ // e.stopPropagation()
988
+ // }
989
+ //}
990
+ } else if (target.closest(".s")) {
991
+ // load unconditionally => .s doesn't change
992
+ e.preventDefault()
993
+ e.stopPropagation()
994
+ } else if (target.closest(".n")) {
995
+ // load unconditionally => .n doesn't change
996
+ e.preventDefault()
997
+ e.stopPropagation()
998
+ }
999
+ } else {
1000
+ if (force) {
1001
+ targetFrame.src = target.href
1002
+ }
1003
+ }
1004
+ targetFrame.contentWindow.postMessage({
1005
+ action: "foreground"
1006
+ }, "*")
1007
+ }
1008
+ } else {
1009
+ let frame = document.createElement("iframe")
1010
+ frame.name = target.target
1011
+ frame.src = target.href
1012
+ frame.setAttribute(
1013
+ "allow",
1014
+ "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
1015
+ )
1016
+ document.querySelector("main").appendChild(frame)
1017
+ loaded[target.target] = true
1018
+ }
1019
+
1020
+ // if (targetFrame) {
1021
+ // targetFrame.classList.remove("hidden")
1022
+ //
1023
+ // // if the iframe already exists, do not
1024
+ // if (loaded[target.target]){
1025
+ //
1026
+ // // Force refresh option
1027
+ // //
1028
+ // // menu := [{
1029
+ // // text,
1030
+ // // icon,
1031
+ // // mode: "refresh" => refresh regardless of any condition
1032
+ // // }]
1033
+ // let mode = target.getAttribute("data-mode")
1034
+ // if (mode === "refresh") {
1035
+ // targetFrame.src = target.href
1036
+ // } else {
1037
+ // console.log("# 4")
1038
+ // if (e) {
1039
+ // // if triggered by a click event,
1040
+ // // 1. if it's a hard link (".h")
1041
+ // // - ignore if the pathname is the same
1042
+ // // - reload if
1043
+ // console.log("# 5", { src: targetFrame.src, href: target.href, target: target.target })
1044
+ // // triggered with a click
1045
+ // // do not reload iframe if the href and the src is the same
1046
+ // if (target.closest(".h")) {
1047
+ // // if (targetFrame.src.includes(target.target)) {
1048
+ // if (targetFrame.src.includes(target.href)) {
1049
+ // // if (targetFrame.src === target.href) {
1050
+ // e.preventDefault()
1051
+ // e.stopPropagation()
1052
+ // }
1053
+ // } else if (target.closest(".s")) {
1054
+ // e.preventDefault()
1055
+ // e.stopPropagation()
1056
+ // }
1057
+ // } else {
1058
+ // // if not triggered by event
1059
+ // // refresh ONLY if programmatically forced refresh
1060
+ // console.log("# 6")
1061
+ // if (force) {
1062
+ // console.log("# 7")
1063
+ // targetFrame.src = target.href
1064
+ // }
1065
+ // }
1066
+ // }
1067
+ //
1068
+ // } else {
1069
+ // console.log("# 8")
1070
+ // //targetFrame.contentWindow.addEventListener("load", () => {
1071
+ // if (e) {
1072
+ // console.log("# 9")
1073
+ // // coming from a click => don't need to set the src
1074
+ // } else {
1075
+ // console.log("# 10")
1076
+ // // load without click
1077
+ // if (targetFrame.src !== target.href) {
1078
+ // console.log("# 11")
1079
+ // targetFrame.src = target.href
1080
+ // }
1081
+ // }
1082
+ // loaded[target.target] = true
1083
+ // //});
1084
+ // }
1085
+ // } else {
1086
+ // console.log("# 12")
1087
+ // let frame = document.createElement("iframe")
1088
+ // frame.name = target.target
1089
+ // frame.src = target.href
1090
+ // frame.setAttribute(
1091
+ // "allow",
1092
+ // "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
1093
+ // )
1094
+ // document.querySelector("main").appendChild(frame)
1095
+ // loaded[target.target] = true
1096
+ // }
1097
+ } else {
1098
+ console.log("# 13")
1099
+ // couldn't find item,
1100
+ // clear the hash
1101
+ //
1102
+ location.hash = ""
1103
+ }
1104
+ } else {
1105
+ console.log("# 14")
1106
+ document.querySelector(".container").classList.remove("active")
1107
+ document.querySelector("aside").classList.add("active")
1108
+
1109
+ }
1110
+ }
1111
+ renderSelection()
1112
+
1113
+
1114
+ if (document.querySelector("#menu")) {
1115
+ document.querySelector("#menu").addEventListener("click", async (e) => {
1116
+ document.querySelector("aside").classList.toggle("hidden")
1117
+ document.querySelector("#menu").classList.toggle("margin")
1118
+ })
1119
+ }
1120
+ document.querySelector("aside").addEventListener("click", async (e) => {
1121
+ let target
1122
+
1123
+ // 0. handle dropdowns
1124
+ if (e.target.classList.contains("revealer")) {
1125
+ target = e.target
1126
+ } else {
1127
+ target = e.target.closest(".revealer")
1128
+ }
1129
+
1130
+ if (target) {
1131
+ e.preventDefault()
1132
+ e.stopPropagation()
1133
+ let group = target.getAttribute("data-group")
1134
+ document.querySelector(group).classList.toggle("hidden")
1135
+ target.querySelector(".loader .fa-angle-down").classList.toggle("hidden")
1136
+ target.querySelector(".loader .fa-angle-up").classList.toggle("hidden")
1137
+ return
1138
+ }
1139
+
1140
+
1141
+
1142
+ // 1. handle delete
1143
+ if (e.target.classList.contains("del")) {
1144
+ target = e.target
1145
+ } else {
1146
+ target = e.target.closest(".del")
1147
+ }
1148
+
1149
+ if (target) {
1150
+ e.preventDefault()
1151
+ e.stopPropagation()
1152
+ // Delete
1153
+ // delete link
1154
+ let el = target.closest(".frame-link")
1155
+ console.log("target", el.target)
1156
+ let iframe = document.querySelector("iframe[name='" + el.target + "']")
1157
+ console.log("iframe", iframe)
1158
+ if (iframe) {
1159
+ // delete iframe
1160
+ iframe.remove()
1161
+ }
1162
+ el.remove()
1163
+ // update tabs
1164
+ await syncTabs()
1165
+ return
1166
+ }
1167
+
1168
+
1169
+ // 2. handle shudown
1170
+ if (e.target.classList.contains("shutdown")) {
1171
+ target = e.target
1172
+ } else {
1173
+ target = e.target.closest(".shutdown")
1174
+ }
1175
+ if (target) {
1176
+ e.preventDefault()
1177
+ e.stopPropagation()
1178
+ let link = target.closest("[href]")
1179
+ let src = new URL(link.href).pathname
1180
+ target.querySelector("i.fa-square").className = "fa-solid fa-check"
1181
+ n.Noty({
1182
+ text: `stopping ${src}`,
1183
+ silent: true,
1184
+ timeout: 2000
1185
+ })
1186
+ console.log("src", src)
1187
+
1188
+ let socket = new Socket()
1189
+ socket.run({
1190
+ method: "kernel.api.stop",
1191
+ params: {
1192
+ uri: "~" + src
1193
+ }
1194
+ }, (packet) => {
1195
+ if (packet.type === "result") {
1196
+ socket.close()
1197
+ n.Noty({
1198
+ timeout: 2000,
1199
+ text: `stopped`,
1200
+ silent: true
1201
+ })
1202
+ console.log("Refresh 4")
1203
+ }
1204
+ })
1205
+
1206
+ return
1207
+ }
1208
+
1209
+
1210
+
1211
+ // 3. handle select
1212
+ if (e.target.classList.contains("frame-link")) {
1213
+ target = e.target
1214
+ } else {
1215
+ target = e.target.closest(".frame-link")
1216
+ }
1217
+
1218
+
1219
+
1220
+ if (target) {
1221
+
1222
+ // if data-confirm exists, ask for confirmation
1223
+ // if not, then just go forward
1224
+ let confirmText = target.getAttribute("data-confirm")
1225
+ if (confirmText && confirmText.length > 0) {
1226
+ const confirmed = confirm(confirmText)
1227
+ if (!confirmed) {
1228
+ e.preventDefault()
1229
+ e.stopPropagation()
1230
+ return
1231
+ }
1232
+ }
1233
+
1234
+ if (target.getAttribute("data-action")) {
1235
+ let actionStr = target.getAttribute("data-action")
1236
+ let action = JSON.parse(actionStr)
1237
+ console.log("action", action)
1238
+ if (action.method === "stop" && action.uri) {
1239
+ let socket = new Socket()
1240
+ socket.run({
1241
+ method: "kernel.api.stop",
1242
+ params: {
1243
+ uri: action.uri//"~" + location.pathname
1244
+ }
1245
+ }, (packet) => {
1246
+ if (packet.type === "result") {
1247
+ socket.close()
1248
+ // location.href = location.href
1249
+ }
1250
+ })
1251
+ }
1252
+ } else if (target.getAttribute("data-filepath")) {
1253
+ return
1254
+ } else if (target.target === "_blank") {
1255
+ // nothing
1256
+ } else {
1257
+ if (target.classList.contains("reveal")) {
1258
+ // nested menu
1259
+ target.closest(".nested-menu").querySelector(".submenu").classList.toggle("hidden")
1260
+ target.querySelector(".loader .fa-angle-down").classList.toggle("hidden")
1261
+ target.querySelector(".loader .fa-angle-up").classList.toggle("hidden")
1262
+ e.preventDefault()
1263
+ e.stopPropagation()
1264
+ } else {
1265
+ //location.hash = target.target
1266
+
1267
+
1268
+ // let id = getTarget(target.getAttribute("href"))
1269
+ // console.log("ID", id, target.href)
1270
+ // location.hash = id
1271
+
1272
+ //location.hash = target.getAttribute("href")
1273
+
1274
+ if (target.closest(".h")) {
1275
+ if (target.target) {
1276
+ location.hash = target.target
1277
+ }
1278
+ } else {
1279
+ let index = target.getAttribute("data-index")
1280
+ let type = target.closest(".m").getAttribute("data-type")
1281
+
1282
+
1283
+ if (type) {
1284
+ location.hash = type + index
1285
+ }
1286
+ }
1287
+
1288
+ document.querySelectorAll(".frame-link").forEach((el) => {
1289
+ el.classList.remove("selected")
1290
+ })
1291
+ target.classList.add("selected")
1292
+
1293
+ renderSelection(e)
1294
+ }
1295
+ }
1296
+ }
1297
+
1298
+
1299
+
1300
+ })
1301
+ </script>
645
1302
  </body>
646
1303
  </html>