pinokiod 1.3.4 → 1.3.6

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